use pinia to replace vuex, code refactor
This commit is contained in:
Generated
+52
-13
@@ -23,13 +23,13 @@
|
||||
"line-awesome": "^1.3.0",
|
||||
"moment": "^2.29.4",
|
||||
"moment-timezone": "^0.5.43",
|
||||
"pinia": "^2.1.3",
|
||||
"register-service-worker": "^1.7.2",
|
||||
"skeleton-elements": "^4.0.1",
|
||||
"swiper": "^9.3.2",
|
||||
"ua-parser-js": "^1.0.35",
|
||||
"vue": "^3.3.4",
|
||||
"vue-i18n": "^9.2.2",
|
||||
"vuex": "^4.1.0"
|
||||
"vue-i18n": "^9.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^4.2.0",
|
||||
@@ -5634,6 +5634,56 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/pinia": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/pinia/-/pinia-2.1.3.tgz",
|
||||
"integrity": "sha512-XNA/z/ye4P5rU1pieVmh0g/hSuDO98/a5UC8oSP0DNdvt6YtetJNHTrXwpwsQuflkGT34qKxAEcp7lSxXNjf/A==",
|
||||
"dependencies": {
|
||||
"@vue/devtools-api": "^6.5.0",
|
||||
"vue-demi": ">=0.14.5"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/posva"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vue/composition-api": "^1.4.0",
|
||||
"typescript": ">=4.4.4",
|
||||
"vue": "^2.6.14 || ^3.3.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vue/composition-api": {
|
||||
"optional": true
|
||||
},
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/pinia/node_modules/vue-demi": {
|
||||
"version": "0.14.5",
|
||||
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.5.tgz",
|
||||
"integrity": "sha512-o9NUVpl/YlsGJ7t+xuqJKx8EBGf1quRhCiT6D/J0pfwmk9zUwYkC7yrF4SZCe6fETvSM3UNL2edcbYrSyc4QHA==",
|
||||
"hasInstallScript": true,
|
||||
"bin": {
|
||||
"vue-demi-fix": "bin/vue-demi-fix.js",
|
||||
"vue-demi-switch": "bin/vue-demi-switch.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vue/composition-api": "^1.0.0-rc.1",
|
||||
"vue": "^3.0.0-0 || ^2.6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vue/composition-api": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.23",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz",
|
||||
@@ -7238,17 +7288,6 @@
|
||||
"vue": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vuex": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vuex/-/vuex-4.1.0.tgz",
|
||||
"integrity": "sha512-hmV6UerDrPcgbSy9ORAtNXDr9M4wlNP4pEFKye4ujJF8oqgFFuxDCdOLS3eNoRTtq5O3hoBDh9Doj1bQMYHRbQ==",
|
||||
"dependencies": {
|
||||
"@vue/devtools-api": "^6.0.0-beta.11"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
|
||||
|
||||
+2
-2
@@ -32,13 +32,13 @@
|
||||
"line-awesome": "^1.3.0",
|
||||
"moment": "^2.29.4",
|
||||
"moment-timezone": "^0.5.43",
|
||||
"pinia": "^2.1.3",
|
||||
"register-service-worker": "^1.7.2",
|
||||
"skeleton-elements": "^4.0.1",
|
||||
"swiper": "^9.3.2",
|
||||
"ua-parser-js": "^1.0.35",
|
||||
"vue": "^3.3.4",
|
||||
"vue-i18n": "^9.2.2",
|
||||
"vuex": "^4.1.0"
|
||||
"vue-i18n": "^9.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^4.2.0",
|
||||
|
||||
+12
-3
@@ -9,6 +9,12 @@
|
||||
import { f7ready } from 'framework7-vue';
|
||||
import routes from './router/mobile.js';
|
||||
|
||||
import { mapStores } from 'pinia';
|
||||
import { useTokensStore } from '@/stores/token.js';
|
||||
import { useExchangeRatesStore } from '@/stores/exchangeRates.js';
|
||||
|
||||
import { isModalShowing } from '@/lib/ui.mobile.js';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
const self = this;
|
||||
@@ -81,13 +87,16 @@ export default {
|
||||
hasBackdrop: undefined
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useTokensStore, useExchangeRatesStore),
|
||||
},
|
||||
created() {
|
||||
const self = this;
|
||||
|
||||
if (self.$user.isUserLogined()) {
|
||||
if (!self.$settings.isEnableApplicationLock()) {
|
||||
// refresh token if user is logined
|
||||
self.$store.dispatch('refreshTokenAndRevokeOldToken').then(response => {
|
||||
self.tokensStore.refreshTokenAndRevokeOldToken().then(response => {
|
||||
if (response.user && response.user.language) {
|
||||
self.$locale.setLanguage(response.user.language);
|
||||
}
|
||||
@@ -95,7 +104,7 @@ export default {
|
||||
|
||||
// auto refresh exchange rates data
|
||||
if (self.$settings.isAutoUpdateExchangeRatesData()) {
|
||||
self.$store.dispatch('getLatestExchangeRates', { silent: true, force: false });
|
||||
self.exchangeRatesStore.getLatestExchangeRates({ silent: true, force: false });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,7 +126,7 @@ export default {
|
||||
f7.on('sheetClose', (event) => this.onBackdropChanged(event));
|
||||
|
||||
f7.on('pageBeforeOut', () => {
|
||||
if (this.$ui.isModalShowing()) {
|
||||
if (isModalShowing()) {
|
||||
f7.actions.close('.actions-modal.modal-in', false);
|
||||
f7.dialog.close('.dialog.modal-in', false);
|
||||
f7.popover.close('.popover.modal-in', false);
|
||||
|
||||
@@ -48,6 +48,25 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useUserStore } from '@/stores/user.js';
|
||||
|
||||
import datetimeConstants from '@/consts/datetime.js';
|
||||
import { arrangeArrayWithNewStartIndex } from '@/lib/common.js';
|
||||
import {
|
||||
getCurrentUnixTime,
|
||||
getCurrentDateTime,
|
||||
getUnixTime,
|
||||
getLocalDatetimeFromUnixTime,
|
||||
getTodayFirstUnixTime,
|
||||
getYear,
|
||||
getDummyUnixTimeForLocalUsage,
|
||||
getActualUnixTimeForStore,
|
||||
getTimezoneOffsetMinutes,
|
||||
getBrowserTimezoneOffsetMinutes,
|
||||
getDateRangeByDateType
|
||||
} from '@/lib/datetime.js';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'minTime',
|
||||
@@ -62,8 +81,8 @@ export default {
|
||||
],
|
||||
data() {
|
||||
const self = this;
|
||||
let minDate = self.$utilities.getTodayFirstUnixTime();
|
||||
let maxDate = self.$utilities.getCurrentUnixTime();
|
||||
let minDate = getTodayFirstUnixTime();
|
||||
let maxDate = getCurrentUnixTime();
|
||||
|
||||
if (self.minTime) {
|
||||
minDate = self.minTime;
|
||||
@@ -76,53 +95,54 @@ export default {
|
||||
return {
|
||||
yearRange: [
|
||||
2000,
|
||||
this.$utilities.getYear(this.$utilities.getCurrentDateTime()) + 1
|
||||
getYear(getCurrentDateTime()) + 1
|
||||
],
|
||||
dateRange: [
|
||||
this.$utilities.getLocalDatetimeFromUnixTime(this.$utilities.getDummyUnixTimeForLocalUsage(minDate, this.$utilities.getTimezoneOffsetMinutes(), this.$utilities.getBrowserTimezoneOffsetMinutes())),
|
||||
this.$utilities.getLocalDatetimeFromUnixTime(this.$utilities.getDummyUnixTimeForLocalUsage(maxDate, this.$utilities.getTimezoneOffsetMinutes(), this.$utilities.getBrowserTimezoneOffsetMinutes()))
|
||||
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(minDate, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes())),
|
||||
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(maxDate, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()))
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useUserStore),
|
||||
isDarkMode() {
|
||||
return this.$root.isDarkMode;
|
||||
},
|
||||
firstDayOfWeek() {
|
||||
return this.$store.getters.currentUserFirstDayOfWeek;
|
||||
return this.userStore.currentUserFirstDayOfWeek;
|
||||
},
|
||||
dayNames() {
|
||||
return this.$utilities.arrangeArrayWithNewStartIndex(this.$locale.getAllMinWeekdayNames(), this.firstDayOfWeek);
|
||||
return arrangeArrayWithNewStartIndex(this.$locale.getAllMinWeekdayNames(), this.firstDayOfWeek);
|
||||
},
|
||||
is24Hour() {
|
||||
return this.$locale.isLongTime24HourFormat();
|
||||
return this.$locale.isLongTime24HourFormat(this.userStore);
|
||||
},
|
||||
beginDateTime() {
|
||||
const actualBeginUnixTime = this.$utilities.getActualUnixTimeForStore(this.$utilities.getUnixTime(this.dateRange[0]), this.$utilities.getTimezoneOffsetMinutes(), this.$utilities.getBrowserTimezoneOffsetMinutes());
|
||||
return this.$utilities.formatUnixTime(actualBeginUnixTime, this.$locale.getLongDateTimeFormat());
|
||||
const actualBeginUnixTime = getActualUnixTimeForStore(getUnixTime(this.dateRange[0]), getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
|
||||
return this.$locale.formatUnixTimeToLongDateTime(this.userStore, actualBeginUnixTime);
|
||||
},
|
||||
endDateTime() {
|
||||
const actualEndUnixTime = this.$utilities.getActualUnixTimeForStore(this.$utilities.getUnixTime(this.dateRange[1]), this.$utilities.getTimezoneOffsetMinutes(), this.$utilities.getBrowserTimezoneOffsetMinutes());
|
||||
return this.$utilities.formatUnixTime(actualEndUnixTime, this.$locale.getLongDateTimeFormat());
|
||||
const actualEndUnixTime = getActualUnixTimeForStore(getUnixTime(this.dateRange[1]), getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
|
||||
return this.$locale.formatUnixTimeToLongDateTime(this.userStore, actualEndUnixTime);
|
||||
},
|
||||
presetRanges() {
|
||||
const presetRanges = [];
|
||||
|
||||
[
|
||||
this.$constants.datetime.allDateRanges.Today,
|
||||
this.$constants.datetime.allDateRanges.LastSevenDays,
|
||||
this.$constants.datetime.allDateRanges.LastThirtyDays,
|
||||
this.$constants.datetime.allDateRanges.ThisWeek,
|
||||
this.$constants.datetime.allDateRanges.ThisMonth,
|
||||
this.$constants.datetime.allDateRanges.ThisYear
|
||||
datetimeConstants.allDateRanges.Today,
|
||||
datetimeConstants.allDateRanges.LastSevenDays,
|
||||
datetimeConstants.allDateRanges.LastThirtyDays,
|
||||
datetimeConstants.allDateRanges.ThisWeek,
|
||||
datetimeConstants.allDateRanges.ThisMonth,
|
||||
datetimeConstants.allDateRanges.ThisYear
|
||||
].forEach(dateRangeType => {
|
||||
const dateRange = this.$utilities.getDateRangeByDateType(dateRangeType.type, this.firstDayOfWeek);
|
||||
const dateRange = getDateRangeByDateType(dateRangeType.type, this.firstDayOfWeek);
|
||||
|
||||
presetRanges.push({
|
||||
label: this.$t(dateRangeType.name),
|
||||
range: [
|
||||
this.$utilities.getLocalDatetimeFromUnixTime(this.$utilities.getDummyUnixTimeForLocalUsage(dateRange.minTime, this.$utilities.getTimezoneOffsetMinutes(), this.$utilities.getBrowserTimezoneOffsetMinutes())),
|
||||
this.$utilities.getLocalDatetimeFromUnixTime(this.$utilities.getDummyUnixTimeForLocalUsage(dateRange.maxTime, this.$utilities.getTimezoneOffsetMinutes(), this.$utilities.getBrowserTimezoneOffsetMinutes()))
|
||||
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(dateRange.minTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes())),
|
||||
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(dateRange.maxTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()))
|
||||
]
|
||||
});
|
||||
});
|
||||
@@ -133,11 +153,11 @@ export default {
|
||||
methods: {
|
||||
onSheetOpen() {
|
||||
if (this.minTime) {
|
||||
this.dateRange[0] = this.$utilities.getLocalDatetimeFromUnixTime(this.$utilities.getDummyUnixTimeForLocalUsage(this.minTime, this.$utilities.getTimezoneOffsetMinutes(), this.$utilities.getBrowserTimezoneOffsetMinutes()));
|
||||
this.dateRange[0] = getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(this.minTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()));
|
||||
}
|
||||
|
||||
if (this.maxTime) {
|
||||
this.dateRange[1] = this.$utilities.getLocalDatetimeFromUnixTime(this.$utilities.getDummyUnixTimeForLocalUsage(this.maxTime, this.$utilities.getTimezoneOffsetMinutes(), this.$utilities.getBrowserTimezoneOffsetMinutes()));
|
||||
this.dateRange[1] = getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(this.maxTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()));
|
||||
}
|
||||
},
|
||||
onSheetClosed() {
|
||||
@@ -151,16 +171,16 @@ export default {
|
||||
const currentMinDate = this.dateRange[0];
|
||||
const currentMaxDate = this.dateRange[1];
|
||||
|
||||
let minUnixTime = this.$utilities.getUnixTime(currentMinDate);
|
||||
let maxUnixTime = this.$utilities.getUnixTime(currentMaxDate);
|
||||
let minUnixTime = getUnixTime(currentMinDate);
|
||||
let maxUnixTime = getUnixTime(currentMaxDate);
|
||||
|
||||
if (minUnixTime < 0 || maxUnixTime < 0) {
|
||||
this.$toast('Date is too early');
|
||||
return;
|
||||
}
|
||||
|
||||
minUnixTime = this.$utilities.getActualUnixTimeForStore(minUnixTime, this.$utilities.getTimezoneOffsetMinutes(), this.$utilities.getBrowserTimezoneOffsetMinutes());
|
||||
maxUnixTime = this.$utilities.getActualUnixTimeForStore(maxUnixTime, this.$utilities.getTimezoneOffsetMinutes(), this.$utilities.getBrowserTimezoneOffsetMinutes());
|
||||
minUnixTime = getActualUnixTimeForStore(minUnixTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
|
||||
maxUnixTime = getActualUnixTimeForStore(maxUnixTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
|
||||
|
||||
this.$emit('dateRange:change', minUnixTime, maxUnixTime);
|
||||
},
|
||||
|
||||
@@ -34,6 +34,18 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useUserStore } from '@/stores/user.js';
|
||||
|
||||
import { arrangeArrayWithNewStartIndex } from '@/lib/common.js';
|
||||
import {
|
||||
getCurrentUnixTime,
|
||||
getCurrentDateTime,
|
||||
getUnixTime,
|
||||
getLocalDatetimeFromUnixTime,
|
||||
getYear
|
||||
} from '@/lib/datetime.js';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'modelValue',
|
||||
@@ -45,7 +57,7 @@ export default {
|
||||
],
|
||||
data() {
|
||||
const self = this;
|
||||
let value = self.$utilities.getCurrentUnixTime();
|
||||
let value = getCurrentUnixTime();
|
||||
|
||||
if (self.modelValue) {
|
||||
value = self.modelValue;
|
||||
@@ -54,43 +66,44 @@ export default {
|
||||
return {
|
||||
yearRange: [
|
||||
2000,
|
||||
this.$utilities.getYear(this.$utilities.getCurrentDateTime()) + 1
|
||||
getYear(getCurrentDateTime()) + 1
|
||||
],
|
||||
dateTime: this.$utilities.getLocalDatetimeFromUnixTime(value),
|
||||
dateTime: getLocalDatetimeFromUnixTime(value),
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useUserStore),
|
||||
isDarkMode() {
|
||||
return this.$root.isDarkMode;
|
||||
},
|
||||
firstDayOfWeek() {
|
||||
return this.$store.getters.currentUserFirstDayOfWeek;
|
||||
return this.userStore.currentUserFirstDayOfWeek;
|
||||
},
|
||||
dayNames() {
|
||||
return this.$utilities.arrangeArrayWithNewStartIndex(this.$locale.getAllMinWeekdayNames(), this.firstDayOfWeek);
|
||||
return arrangeArrayWithNewStartIndex(this.$locale.getAllMinWeekdayNames(), this.firstDayOfWeek);
|
||||
},
|
||||
is24Hour() {
|
||||
return this.$locale.isLongTime24HourFormat();
|
||||
return this.$locale.isLongTime24HourFormat(this.userStore);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onSheetOpen() {
|
||||
if (this.modelValue) {
|
||||
this.dateTime = this.$utilities.getLocalDatetimeFromUnixTime(this.modelValue)
|
||||
this.dateTime = getLocalDatetimeFromUnixTime(this.modelValue)
|
||||
}
|
||||
},
|
||||
onSheetClosed() {
|
||||
this.$emit('update:show', false);
|
||||
},
|
||||
setCurrentTime() {
|
||||
this.dateTime = this.$utilities.getLocalDatetimeFromUnixTime(this.$utilities.getCurrentUnixTime())
|
||||
this.dateTime = getLocalDatetimeFromUnixTime(getCurrentUnixTime())
|
||||
},
|
||||
confirm() {
|
||||
if (!this.dateTime) {
|
||||
return;
|
||||
}
|
||||
|
||||
const unixTime = this.$utilities.getUnixTime(this.dateTime);
|
||||
const unixTime = getUnixTime(this.dateTime);
|
||||
|
||||
if (unixTime < 0) {
|
||||
this.$toast('Date is too early');
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { makeButtonCopyToClipboard, changeClipboardObjectText } from '@/lib/misc.js';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'title',
|
||||
@@ -52,7 +54,7 @@ export default {
|
||||
watch: {
|
||||
'information': function (newValue) {
|
||||
if (this.clipboardHolder) {
|
||||
this.$utilities.changeClipboardObjectText(this.clipboardHolder, newValue);
|
||||
changeClipboardObjectText(this.clipboardHolder, newValue);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -71,7 +73,7 @@ export default {
|
||||
}
|
||||
|
||||
if (self.$refs.copyToClipboardIcon) {
|
||||
self.clipboardHolder = self.$utilities.makeButtonCopyToClipboard({
|
||||
self.clipboardHolder = makeButtonCopyToClipboard({
|
||||
el: '#copy-to-clipboard-icon',
|
||||
text: self.information,
|
||||
successCallback: function () {
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import iconConstatns from '@/consts/icon.js';
|
||||
import colorConstatns from '@/consts/color.js';
|
||||
import { isNumber } from '@/lib/common.js';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'iconType',
|
||||
@@ -50,29 +54,29 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
getAccountIcon(iconId) {
|
||||
if (this.$utilities.isNumber(iconId)) {
|
||||
if (isNumber(iconId)) {
|
||||
iconId = iconId.toString();
|
||||
}
|
||||
|
||||
if (!this.$constants.icons.allAccountIcons[iconId]) {
|
||||
return this.$constants.icons.defaultAccountIcon.icon;
|
||||
if (!iconConstatns.allAccountIcons[iconId]) {
|
||||
return iconConstatns.defaultAccountIcon.icon;
|
||||
}
|
||||
|
||||
return this.$constants.icons.allAccountIcons[iconId].icon;
|
||||
return iconConstatns.allAccountIcons[iconId].icon;
|
||||
},
|
||||
getCategoryIcon(iconId) {
|
||||
if (this.$utilities.isNumber(iconId)) {
|
||||
if (isNumber(iconId)) {
|
||||
iconId = iconId.toString();
|
||||
}
|
||||
|
||||
if (!this.$constants.icons.allCategoryIcons[iconId]) {
|
||||
return this.$constants.icons.defaultCategoryIcon.icon;
|
||||
if (!iconConstatns.allCategoryIcons[iconId]) {
|
||||
return iconConstatns.defaultCategoryIcon.icon;
|
||||
}
|
||||
|
||||
return this.$constants.icons.allCategoryIcons[iconId].icon;
|
||||
return iconConstatns.allCategoryIcons[iconId].icon;
|
||||
},
|
||||
getAccountIconStyle(color, defaultColor, additionalColorAttr) {
|
||||
if (color && color !== this.$constants.colors.defaultAccountColor) {
|
||||
if (color && color !== colorConstatns.defaultAccountColor) {
|
||||
color = '#' + color;
|
||||
} else {
|
||||
color = defaultColor;
|
||||
@@ -89,7 +93,7 @@ export default {
|
||||
return ret;
|
||||
},
|
||||
getCategoryIconStyle(color, defaultColor, additionalColorAttr) {
|
||||
if (color && color !== this.$constants.colors.defaultCategoryColor) {
|
||||
if (color && color !== colorConstatns.defaultCategoryColor) {
|
||||
color = '#' + color;
|
||||
} else {
|
||||
color = defaultColor;
|
||||
@@ -106,7 +110,7 @@ export default {
|
||||
return ret;
|
||||
},
|
||||
getDefaultIconStyle(color, defaultColor, additionalColorAttr) {
|
||||
if (color && color !== this.$constants.colors.defaultColor) {
|
||||
if (color && color !== colorConstatns.defaultColor) {
|
||||
color = '#' + color;
|
||||
} else {
|
||||
color = defaultColor;
|
||||
|
||||
@@ -63,6 +63,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { isString, appendThousandsSeparator } from '@/lib/common.js';
|
||||
import { numericCurrencyToString, stringCurrencyToNumeric } from '@/lib/currency.js';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'modelValue',
|
||||
@@ -85,8 +88,8 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
currentDisplay() {
|
||||
const previousValue = this.$utilities.appendThousandsSeparator(this.previousValue);
|
||||
const currentValue = this.$utilities.appendThousandsSeparator(this.currentValue);
|
||||
const previousValue = appendThousandsSeparator(this.previousValue);
|
||||
const currentValue = appendThousandsSeparator(this.currentValue);
|
||||
|
||||
if (this.currentSymbol) {
|
||||
return `${previousValue} ${this.currentSymbol} ${currentValue}`;
|
||||
@@ -115,7 +118,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
getStringValue(value) {
|
||||
let str = this.$utilities.numericCurrencyToString(value);
|
||||
let str = numericCurrencyToString(value);
|
||||
|
||||
if (str.indexOf(',')) {
|
||||
str = str.replaceAll(/,/g, '');
|
||||
@@ -173,18 +176,18 @@ export default {
|
||||
|
||||
const newValue = this.currentValue + num.toString();
|
||||
|
||||
if (this.$utilities.isString(this.minValue) && this.minValue !== '') {
|
||||
const min = this.$utilities.stringCurrencyToNumeric(this.minValue);
|
||||
const current = this.$utilities.stringCurrencyToNumeric(newValue);
|
||||
if (isString(this.minValue) && this.minValue !== '') {
|
||||
const min = stringCurrencyToNumeric(this.minValue);
|
||||
const current = stringCurrencyToNumeric(newValue);
|
||||
|
||||
if (current < min) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.$utilities.isString(this.maxValue) && this.maxValue !== '') {
|
||||
const max = this.$utilities.stringCurrencyToNumeric(this.maxValue);
|
||||
const current = this.$utilities.stringCurrencyToNumeric(newValue);
|
||||
if (isString(this.maxValue) && this.maxValue !== '') {
|
||||
const max = stringCurrencyToNumeric(this.maxValue);
|
||||
const current = stringCurrencyToNumeric(newValue);
|
||||
|
||||
if (current > max) {
|
||||
return;
|
||||
@@ -247,8 +250,8 @@ export default {
|
||||
},
|
||||
confirm() {
|
||||
if (this.currentSymbol && this.currentValue.length >= 1) {
|
||||
const previousValue = this.$utilities.stringCurrencyToNumeric(this.previousValue);
|
||||
const currentValue = this.$utilities.stringCurrencyToNumeric(this.currentValue);
|
||||
const previousValue = stringCurrencyToNumeric(this.previousValue);
|
||||
const currentValue = stringCurrencyToNumeric(this.currentValue);
|
||||
let finalValue = 0;
|
||||
|
||||
switch (this.currentSymbol) {
|
||||
@@ -265,8 +268,8 @@ export default {
|
||||
finalValue = previousValue;
|
||||
}
|
||||
|
||||
if (this.$utilities.isString(this.minValue) && this.minValue !== '') {
|
||||
const min = this.$utilities.stringCurrencyToNumeric(this.minValue);
|
||||
if (isString(this.minValue) && this.minValue !== '') {
|
||||
const min = stringCurrencyToNumeric(this.minValue);
|
||||
|
||||
if (finalValue < min) {
|
||||
this.$toast('Numeric Overflow');
|
||||
@@ -274,8 +277,8 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.$utilities.isString(this.maxValue) && this.maxValue !== '') {
|
||||
const max = this.$utilities.stringCurrencyToNumeric(this.maxValue);
|
||||
if (isString(this.maxValue) && this.maxValue !== '') {
|
||||
const max = stringCurrencyToNumeric(this.maxValue);
|
||||
|
||||
if (finalValue > max) {
|
||||
this.$toast('Numeric Overflow');
|
||||
@@ -295,7 +298,7 @@ export default {
|
||||
|
||||
return true;
|
||||
} else {
|
||||
const value = this.$utilities.stringCurrencyToNumeric(this.currentValue);
|
||||
const value = stringCurrencyToNumeric(this.currentValue);
|
||||
|
||||
this.$emit('update:modelValue', value);
|
||||
this.close();
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
<span class="skeleton-text">Percent</span>
|
||||
</f7-chip>
|
||||
<f7-chip outline
|
||||
:text="$utilities.formatPercent(selectedItem.percent, 2, '<0.01')"
|
||||
:text="selectedItem.displayPercent"
|
||||
:style="getColorStyle(selectedItem ? selectedItem.color : '', '--f7-chip-outline-border-color')"
|
||||
v-else-if="!skeleton"></f7-chip>
|
||||
</p>
|
||||
@@ -60,7 +60,7 @@
|
||||
<span class="skeleton-text" v-if="skeleton">Name</span>
|
||||
<span v-else-if="!skeleton && selectedItem.name">{{ selectedItem.name }}</span>
|
||||
<span class="skeleton-text" v-if="skeleton">Value</span>
|
||||
<span v-else-if="!skeleton && showValue" :style="getColorStyle(selectedItem ? selectedItem.color : '')">{{ $locale.getDisplayCurrency(selectedItem.value, (selectedItem.currency || defaultCurrency)) }}</span>
|
||||
<span v-else-if="!skeleton && showValue" :style="getColorStyle(selectedItem ? selectedItem.color : '')">{{ selectedItem.displayValue }}</span>
|
||||
<f7-icon class="item-navigate-icon" f7="chevron_right" v-if="enableClickItem"></f7-icon>
|
||||
</f7-link>
|
||||
<f7-link :no-link-class="true" v-else-if="!validItems || !validItems.length">
|
||||
@@ -77,6 +77,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import colorConstants from '@/consts/color.js';
|
||||
import { formatPercent } from '@/lib/common.js';
|
||||
|
||||
const defaultColors = [
|
||||
'cc4a66',
|
||||
'e3564a',
|
||||
@@ -140,7 +143,7 @@ export default {
|
||||
if (item[this.valueField] && item[this.valueField] > 0 &&
|
||||
(!this.hiddenField || !item[this.hiddenField]) &&
|
||||
(!this.minValidPercent || item[this.valueField] / totalValidValue > this.minValidPercent)) {
|
||||
validItems.push({
|
||||
const finalItem = {
|
||||
name: item[this.nameField],
|
||||
value: item[this.valueField],
|
||||
percent: (item[this.percentField] > 0 || item[this.percentField] === 0 || item[this.percentField] === '0') ? item[this.percentField] : (item[this.valueField] / totalValidValue * 100),
|
||||
@@ -148,7 +151,12 @@ export default {
|
||||
currency: item[this.currencyField],
|
||||
color: item[this.colorField] ? item[this.colorField] : defaultColors[validItems.length % defaultColors.length],
|
||||
sourceItem: item
|
||||
});
|
||||
};
|
||||
|
||||
finalItem.displayPercent = formatPercent(finalItem.percent, 2, '<0.01');
|
||||
finalItem.displayValue = this.$locale.getDisplayCurrency(finalItem.value, (finalItem.currency || this.defaultCurrency));
|
||||
|
||||
validItems.push(finalItem);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +233,7 @@ export default {
|
||||
}
|
||||
},
|
||||
getColor: function (color) {
|
||||
if (color && color !== this.$constants.colors.defaultColor) {
|
||||
if (color && color !== colorConstants.defaultColor) {
|
||||
color = '#' + color;
|
||||
} else {
|
||||
color = 'var(--default-icon-color)';
|
||||
|
||||
@@ -41,6 +41,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { copyArrayTo } from '@/lib/common.js';
|
||||
import { elements } from '@/lib/ui.mobile.js';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'modelValue',
|
||||
@@ -55,7 +58,7 @@ export default {
|
||||
const self = this;
|
||||
|
||||
return {
|
||||
selectedItemIds: self.$utilities.copyArrayTo(self.modelValue, [])
|
||||
selectedItemIds: copyArrayTo(self.modelValue, [])
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -78,7 +81,7 @@ export default {
|
||||
this.$emit('update:show', false);
|
||||
},
|
||||
onSheetOpen(event) {
|
||||
this.selectedItemIds = this.$utilities.copyArrayTo(this.modelValue, []);
|
||||
this.selectedItemIds = copyArrayTo(this.modelValue, []);
|
||||
this.scrollToSelectedItem(event.$el);
|
||||
},
|
||||
onSheetClosed() {
|
||||
@@ -120,8 +123,8 @@ export default {
|
||||
let lastSelectedItem = selectedItem;
|
||||
|
||||
if (selectedItem.length > 0) {
|
||||
firstSelectedItem = this.$ui.elements(selectedItem[0]);
|
||||
lastSelectedItem = this.$ui.elements(selectedItem[selectedItem.length - 1]);
|
||||
firstSelectedItem = elements(selectedItem[0]);
|
||||
lastSelectedItem = elements(selectedItem[selectedItem.length - 1]);
|
||||
}
|
||||
|
||||
let firstSelectedItemInTop = firstSelectedItem.offset().top - container.offset().top - parseInt(container.css('padding-top'), 10);
|
||||
|
||||
@@ -39,6 +39,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { isArray } from '@/lib/common.js';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'modelValue',
|
||||
@@ -73,7 +75,7 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
hugeTreeViewItems() {
|
||||
if (this.$utilities.isArray(this.items)) {
|
||||
if (isArray(this.items)) {
|
||||
return this.items.length > 10;
|
||||
} else {
|
||||
let count = 0;
|
||||
|
||||
@@ -60,6 +60,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { isArray } from '@/lib/common.js';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'modelValue',
|
||||
@@ -104,7 +106,7 @@ export default {
|
||||
computed: {
|
||||
selectedPrimaryItem() {
|
||||
if (this.primaryValueField) {
|
||||
if (this.$utilities.isArray(this.items)) {
|
||||
if (isArray(this.items)) {
|
||||
for (let i = 0; i < this.items.length; i++) {
|
||||
const item = this.items[i];
|
||||
|
||||
@@ -181,7 +183,7 @@ export default {
|
||||
},
|
||||
getPrimaryValueBySecondaryValue(secondaryValue) {
|
||||
if (this.primarySubItemsField) {
|
||||
if (this.$utilities.isArray(this.items)) {
|
||||
if (isArray(this.items)) {
|
||||
for (let i = 0; i < this.items.length; i++) {
|
||||
const primaryItem = this.items[i];
|
||||
|
||||
|
||||
@@ -39,8 +39,18 @@ const allAccountTypes = {
|
||||
SingleAccount: 1,
|
||||
MultiSubAccounts: 2
|
||||
};
|
||||
const allAccountTypesArray = [
|
||||
{
|
||||
id: allAccountTypes.SingleAccount,
|
||||
name: 'Single Account'
|
||||
}, {
|
||||
id: allAccountTypes.MultiSubAccounts,
|
||||
name: 'Multi Sub Accounts'
|
||||
}
|
||||
];
|
||||
|
||||
export default {
|
||||
allCategories: allAccountCategories,
|
||||
allAccountTypes: allAccountTypes,
|
||||
allAccountTypesArray: allAccountTypesArray,
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { autoChangeTextareaSize } from '../../lib/mobile/ui.js';
|
||||
import { autoChangeTextareaSize } from '@/lib/ui.mobile.js';
|
||||
|
||||
export default {
|
||||
mounted(el) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import accountConstants from '../../consts/account.js';
|
||||
import accountConstants from '@/consts/account.js';
|
||||
|
||||
export function getAccountCategoryInfo(categoryId) {
|
||||
for (let i = 0; i < accountConstants.allCategories.length; i++) {
|
||||
@@ -1,5 +1,5 @@
|
||||
import categoryConstants from '../../consts/category.js';
|
||||
import transactionConstants from '../../consts/transaction.js';
|
||||
import categoryConstants from '@/consts/category.js';
|
||||
import transactionConstants from '@/consts/transaction.js';
|
||||
|
||||
export function transactionTypeToCategoryType(transactionType) {
|
||||
if (transactionType === transactionConstants.allTransactionTypes.Income) {
|
||||
@@ -1,4 +1,4 @@
|
||||
import settings from "../../lib/settings";
|
||||
import settings from './settings.js';
|
||||
|
||||
export function isFunction(val) {
|
||||
return typeof(val) === 'function';
|
||||
@@ -227,9 +227,9 @@ export function copyObjectTo(fromObject, toObject) {
|
||||
const toValue = toObject[key];
|
||||
|
||||
if (isArray(fromValue)) {
|
||||
toObject[key] = this.copyArrayTo(fromValue, toValue);
|
||||
toObject[key] = copyArrayTo(fromValue, toValue);
|
||||
} else if (isObject(fromValue)) {
|
||||
toObject[key] = this.copyObjectTo(fromValue, toValue);
|
||||
toObject[key] = copyObjectTo(fromValue, toValue);
|
||||
} else {
|
||||
if (fromValue !== toValue) {
|
||||
toObject[key] = fromValue;
|
||||
@@ -256,9 +256,9 @@ export function copyArrayTo(fromArray, toArray) {
|
||||
const toValue = toArray[i];
|
||||
|
||||
if (isArray(fromValue)) {
|
||||
toArray[i] = this.copyArrayTo(fromValue, toValue);
|
||||
toArray[i] = copyArrayTo(fromValue, toValue);
|
||||
} else if (isObject(fromValue)) {
|
||||
toArray[i] = this.copyObjectTo(fromValue, toValue);
|
||||
toArray[i] = copyObjectTo(fromValue, toValue);
|
||||
} else {
|
||||
if (fromValue !== toValue) {
|
||||
toArray[i] = fromValue;
|
||||
@@ -266,9 +266,9 @@ export function copyArrayTo(fromArray, toArray) {
|
||||
}
|
||||
} else {
|
||||
if (isArray(fromValue)) {
|
||||
toArray.push(this.copyArrayTo(fromValue, []));
|
||||
toArray.push(copyArrayTo(fromValue, []));
|
||||
} else if (isObject(fromValue)) {
|
||||
toArray.push(this.copyObjectTo(fromValue, {}));
|
||||
toArray.push(copyObjectTo(fromValue, {}));
|
||||
} else {
|
||||
toArray.push(fromValue);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { isNumber, appendThousandsSeparator } from "./common.js";
|
||||
import { isNumber, appendThousandsSeparator } from './common.js';
|
||||
|
||||
export function numericCurrencyToString(num) {
|
||||
let str = num.toString();
|
||||
@@ -1,7 +1,7 @@
|
||||
import moment from 'moment';
|
||||
|
||||
import dateTimeConstants from '../../consts/datetime.js';
|
||||
import { isNumber } from "./common.js";
|
||||
import dateTimeConstants from '@/consts/datetime.js';
|
||||
import { isNumber } from './common.js';
|
||||
|
||||
export function getUtcOffsetMinutesByUtcOffset(utcOffset) {
|
||||
if (!utcOffset) {
|
||||
+32
-19
@@ -1,9 +1,22 @@
|
||||
import { defaultLanguage, allLanguages } from '../locales/index.js';
|
||||
import datetime from "../consts/datetime.js";
|
||||
import timezone from "../consts/timezone.js";
|
||||
import currency from "../consts/currency.js";
|
||||
import settings from "./settings.js";
|
||||
import utilities from './utilities/index.js';
|
||||
import { defaultLanguage, allLanguages } from '@/locales/index.js';
|
||||
import datetime from '@/consts/datetime.js';
|
||||
import timezone from '@/consts/timezone.js';
|
||||
import currency from '@/consts/currency.js';
|
||||
import settings from './settings.js';
|
||||
import {
|
||||
isString,
|
||||
isNumber
|
||||
} from './common.js';
|
||||
import {
|
||||
formatTime,
|
||||
getCurrentDateTime,
|
||||
getTimezoneOffset,
|
||||
getTimezoneOffsetMinutes,
|
||||
getDateTimeFormatType
|
||||
} from './datetime.js';
|
||||
import {
|
||||
numericCurrencyToString
|
||||
} from './currency.js';
|
||||
|
||||
const apiNotFoundErrorCode = 100001;
|
||||
const specifiedApiNotFoundErrors = {
|
||||
@@ -332,27 +345,27 @@ export function getI18nShortTimeFormat(translateFn, formatTypeValue) {
|
||||
|
||||
export function isLongTime24HourFormat(translateFn, formatTypeValue) {
|
||||
const defaultLongTimeFormatTypeName = translateFn('default.longTimeFormat');
|
||||
const type = utilities.getDateTimeFormatType(datetime.allLongTimeFormat, datetime.allLongTimeFormatArray, defaultLongTimeFormatTypeName, datetime.defaultLongTimeFormat, formatTypeValue);
|
||||
const type = getDateTimeFormatType(datetime.allLongTimeFormat, datetime.allLongTimeFormatArray, defaultLongTimeFormatTypeName, datetime.defaultLongTimeFormat, formatTypeValue);
|
||||
return type.is24HourFormat;
|
||||
}
|
||||
|
||||
export function isShortTime24HourFormat(translateFn, formatTypeValue) {
|
||||
const defaultShortTimeFormatTypeName = translateFn('default.shortTimeFormat');
|
||||
const type = utilities.getDateTimeFormatType(datetime.allShortTimeFormat, datetime.allShortTimeFormatArray, defaultShortTimeFormatTypeName, datetime.defaultShortTimeFormat, formatTypeValue);
|
||||
const type = getDateTimeFormatType(datetime.allShortTimeFormat, datetime.allShortTimeFormatArray, defaultShortTimeFormatTypeName, datetime.defaultShortTimeFormat, formatTypeValue);
|
||||
return type.is24HourFormat;
|
||||
}
|
||||
|
||||
export function getAllTimezones(includeSystemDefault, translateFn) {
|
||||
const defaultTimezoneOffset = utilities.getTimezoneOffset();
|
||||
const defaultTimezoneOffsetMinutes = utilities.getTimezoneOffsetMinutes();
|
||||
const defaultTimezoneOffset = getTimezoneOffset();
|
||||
const defaultTimezoneOffsetMinutes = getTimezoneOffsetMinutes();
|
||||
const allTimezones = timezone.all;
|
||||
const allTimezoneInfos = [];
|
||||
|
||||
for (let i = 0; i < allTimezones.length; i++) {
|
||||
allTimezoneInfos.push({
|
||||
name: allTimezones[i].timezoneName,
|
||||
utcOffset: (allTimezones[i].timezoneName !== timezone.utcTimezoneName ? utilities.getTimezoneOffset(allTimezones[i].timezoneName) : ''),
|
||||
utcOffsetMinutes: utilities.getTimezoneOffsetMinutes(allTimezones[i].timezoneName),
|
||||
utcOffset: (allTimezones[i].timezoneName !== timezone.utcTimezoneName ? getTimezoneOffset(allTimezones[i].timezoneName) : ''),
|
||||
utcOffsetMinutes: getTimezoneOffsetMinutes(allTimezones[i].timezoneName),
|
||||
displayName: translateFn(`timezone.${allTimezones[i].displayName}`)
|
||||
});
|
||||
}
|
||||
@@ -403,22 +416,22 @@ export function getAllCurrencies(translateFn) {
|
||||
}
|
||||
|
||||
export function getDisplayCurrency(value, currencyCode, notConvertValue, translateFn) {
|
||||
if (!utilities.isNumber(value) && !utilities.isString(value)) {
|
||||
if (!isNumber(value) && !isString(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (utilities.isNumber(value)) {
|
||||
if (isNumber(value)) {
|
||||
value = value.toString();
|
||||
}
|
||||
|
||||
if (!notConvertValue) {
|
||||
const hasIncompleteFlag = utilities.isString(value) && value.charAt(value.length - 1) === '+';
|
||||
const hasIncompleteFlag = isString(value) && value.charAt(value.length - 1) === '+';
|
||||
|
||||
if (hasIncompleteFlag) {
|
||||
value = value.substring(0, value.length - 1);
|
||||
}
|
||||
|
||||
value = utilities.numericCurrencyToString(value);
|
||||
value = numericCurrencyToString(value);
|
||||
|
||||
if (hasIncompleteFlag) {
|
||||
value = value + '+';
|
||||
@@ -563,7 +576,7 @@ function getDateTimeFormats(translateFn, allFormatMap, allFormatArray, localeFor
|
||||
ret.push({
|
||||
type: datetime.defaultDateTimeFormatValue,
|
||||
format: defaultFormat,
|
||||
displayName: `${translateFn('Language Default')} (${utilities.formatTime(utilities.getCurrentDateTime(), defaultFormat)})`
|
||||
displayName: `${translateFn('Language Default')} (${formatTime(getCurrentDateTime(), defaultFormat)})`
|
||||
});
|
||||
|
||||
for (let i = 0; i < allFormatArray.length; i++) {
|
||||
@@ -573,7 +586,7 @@ function getDateTimeFormats(translateFn, allFormatMap, allFormatArray, localeFor
|
||||
ret.push({
|
||||
type: formatType.type,
|
||||
format: format,
|
||||
displayName: utilities.formatTime(utilities.getCurrentDateTime(), format)
|
||||
displayName: formatTime(getCurrentDateTime(), format)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -581,7 +594,7 @@ function getDateTimeFormats(translateFn, allFormatMap, allFormatArray, localeFor
|
||||
}
|
||||
|
||||
function getDateTimeFormat(translateFn, allFormatMap, allFormatArray, localeFormatPathPrefix, localeDefaultFormatTypeName, systemDefaultFormatType, formatTypeValue) {
|
||||
const type = utilities.getDateTimeFormatType(allFormatMap, allFormatArray,
|
||||
const type = getDateTimeFormatType(allFormatMap, allFormatArray,
|
||||
localeDefaultFormatTypeName, systemDefaultFormatType, formatTypeValue);
|
||||
return translateFn(`${localeFormatPathPrefix}.${type.key}`);
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,9 +1,9 @@
|
||||
import axios from 'axios';
|
||||
|
||||
import api from '../consts/api.js';
|
||||
import api from '@/consts/api.js';
|
||||
import userState from './userstate.js';
|
||||
import settings from './settings.js';
|
||||
import utilities from './utilities/index.js';
|
||||
import { getTimezoneOffsetMinutes } from './datetime.js';
|
||||
|
||||
let needBlockRequest = false;
|
||||
let blockedRequests = [];
|
||||
@@ -17,7 +17,7 @@ axios.interceptors.request.use(config => {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
config.headers['X-Timezone-Offset'] = utilities.getTimezoneOffsetMinutes();
|
||||
config.headers['X-Timezone-Offset'] = getTimezoneOffsetMinutes();
|
||||
|
||||
if (needBlockRequest && !config.ignoreBlocked) {
|
||||
return new Promise(resolve => {
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import Cookies from 'js-cookie';
|
||||
|
||||
import currencyConstants from '../consts/currency.js';
|
||||
import statisticsConstants from '../consts/statistics.js';
|
||||
import currencyConstants from '@/consts/currency.js';
|
||||
import statisticsConstants from '@/consts/statistics.js';
|
||||
|
||||
const settingsLocalStorageKey = 'ebk_app_settings';
|
||||
const serverSettingsCookieKey = 'ebk_server_settings';
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { f7, f7ready } from 'framework7-vue';
|
||||
|
||||
import settings from "../settings.js";
|
||||
import settings from './settings.js';
|
||||
import {
|
||||
getLocalizedError,
|
||||
getLocalizedErrorParameters
|
||||
} from "../i18n.js";
|
||||
} from './i18n.js';
|
||||
|
||||
export function showAlert(message, confirmCallback, translateFn) {
|
||||
let parameters = {};
|
||||
@@ -1,7 +1,7 @@
|
||||
import CryptoJS from 'crypto-js';
|
||||
|
||||
import settings from './settings.js';
|
||||
import utilities from './utilities/index.js';
|
||||
import { isString, isObject } from './common.js';
|
||||
|
||||
const appLockSecretBaseStringPrefix = 'EBK_LOCK_SECRET_';
|
||||
|
||||
@@ -141,7 +141,7 @@ function isCorrectPinCode(pinCode) {
|
||||
}
|
||||
|
||||
function updateToken(token) {
|
||||
if (utilities.isString(token)) {
|
||||
if (isString(token)) {
|
||||
if (settings.isEnableApplicationLock()) {
|
||||
sessionStorage.setItem(tokenSessionStorageKey, token);
|
||||
|
||||
@@ -155,7 +155,7 @@ function updateToken(token) {
|
||||
}
|
||||
|
||||
function updateUserInfo(user) {
|
||||
if (utilities.isObject(user)) {
|
||||
if (isObject(user)) {
|
||||
localStorage.setItem(userInfoLocalStorageKey, JSON.stringify(user));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,179 +0,0 @@
|
||||
import {
|
||||
isFunction,
|
||||
isObject,
|
||||
isArray,
|
||||
isString,
|
||||
isNumber,
|
||||
isBoolean,
|
||||
isEquals,
|
||||
appendThousandsSeparator,
|
||||
formatPercent,
|
||||
limitText,
|
||||
base64encode,
|
||||
arrayBufferToString,
|
||||
stringToArrayBuffer,
|
||||
getNameByKeyValue,
|
||||
copyObjectTo,
|
||||
copyArrayTo,
|
||||
arrangeArrayWithNewStartIndex,
|
||||
} from './common.js'
|
||||
|
||||
import {
|
||||
getUtcOffsetMinutesByUtcOffset,
|
||||
getUtcOffsetByUtcOffsetMinutes,
|
||||
getTimezoneOffset,
|
||||
getTimezoneOffsetMinutes,
|
||||
getBrowserTimezoneOffsetMinutes,
|
||||
getLocalDatetimeFromUnixTime,
|
||||
getUnixTimeFromLocalDatetime,
|
||||
getActualUnixTimeForStore,
|
||||
getDummyUnixTimeForLocalUsage,
|
||||
getCurrentUnixTime,
|
||||
getCurrentDateTime,
|
||||
parseDateFromUnixTime,
|
||||
formatUnixTime,
|
||||
formatTime,
|
||||
getUnixTime,
|
||||
getYear,
|
||||
getMonth,
|
||||
getYearAndMonth,
|
||||
getDay,
|
||||
getDayOfWeekName,
|
||||
getHour,
|
||||
getMinute,
|
||||
getSecond,
|
||||
getUnixTimeBeforeUnixTime,
|
||||
getUnixTimeAfterUnixTime,
|
||||
getMinuteFirstUnixTime,
|
||||
getMinuteLastUnixTime,
|
||||
getTodayFirstUnixTime,
|
||||
getTodayLastUnixTime,
|
||||
getThisWeekFirstUnixTime,
|
||||
getThisWeekLastUnixTime,
|
||||
getThisMonthFirstUnixTime,
|
||||
getThisMonthLastUnixTime,
|
||||
getThisYearFirstUnixTime,
|
||||
getThisYearLastUnixTime,
|
||||
getDateTimeFormatType,
|
||||
getShiftedDateRange,
|
||||
getDateRangeByDateType,
|
||||
isDateRangeMatchFullYears,
|
||||
isDateRangeMatchFullMonths,
|
||||
} from './datetime.js'
|
||||
|
||||
import {
|
||||
numericCurrencyToString,
|
||||
stringCurrencyToNumeric,
|
||||
getExchangedAmount,
|
||||
} from './currency.js'
|
||||
|
||||
import {
|
||||
generateRandomString,
|
||||
parseUserAgent,
|
||||
parseDeviceInfo,
|
||||
makeButtonCopyToClipboard,
|
||||
changeClipboardObjectText,
|
||||
} from './misc.js'
|
||||
|
||||
import {
|
||||
transactionTypeToCategoryType,
|
||||
categoryTypeToTransactionType,
|
||||
getTransactionPrimaryCategoryName,
|
||||
getTransactionSecondaryCategoryName,
|
||||
allVisibleTransactionCategories,
|
||||
} from './category.js'
|
||||
|
||||
import {
|
||||
getAccountCategoryInfo,
|
||||
getCategorizedAccounts,
|
||||
getVisibleCategorizedAccounts,
|
||||
getAllFilteredAccountsBalance,
|
||||
} from './account.js'
|
||||
|
||||
export default {
|
||||
// common.js
|
||||
isFunction,
|
||||
isObject,
|
||||
isArray,
|
||||
isString,
|
||||
isNumber,
|
||||
isBoolean,
|
||||
isEquals,
|
||||
appendThousandsSeparator,
|
||||
formatPercent,
|
||||
limitText,
|
||||
base64encode,
|
||||
arrayBufferToString,
|
||||
stringToArrayBuffer,
|
||||
getNameByKeyValue,
|
||||
copyObjectTo,
|
||||
copyArrayTo,
|
||||
arrangeArrayWithNewStartIndex,
|
||||
|
||||
// datetime.js
|
||||
getUtcOffsetMinutesByUtcOffset,
|
||||
getUtcOffsetByUtcOffsetMinutes,
|
||||
getTimezoneOffset,
|
||||
getTimezoneOffsetMinutes,
|
||||
getBrowserTimezoneOffsetMinutes,
|
||||
getLocalDatetimeFromUnixTime,
|
||||
getUnixTimeFromLocalDatetime,
|
||||
getActualUnixTimeForStore,
|
||||
getDummyUnixTimeForLocalUsage,
|
||||
getCurrentUnixTime,
|
||||
getCurrentDateTime,
|
||||
parseDateFromUnixTime,
|
||||
formatUnixTime,
|
||||
formatTime,
|
||||
getUnixTime,
|
||||
getYear,
|
||||
getMonth,
|
||||
getYearAndMonth,
|
||||
getDay,
|
||||
getDayOfWeekName,
|
||||
getHour,
|
||||
getMinute,
|
||||
getSecond,
|
||||
getUnixTimeBeforeUnixTime,
|
||||
getUnixTimeAfterUnixTime,
|
||||
getMinuteFirstUnixTime,
|
||||
getMinuteLastUnixTime,
|
||||
getTodayFirstUnixTime,
|
||||
getTodayLastUnixTime,
|
||||
getThisWeekFirstUnixTime,
|
||||
getThisWeekLastUnixTime,
|
||||
getThisMonthFirstUnixTime,
|
||||
getThisMonthLastUnixTime,
|
||||
getThisYearFirstUnixTime,
|
||||
getThisYearLastUnixTime,
|
||||
getDateTimeFormatType,
|
||||
getShiftedDateRange,
|
||||
getDateRangeByDateType,
|
||||
isDateRangeMatchFullYears,
|
||||
isDateRangeMatchFullMonths,
|
||||
|
||||
// currency.js
|
||||
numericCurrencyToString,
|
||||
stringCurrencyToNumeric,
|
||||
getExchangedAmount,
|
||||
|
||||
// misc.js
|
||||
generateRandomString,
|
||||
parseUserAgent,
|
||||
parseDeviceInfo,
|
||||
makeButtonCopyToClipboard,
|
||||
changeClipboardObjectText,
|
||||
|
||||
// category.js
|
||||
transactionTypeToCategoryType,
|
||||
categoryTypeToTransactionType,
|
||||
getTransactionPrimaryCategoryName,
|
||||
getTransactionSecondaryCategoryName,
|
||||
allVisibleTransactionCategories,
|
||||
|
||||
// account.js
|
||||
getAccountCategoryInfo,
|
||||
getCategorizedAccounts,
|
||||
getVisibleCategorizedAccounts,
|
||||
getAllFilteredAccountsBalance,
|
||||
};
|
||||
+19
-11
@@ -1,6 +1,14 @@
|
||||
import CBOR from 'cbor-js';
|
||||
import logger from './logger.js';
|
||||
import utilities from './utilities/index.js';
|
||||
import {
|
||||
isFunction,
|
||||
stringToArrayBuffer,
|
||||
arrayBufferToString,
|
||||
base64encode
|
||||
} from './common.js';
|
||||
import {
|
||||
generateRandomString
|
||||
} from './misc.js';
|
||||
|
||||
const publicKeyCredentialCreationOptionsBaseTemplate = {
|
||||
attestation: "none",
|
||||
@@ -28,7 +36,7 @@ const publicKeyCredentialRequestOptionsBaseTemplate = {
|
||||
function isSupported() {
|
||||
return !!window.PublicKeyCredential
|
||||
&& !!navigator.credentials
|
||||
&& utilities.isFunction(window.PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable);
|
||||
&& isFunction(window.PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable);
|
||||
}
|
||||
|
||||
function isCompletelySupported() {
|
||||
@@ -52,17 +60,17 @@ function registerCredential({ username, secret }, { nickname }) {
|
||||
});
|
||||
}
|
||||
|
||||
const challenge = utilities.generateRandomString();
|
||||
const challenge = generateRandomString();
|
||||
const userId = `${username}|${secret}`; // username 32bytes(max) + secret 24bytes = 56bytes(max)
|
||||
|
||||
const publicKeyCredentialCreationOptions = Object.assign({}, publicKeyCredentialCreationOptionsBaseTemplate, {
|
||||
challenge: utilities.stringToArrayBuffer(challenge),
|
||||
challenge: stringToArrayBuffer(challenge),
|
||||
rp: {
|
||||
name: window.location.hostname,
|
||||
id: window.location.hostname
|
||||
},
|
||||
user: {
|
||||
id: utilities.stringToArrayBuffer(userId),
|
||||
id: stringToArrayBuffer(userId),
|
||||
name: username,
|
||||
displayName: nickname
|
||||
}
|
||||
@@ -83,7 +91,7 @@ function registerCredential({ username, secret }, { nickname }) {
|
||||
if (rawCredential && rawCredential.rawId &&
|
||||
clientData && clientData.type === 'webauthn.create' && challengeFromClientData === challenge) {
|
||||
const ret = {
|
||||
id: utilities.base64encode(rawCredential.rawId),
|
||||
id: base64encode(rawCredential.rawId),
|
||||
clientData: clientData,
|
||||
publicKey: publicKey,
|
||||
rawCredential: rawCredential
|
||||
@@ -133,12 +141,12 @@ function verifyCredential({ username }, credentialId) {
|
||||
});
|
||||
}
|
||||
|
||||
const challenge = utilities.generateRandomString();
|
||||
const challenge = generateRandomString();
|
||||
const publicKeyCredentialRequestOptions = Object.assign({}, publicKeyCredentialRequestOptionsBaseTemplate, {
|
||||
challenge: utilities.stringToArrayBuffer(challenge),
|
||||
challenge: stringToArrayBuffer(challenge),
|
||||
rpId: window.location.hostname
|
||||
});
|
||||
publicKeyCredentialRequestOptions.allowCredentials[0].id = utilities.stringToArrayBuffer(atob(credentialId));
|
||||
publicKeyCredentialRequestOptions.allowCredentials[0].id = stringToArrayBuffer(atob(credentialId));
|
||||
|
||||
logger.debug('webauthn get options', publicKeyCredentialRequestOptions);
|
||||
|
||||
@@ -147,7 +155,7 @@ function verifyCredential({ username }, credentialId) {
|
||||
}).then(rawCredential => {
|
||||
const clientData = rawCredential ? parseClientData(rawCredential) : null;
|
||||
const challengeFromClientData = clientData && clientData.challenge ? atob(clientData.challenge) : null;
|
||||
const userIdParts = rawCredential && rawCredential.response && rawCredential.response.userHandle ? utilities.arrayBufferToString(rawCredential.response.userHandle).split('|') : null;
|
||||
const userIdParts = rawCredential && rawCredential.response && rawCredential.response.userHandle ? arrayBufferToString(rawCredential.response.userHandle).split('|') : null;
|
||||
|
||||
logger.debug('webauthn get raw response', rawCredential);
|
||||
|
||||
@@ -155,7 +163,7 @@ function verifyCredential({ username }, credentialId) {
|
||||
clientData && clientData.type === 'webauthn.get' && challengeFromClientData === challenge &&
|
||||
userIdParts && userIdParts.length === 2 && userIdParts[0] === username) {
|
||||
const ret = {
|
||||
id: utilities.base64encode(rawCredential.rawId),
|
||||
id: base64encode(rawCredential.rawId),
|
||||
userName: userIdParts[0],
|
||||
userSecret: userIdParts[1],
|
||||
clientData: clientData,
|
||||
|
||||
+63
-92
@@ -1,8 +1,8 @@
|
||||
import { createApp } from 'vue';
|
||||
import { createStore } from 'vuex';
|
||||
import { createPinia } from 'pinia';
|
||||
import { createI18n } from 'vue-i18n';
|
||||
|
||||
import moment from "moment-timezone";
|
||||
import moment from 'moment-timezone';
|
||||
|
||||
import Framework7 from 'framework7/lite';
|
||||
import Framework7Dialog from 'framework7/components/dialog';
|
||||
@@ -76,24 +76,18 @@ import '@vuepic/vue-datepicker/dist/main.css';
|
||||
import * as Leaflet from 'leaflet/dist/leaflet-src.esm.js';
|
||||
import 'leaflet/dist/leaflet.css';
|
||||
|
||||
import api from './consts/api.js';
|
||||
import datetime from './consts/datetime.js';
|
||||
import currency from './consts/currency.js';
|
||||
import colors from './consts/color.js';
|
||||
import icons from './consts/icon.js';
|
||||
import account from './consts/account.js';
|
||||
import transaction from './consts/transaction.js';
|
||||
import category from './consts/category.js';
|
||||
import statistics from './consts/statistics.js';
|
||||
import datetimeConstants from '@/consts/datetime.js';
|
||||
|
||||
import licenses from './lib/licenses.js';
|
||||
import version from './lib/version.js';
|
||||
import logger from './lib/logger.js';
|
||||
import settings from './lib/settings.js';
|
||||
import services from './lib/services.js';
|
||||
import userstate from './lib/userstate.js';
|
||||
import webauthn from './lib/webauthn.js';
|
||||
import utilities from './lib/utilities/index.js';
|
||||
import version from '@/lib/version.js';
|
||||
import logger from '@/lib/logger.js';
|
||||
import settings from '@/lib/settings.js';
|
||||
import services from '@/lib/services.js';
|
||||
import userstate from '@/lib/userstate.js';
|
||||
import {
|
||||
formatUnixTime,
|
||||
formatTime,
|
||||
getTimezoneOffset
|
||||
} from '@/lib/datetime.js';
|
||||
import {
|
||||
getAllLanguageInfos,
|
||||
getLanguageInfo,
|
||||
@@ -124,42 +118,40 @@ import {
|
||||
getAllCurrencies,
|
||||
getDisplayCurrency,
|
||||
getI18nOptions,
|
||||
} from './lib/i18n.js';
|
||||
} from '@/lib/i18n.js';
|
||||
import {
|
||||
showAlert,
|
||||
showConfirm,
|
||||
showToast,
|
||||
showLoading,
|
||||
hideLoading,
|
||||
routeBackOnError,
|
||||
elements,
|
||||
isModalShowing,
|
||||
onSwipeoutDeleted
|
||||
} from './lib/mobile/ui.js';
|
||||
routeBackOnError
|
||||
} from '@/lib/ui.mobile.js';
|
||||
|
||||
import stores from './store/index.js';
|
||||
import ItemIcon from '@/components/mobile/ItemIcon.vue';
|
||||
import PieChart from '@/components/mobile/PieChart.vue';
|
||||
import PinCodeInput from '@/components/mobile/PinCodeInput.vue';
|
||||
import PinCodeInputSheet from '@/components/mobile/PinCodeInputSheet.vue';
|
||||
import PasswordInputSheet from '@/components/mobile/PasswordInputSheet.vue';
|
||||
import PasscodeInputSheet from '@/components/mobile/PasscodeInputSheet.vue';
|
||||
import DateTimeSelectionSheet from '@/components/mobile/DateTimeSelectionSheet.vue';
|
||||
import DateRangeSelectionSheet from '@/components/mobile/DateRangeSelectionSheet.vue';
|
||||
import ListItemSelectionSheet from '@/components/mobile/ListItemSelectionSheet.vue';
|
||||
import TwoColumnListItemSelectionSheet from '@/components/mobile/TwoColumnListItemSelectionSheet.vue';
|
||||
import TreeViewSelectionSheet from '@/components/mobile/TreeViewSelectionSheet.vue';
|
||||
import IconSelectionSheet from '@/components/mobile/IconSelectionSheet.vue';
|
||||
import ColorSelectionSheet from '@/components/mobile/ColorSelectionSheet.vue';
|
||||
import InformationSheet from '@/components/mobile/InformationSheet.vue';
|
||||
import NumberPadSheet from '@/components/mobile/NumberPadSheet.vue';
|
||||
import MapSheet from '@/components/mobile/MapSheet.vue';
|
||||
import TransactionTagSelectionSheet from '@/components/mobile/TransactionTagSelectionSheet.vue';
|
||||
|
||||
import ItemIcon from './components/mobile/ItemIcon.vue';
|
||||
import PieChart from './components/mobile/PieChart.vue';
|
||||
import PinCodeInput from './components/mobile/PinCodeInput.vue';
|
||||
import PinCodeInputSheet from './components/mobile/PinCodeInputSheet.vue';
|
||||
import PasswordInputSheet from './components/mobile/PasswordInputSheet.vue';
|
||||
import PasscodeInputSheet from './components/mobile/PasscodeInputSheet.vue';
|
||||
import DateTimeSelectionSheet from './components/mobile/DateTimeSelectionSheet.vue';
|
||||
import DateRangeSelectionSheet from './components/mobile/DateRangeSelectionSheet.vue';
|
||||
import ListItemSelectionSheet from './components/mobile/ListItemSelectionSheet.vue';
|
||||
import TwoColumnListItemSelectionSheet from './components/mobile/TwoColumnListItemSelectionSheet.vue';
|
||||
import TreeViewSelectionSheet from './components/mobile/TreeViewSelectionSheet.vue';
|
||||
import IconSelectionSheet from './components/mobile/IconSelectionSheet.vue';
|
||||
import ColorSelectionSheet from './components/mobile/ColorSelectionSheet.vue';
|
||||
import InformationSheet from './components/mobile/InformationSheet.vue';
|
||||
import NumberPadSheet from './components/mobile/NumberPadSheet.vue';
|
||||
import MapSheet from './components/mobile/MapSheet.vue';
|
||||
import TransactionTagSelectionSheet from './components/mobile/TransactionTagSelectionSheet.vue';
|
||||
import TextareaAutoSize from '@/directives/mobile/textareaAutoSize.js';
|
||||
|
||||
import TextareaAutoSize from "./directives/mobile/textareaAutoSize.js";
|
||||
import { useSettingsStore } from '@/stores/setting.js';
|
||||
import { useUserStore } from '@/stores/user.js';
|
||||
|
||||
import App from './MobileApp.vue';
|
||||
import App from '@/MobileApp.vue';
|
||||
|
||||
Framework7.use([
|
||||
Framework7Dialog,
|
||||
@@ -195,10 +187,10 @@ Framework7.use([
|
||||
]);
|
||||
|
||||
const app = createApp(App);
|
||||
const store = createStore(stores);
|
||||
const pinia = createPinia();
|
||||
const i18n = createI18n(getI18nOptions());
|
||||
registerComponents(app);
|
||||
app.use(store);
|
||||
app.use(pinia);
|
||||
app.use(i18n);
|
||||
|
||||
function setLanguage(locale) {
|
||||
@@ -238,13 +230,14 @@ function setLanguage(locale) {
|
||||
|
||||
const defaultCurrency = i18n.global.t('default.currency');
|
||||
const defaultFirstDayOfWeekName = i18n.global.t('default.firstDayOfWeek');
|
||||
let defaultFirstDayOfWeek = datetime.defaultFirstDayOfWeek;
|
||||
let defaultFirstDayOfWeek = datetimeConstants.defaultFirstDayOfWeek;
|
||||
|
||||
if (datetime.allWeekDays[defaultFirstDayOfWeekName]) {
|
||||
defaultFirstDayOfWeek = datetime.allWeekDays[defaultFirstDayOfWeekName].type;
|
||||
if (datetimeConstants.allWeekDays[defaultFirstDayOfWeekName]) {
|
||||
defaultFirstDayOfWeek = datetimeConstants.allWeekDays[defaultFirstDayOfWeekName].type;
|
||||
}
|
||||
|
||||
store.dispatch('updateLocalizedDefaultSettings', { defaultCurrency, defaultFirstDayOfWeek });
|
||||
const settingsStore = useSettingsStore();
|
||||
settingsStore.updateLocalizedDefaultSettings({ defaultCurrency, defaultFirstDayOfWeek });
|
||||
|
||||
return locale;
|
||||
}
|
||||
@@ -260,7 +253,8 @@ function setTimezone(timezone) {
|
||||
}
|
||||
|
||||
function initLocale() {
|
||||
const lastUserLanguage = store.getters.currentUserLanguage;
|
||||
const userStore = useUserStore();
|
||||
const lastUserLanguage = userStore.currentUserLanguage;
|
||||
|
||||
if (lastUserLanguage && getLanguageInfo(lastUserLanguage)) {
|
||||
logger.info(`Last user language is ${lastUserLanguage}`);
|
||||
@@ -273,7 +267,7 @@ function initLocale() {
|
||||
logger.info(`Current timezone is ${settings.getTimezone()}`);
|
||||
setTimezone(settings.getTimezone());
|
||||
} else {
|
||||
logger.info(`No timezone is set, use browser default ${utilities.getTimezoneOffset()} (maybe ${moment.tz.guess(true)})`);
|
||||
logger.info(`No timezone is set, use browser default ${getTimezoneOffset()} (maybe ${moment.tz.guess(true)})`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,26 +296,6 @@ app.directive('TextareaAutoSize', TextareaAutoSize);
|
||||
app.config.globalProperties.$version = version.getVersion();
|
||||
app.config.globalProperties.$buildTime = version.getBuildTime();
|
||||
|
||||
app.config.globalProperties.$licenses = {
|
||||
license: licenses.getLicense(),
|
||||
thirdPartyLicenses: licenses.getThirdPartyLicenses()
|
||||
};
|
||||
|
||||
app.config.globalProperties.$constants = {
|
||||
api: api,
|
||||
datetime: datetime,
|
||||
currency: currency,
|
||||
colors: colors,
|
||||
icons: icons,
|
||||
account: account,
|
||||
transaction: transaction,
|
||||
category: category,
|
||||
statistics: statistics,
|
||||
};
|
||||
|
||||
app.config.globalProperties.$utilities = utilities;
|
||||
app.config.globalProperties.$logger = logger;
|
||||
app.config.globalProperties.$webauthn = webauthn;
|
||||
app.config.globalProperties.$settings = settings;
|
||||
app.config.globalProperties.$locale = {
|
||||
getDefaultLanguage: getDefaultLanguage,
|
||||
@@ -336,20 +310,22 @@ app.config.globalProperties.$locale = {
|
||||
getAllShortDateFormats: () => getAllShortDateFormats(i18n.global.t),
|
||||
getAllLongTimeFormats: () => getAllLongTimeFormats(i18n.global.t),
|
||||
getAllShortTimeFormats: () => getAllShortTimeFormats(i18n.global.t),
|
||||
getLongDateTimeFormat: () => getI18nLongDateFormat(i18n.global.t, store.getters.currentUserLongDateFormat) + ' ' + getI18nLongTimeFormat(i18n.global.t, store.getters.currentUserLongTimeFormat),
|
||||
getShortDateTimeFormat: () => getI18nShortDateFormat(i18n.global.t, store.getters.currentUserShortDateFormat) + ' ' + getI18nShortTimeFormat(i18n.global.t, store.getters.currentUserShortTimeFormat),
|
||||
getLongDateFormat: () => getI18nLongDateFormat(i18n.global.t, store.getters.currentUserLongDateFormat),
|
||||
getShortDateFormat: () => getI18nShortDateFormat(i18n.global.t, store.getters.currentUserShortDateFormat),
|
||||
getLongYearFormat: () => getI18nLongYearFormat(i18n.global.t, store.getters.currentUserLongDateFormat),
|
||||
getShortYearFormat: () => getI18nShortYearFormat(i18n.global.t, store.getters.currentUserShortDateFormat),
|
||||
getLongYearMonthFormat: () => getI18nLongYearMonthFormat(i18n.global.t, store.getters.currentUserLongDateFormat),
|
||||
getShortYearMonthFormat: () => getI18nShortYearMonthFormat(i18n.global.t, store.getters.currentUserShortDateFormat),
|
||||
getLongMonthDayFormat: () => getI18nLongMonthDayFormat(i18n.global.t, store.getters.currentUserLongDateFormat),
|
||||
getShortMonthDayFormat: () => getI18nShortMonthDayFormat(i18n.global.t, store.getters.currentUserShortDateFormat),
|
||||
getLongTimeFormat: () => getI18nLongTimeFormat(i18n.global.t, store.getters.currentUserLongTimeFormat),
|
||||
getShortTimeFormat: () => getI18nShortTimeFormat(i18n.global.t, store.getters.currentUserShortTimeFormat),
|
||||
isLongTime24HourFormat: () => isLongTime24HourFormat(i18n.global.t, store.getters.currentUserLongTimeFormat),
|
||||
isShortTime24HourFormat: () => isShortTime24HourFormat(i18n.global.t, store.getters.currentUserShortTimeFormat),
|
||||
formatUnixTimeToLongDateTime: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nLongDateFormat(i18n.global.t, userStore.currentUserLongDateFormat) + ' ' + getI18nLongTimeFormat(i18n.global.t, userStore.currentUserLongTimeFormat), utcOffset, currentUtcOffset),
|
||||
formatUnixTimeToShortDateTime: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nShortDateFormat(i18n.global.t, userStore.currentUserShortDateFormat) + ' ' + getI18nShortTimeFormat(i18n.global.t, userStore.currentUserShortTimeFormat), utcOffset, currentUtcOffset),
|
||||
formatUnixTimeToLongDate: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nLongDateFormat(i18n.global.t, userStore.currentUserLongDateFormat), utcOffset, currentUtcOffset),
|
||||
formatUnixTimeToShortDate: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nShortDateFormat(i18n.global.t, userStore.currentUserShortDateFormat), utcOffset, currentUtcOffset),
|
||||
formatUnixTimeToLongYear: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nLongYearFormat(i18n.global.t, userStore.currentUserLongDateFormat), utcOffset, currentUtcOffset),
|
||||
formatUnixTimeToShortYear: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nShortYearFormat(i18n.global.t, userStore.currentUserShortDateFormat), utcOffset, currentUtcOffset),
|
||||
formatUnixTimeToLongYearMonth: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nLongYearMonthFormat(i18n.global.t, userStore.currentUserLongDateFormat), utcOffset, currentUtcOffset),
|
||||
formatUnixTimeToShortYearMonth: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nShortYearMonthFormat(i18n.global.t, userStore.currentUserShortDateFormat), utcOffset, currentUtcOffset),
|
||||
formatUnixTimeToLongMonthDay: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nLongMonthDayFormat(i18n.global.t, userStore.currentUserLongDateFormat), utcOffset, currentUtcOffset),
|
||||
formatUnixTimeToShortMonthDay: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nShortMonthDayFormat(i18n.global.t, userStore.currentUserShortDateFormat), utcOffset, currentUtcOffset),
|
||||
formatUnixTimeToLongTime: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nLongTimeFormat(i18n.global.t, userStore.currentUserLongTimeFormat), utcOffset, currentUtcOffset),
|
||||
formatUnixTimeToShortTime: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nShortTimeFormat(i18n.global.t, userStore.currentUserShortTimeFormat), utcOffset, currentUtcOffset),
|
||||
formatTimeToLongYearMonth: (userStore, dateTime) => formatTime(dateTime, getI18nLongYearMonthFormat(i18n.global.t, userStore.currentUserLongDateFormat)),
|
||||
formatTimeToShortYearMonth: (userStore, dateTime) => formatTime(dateTime, getI18nShortYearMonthFormat(i18n.global.t, userStore.currentUserShortDateFormat)),
|
||||
isLongTime24HourFormat: (userStore) => isLongTime24HourFormat(i18n.global.t, userStore.currentUserLongTimeFormat),
|
||||
isShortTime24HourFormat: (userStore) => isShortTime24HourFormat(i18n.global.t, userStore.currentUserShortTimeFormat),
|
||||
setLanguage: setLanguage,
|
||||
getTimezone: settings.getTimezone,
|
||||
setTimezone: setTimezone,
|
||||
@@ -370,11 +346,6 @@ app.config.globalProperties.$toast = (message, timeout) => showToast(message, ti
|
||||
app.config.globalProperties.$showLoading = showLoading;
|
||||
app.config.globalProperties.$hideLoading = hideLoading;
|
||||
app.config.globalProperties.$routeBackOnError = routeBackOnError;
|
||||
app.config.globalProperties.$ui = {
|
||||
elements: elements,
|
||||
isModalShowing: isModalShowing,
|
||||
onSwipeoutDeleted: onSwipeoutDeleted
|
||||
};
|
||||
|
||||
app.config.globalProperties.$user = userstate;
|
||||
|
||||
|
||||
+26
-26
@@ -1,37 +1,37 @@
|
||||
import userState from '../lib/userstate.js';
|
||||
import userState from '@/lib/userstate.js';
|
||||
|
||||
import HomePage from '../views/mobile/HomePage.vue';
|
||||
import LoginPage from '../views/mobile/LoginPage.vue';
|
||||
import SignUpPage from '../views/mobile/SignupPage.vue';
|
||||
import UnlockPage from '../views/mobile/UnlockPage.vue';
|
||||
import HomePage from '@/views/mobile/HomePage.vue';
|
||||
import LoginPage from '@/views/mobile/LoginPage.vue';
|
||||
import SignUpPage from '@/views/mobile/SignupPage.vue';
|
||||
import UnlockPage from '@/views/mobile/UnlockPage.vue';
|
||||
|
||||
import TransactionListPage from '../views/mobile/transactions/ListPage.vue';
|
||||
import TransactionEditPage from '../views/mobile/transactions/EditPage.vue';
|
||||
import TransactionListPage from '@/views/mobile/transactions/ListPage.vue';
|
||||
import TransactionEditPage from '@/views/mobile/transactions/EditPage.vue';
|
||||
|
||||
import AccountListPage from '../views/mobile/accounts/ListPage.vue';
|
||||
import AccountEditPage from '../views/mobile/accounts/EditPage.vue';
|
||||
import AccountListPage from '@/views/mobile/accounts/ListPage.vue';
|
||||
import AccountEditPage from '@/views/mobile/accounts/EditPage.vue';
|
||||
|
||||
import StatisticsTransactionPage from '../views/mobile/statistics/TransactionPage.vue';
|
||||
import StatisticsSettingsPage from '../views/mobile/statistics/SettingsPage.vue';
|
||||
import StatisticsAccountFilterSettingsPage from '../views/mobile/statistics/AccountFilterSettingsPage.vue';
|
||||
import StatisticsCategoryFilterSettingsPage from '../views/mobile/statistics/CategoryFilterSettingsPage.vue';
|
||||
import StatisticsTransactionPage from '@/views/mobile/statistics/TransactionPage.vue';
|
||||
import StatisticsSettingsPage from '@/views/mobile/statistics/SettingsPage.vue';
|
||||
import StatisticsAccountFilterSettingsPage from '@/views/mobile/statistics/AccountFilterSettingsPage.vue';
|
||||
import StatisticsCategoryFilterSettingsPage from '@/views/mobile/statistics/CategoryFilterSettingsPage.vue';
|
||||
|
||||
import SettingsPage from '../views/mobile/SettingsPage.vue';
|
||||
import ApplicationLockPage from '../views/mobile/ApplicationLockPage.vue';
|
||||
import ExchangeRatesPage from '../views/mobile/ExchangeRatesPage.vue';
|
||||
import AboutPage from '../views/mobile/AboutPage.vue';
|
||||
import SettingsPage from '@/views/mobile/SettingsPage.vue';
|
||||
import ApplicationLockPage from '@/views/mobile/ApplicationLockPage.vue';
|
||||
import ExchangeRatesPage from '@/views/mobile/ExchangeRatesPage.vue';
|
||||
import AboutPage from '@/views/mobile/AboutPage.vue';
|
||||
|
||||
import UserProfilePage from '../views/mobile/users/UserProfilePage.vue';
|
||||
import DataManagementPage from '../views/mobile/users/DataManagementPage.vue';
|
||||
import TwoFactorAuthPage from '../views/mobile/users/TwoFactorAuthPage.vue';
|
||||
import SessionListPage from '../views/mobile/users/SessionListPage.vue';
|
||||
import UserProfilePage from '@/views/mobile/users/UserProfilePage.vue';
|
||||
import DataManagementPage from '@/views/mobile/users/DataManagementPage.vue';
|
||||
import TwoFactorAuthPage from '@/views/mobile/users/TwoFactorAuthPage.vue';
|
||||
import SessionListPage from '@/views/mobile/users/SessionListPage.vue';
|
||||
|
||||
import CategoryAllPage from '../views/mobile/categories/AllPage.vue';
|
||||
import CategoryListPage from '../views/mobile/categories/ListPage.vue';
|
||||
import CategoryEditPage from '../views/mobile/categories/EditPage.vue';
|
||||
import CategoryPresetPage from '../views/mobile/categories/PresetPage.vue';
|
||||
import CategoryAllPage from '@/views/mobile/categories/AllPage.vue';
|
||||
import CategoryListPage from '@/views/mobile/categories/ListPage.vue';
|
||||
import CategoryEditPage from '@/views/mobile/categories/EditPage.vue';
|
||||
import CategoryPresetPage from '@/views/mobile/categories/PresetPage.vue';
|
||||
|
||||
import TagListPage from '../views/mobile/tags/ListPage.vue';
|
||||
import TagListPage from '@/views/mobile/tags/ListPage.vue';
|
||||
|
||||
function asyncResolve(component) {
|
||||
return function({ resolve }) {
|
||||
|
||||
@@ -1,363 +0,0 @@
|
||||
import accountConstants from '../consts/account.js';
|
||||
import services from '../lib/services.js';
|
||||
import logger from '../lib/logger.js';
|
||||
|
||||
import {
|
||||
LOAD_ACCOUNT_LIST,
|
||||
ADD_ACCOUNT_TO_ACCOUNT_LIST,
|
||||
SAVE_ACCOUNT_IN_ACCOUNT_LIST,
|
||||
CHANGE_ACCOUNT_DISPLAY_ORDER_IN_ACCOUNT_LIST,
|
||||
UPDATE_ACCOUNT_VISIBILITY_IN_ACCOUNT_LIST,
|
||||
REMOVE_ACCOUNT_FROM_ACCOUNT_LIST,
|
||||
UPDATE_ACCOUNT_LIST_INVALID_STATE
|
||||
} from './mutations.js';
|
||||
|
||||
export function loadAllAccounts(context, { force }) {
|
||||
if (!force && !context.state.accountListStateInvalid) {
|
||||
return new Promise((resolve) => {
|
||||
resolve(context.state.allAccounts);
|
||||
});
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getAllAccounts({
|
||||
visibleOnly: false
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to get account list' });
|
||||
return;
|
||||
}
|
||||
|
||||
context.commit(LOAD_ACCOUNT_LIST, data.result);
|
||||
|
||||
if (context.state.accountListStateInvalid) {
|
||||
context.commit(UPDATE_ACCOUNT_LIST_INVALID_STATE, false);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
if (force) {
|
||||
logger.error('failed to force load account list', error);
|
||||
} else {
|
||||
logger.error('failed to load account list', error);
|
||||
}
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to get account list' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function getAccount(context, { accountId }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getAccount({
|
||||
id: accountId
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to get account' });
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to load account info', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to get account' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function saveAccount(context, { account }) {
|
||||
const oldAccount = account.id ? context.state.allAccountsMap[account.id] : null;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let promise = null;
|
||||
|
||||
if (!account.id) {
|
||||
promise = services.addAccount(account);
|
||||
} else {
|
||||
promise = services.modifyAccount(account);
|
||||
}
|
||||
|
||||
promise.then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
if (!account.id) {
|
||||
reject({ message: 'Unable to add account' });
|
||||
} else {
|
||||
reject({ message: 'Unable to save account' });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!account.id) {
|
||||
context.commit(ADD_ACCOUNT_TO_ACCOUNT_LIST, data.result);
|
||||
} else {
|
||||
if (oldAccount && oldAccount.category === data.result.category) {
|
||||
context.commit(SAVE_ACCOUNT_IN_ACCOUNT_LIST, data.result);
|
||||
} else {
|
||||
context.commit(UPDATE_ACCOUNT_LIST_INVALID_STATE, true);
|
||||
}
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to save account', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
if (!account.id) {
|
||||
reject({ message: 'Unable to add account' });
|
||||
} else {
|
||||
reject({ message: 'Unable to save account' });
|
||||
}
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function changeAccountDisplayOrder(context, { accountId, from, to }) {
|
||||
const account = context.state.allAccountsMap[accountId];
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!account ||
|
||||
!context.state.allCategorizedAccounts[account.category] ||
|
||||
!context.state.allCategorizedAccounts[account.category].accounts ||
|
||||
!context.state.allCategorizedAccounts[account.category].accounts[to]) {
|
||||
reject({ message: 'Unable to move account' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!context.state.accountListStateInvalid) {
|
||||
context.commit(UPDATE_ACCOUNT_LIST_INVALID_STATE, true);
|
||||
}
|
||||
|
||||
context.commit(CHANGE_ACCOUNT_DISPLAY_ORDER_IN_ACCOUNT_LIST, {
|
||||
account: account,
|
||||
from: from,
|
||||
to: to
|
||||
});
|
||||
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
export function updateAccountDisplayOrders(context) {
|
||||
const newDisplayOrders = [];
|
||||
|
||||
for (let category in context.state.allCategorizedAccounts) {
|
||||
if (!Object.prototype.hasOwnProperty.call(context.state.allCategorizedAccounts, category)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const accountList = context.state.allCategorizedAccounts[category].accounts;
|
||||
|
||||
for (let i = 0; i < accountList.length; i++) {
|
||||
newDisplayOrders.push({
|
||||
id: accountList[i].id,
|
||||
displayOrder: i + 1
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.moveAccount({
|
||||
newDisplayOrders: newDisplayOrders
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to move account' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.state.accountListStateInvalid) {
|
||||
context.commit(UPDATE_ACCOUNT_LIST_INVALID_STATE, false);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to save accounts display order', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to move account' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function hideAccount(context, { account, hidden }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.hideAccount({
|
||||
id: account.id,
|
||||
hidden: hidden
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
if (hidden) {
|
||||
reject({ message: 'Unable to hide this account' });
|
||||
} else {
|
||||
reject({ message: 'Unable to unhide this account' });
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
context.commit(UPDATE_ACCOUNT_VISIBILITY_IN_ACCOUNT_LIST, {
|
||||
account: account,
|
||||
hidden: hidden
|
||||
});
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to change account visibility', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
if (hidden) {
|
||||
reject({ message: 'Unable to hide this account' });
|
||||
} else {
|
||||
reject({ message: 'Unable to unhide this account' });
|
||||
}
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteAccount(context, { account, beforeResolve }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.deleteAccount({
|
||||
id: account.id
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to delete this account' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (beforeResolve) {
|
||||
beforeResolve(() => {
|
||||
context.commit(REMOVE_ACCOUNT_FROM_ACCOUNT_LIST, account);
|
||||
});
|
||||
} else {
|
||||
context.commit(REMOVE_ACCOUNT_FROM_ACCOUNT_LIST, account);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to delete account', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to delete this account' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function allPlainAccounts(state) {
|
||||
const allAccounts = [];
|
||||
|
||||
for (let i = 0; i < state.allAccounts.length; i++) {
|
||||
const account = state.allAccounts[i];
|
||||
|
||||
if (account.type === accountConstants.allAccountTypes.SingleAccount) {
|
||||
allAccounts.push(account);
|
||||
} else if (account.type === accountConstants.allAccountTypes.MultiSubAccounts) {
|
||||
for (let j = 0; j < account.subAccounts.length; j++) {
|
||||
const subAccount = account.subAccounts[j];
|
||||
allAccounts.push(subAccount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allAccounts;
|
||||
}
|
||||
|
||||
export function allVisiblePlainAccounts(state) {
|
||||
const allVisibleAccounts = [];
|
||||
|
||||
for (let i = 0; i < state.allAccounts.length; i++) {
|
||||
const account = state.allAccounts[i];
|
||||
|
||||
if (account.hidden) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (account.type === accountConstants.allAccountTypes.SingleAccount) {
|
||||
allVisibleAccounts.push(account);
|
||||
} else if (account.type === accountConstants.allAccountTypes.MultiSubAccounts) {
|
||||
for (let j = 0; j < account.subAccounts.length; j++) {
|
||||
const subAccount = account.subAccounts[j];
|
||||
allVisibleAccounts.push(subAccount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allVisibleAccounts;
|
||||
}
|
||||
|
||||
export function allAvailableAccountsCount(state) {
|
||||
let allAccountCount = 0;
|
||||
|
||||
for (let category in state.allCategorizedAccounts) {
|
||||
if (!Object.prototype.hasOwnProperty.call(state.allCategorizedAccounts, category)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
allAccountCount += state.allCategorizedAccounts[category].accounts.length;
|
||||
}
|
||||
|
||||
return allAccountCount;
|
||||
}
|
||||
|
||||
export function allVisibleAccountsCount(state) {
|
||||
let shownAccountCount = 0;
|
||||
|
||||
for (let category in state.allCategorizedAccounts) {
|
||||
if (!Object.prototype.hasOwnProperty.call(state.allCategorizedAccounts, category)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const accountList = state.allCategorizedAccounts[category].accounts;
|
||||
|
||||
for (let i = 0; i < accountList.length; i++) {
|
||||
if (!accountList[i].hidden) {
|
||||
shownAccountCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return shownAccountCount;
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
import services from '../lib/services.js';
|
||||
import logger from '../lib/logger.js';
|
||||
import utilities from '../lib/utilities/index.js';
|
||||
|
||||
import {
|
||||
STORE_LATEST_EXCHANGE_RATES
|
||||
} from './mutations.js';
|
||||
|
||||
const exchangeRatesLocalStorageKey = 'ebk_app_exchange_rates';
|
||||
|
||||
export function getLatestExchangeRates(context, { silent, force }) {
|
||||
const currentExchangeRateData = context.state.latestExchangeRates;
|
||||
const now = utilities.getCurrentUnixTime();
|
||||
|
||||
if (!force) {
|
||||
if (currentExchangeRateData && currentExchangeRateData.time && currentExchangeRateData.data &&
|
||||
utilities.formatUnixTime(currentExchangeRateData.data.updateTime, 'YYYY-MM-DD') === utilities.formatUnixTime(now, 'YYYY-MM-DD')) {
|
||||
return currentExchangeRateData.data;
|
||||
}
|
||||
|
||||
if (currentExchangeRateData && currentExchangeRateData.time && currentExchangeRateData.data &&
|
||||
utilities.formatUnixTime(currentExchangeRateData.time, 'YYYY-MM-DD HH') === utilities.formatUnixTime(now, 'YYYY-MM-DD HH')) {
|
||||
return currentExchangeRateData.data;
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getLatestExchangeRates({
|
||||
ignoreError: silent
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to get exchange rates data' });
|
||||
return;
|
||||
}
|
||||
|
||||
const currentData = getExchangeRatesFromLocalStorage();
|
||||
|
||||
if (currentData && currentData.data && utilities.isEquals(currentData.data, data.result)) {
|
||||
reject({ message: 'Exchange rates data is up to date' });
|
||||
return;
|
||||
}
|
||||
|
||||
context.commit(STORE_LATEST_EXCHANGE_RATES, {
|
||||
time: now,
|
||||
data: data.result
|
||||
});
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to get latest exchange rates data', error);
|
||||
|
||||
if (error && error.processed) {
|
||||
reject(error);
|
||||
} else if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else {
|
||||
reject({ message: 'Unable to get exchange rates data' });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function exchangeRatesLastUpdateTime(state) {
|
||||
const exchangeRates = state.latestExchangeRates || {};
|
||||
return exchangeRates && exchangeRates.data ? exchangeRates.data.updateTime : null;
|
||||
}
|
||||
|
||||
export function getExchangedAmount(state) {
|
||||
return (amount, fromCurrency, toCurrency) => {
|
||||
if (!state.latestExchangeRates || !state.latestExchangeRates.data || !state.latestExchangeRates.data.exchangeRates) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const exchangeRates = state.latestExchangeRates.data.exchangeRates;
|
||||
const exchangeRateMap = {};
|
||||
|
||||
for (let i = 0; i < exchangeRates.length; i++) {
|
||||
const exchangeRate = exchangeRates[i];
|
||||
exchangeRateMap[exchangeRate.currency] = exchangeRate;
|
||||
}
|
||||
|
||||
const fromCurrencyExchangeRate = exchangeRateMap[fromCurrency];
|
||||
const toCurrencyExchangeRate = exchangeRateMap[toCurrency];
|
||||
|
||||
if (!fromCurrencyExchangeRate || !toCurrencyExchangeRate) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return utilities.getExchangedAmount(amount, fromCurrencyExchangeRate.rate, toCurrencyExchangeRate.rate)
|
||||
};
|
||||
}
|
||||
|
||||
export function getExchangeRatesFromLocalStorage() {
|
||||
const storageData = localStorage.getItem(exchangeRatesLocalStorageKey) || '{}';
|
||||
return JSON.parse(storageData);
|
||||
}
|
||||
|
||||
export function setExchangeRatesToLocalStorage(value) {
|
||||
const storageData = JSON.stringify(value);
|
||||
localStorage.setItem(exchangeRatesLocalStorageKey, storageData);
|
||||
}
|
||||
|
||||
export function clearExchangeRatesFromLocalStorage() {
|
||||
localStorage.removeItem(exchangeRatesLocalStorageKey);
|
||||
}
|
||||
-1033
File diff suppressed because it is too large
Load Diff
@@ -1,48 +0,0 @@
|
||||
export const RESET_STATE = 'RESET_STATE';
|
||||
|
||||
export const UPDATE_DEFAULT_SETTING = 'UPDATE_DEFAULT_SETTING';
|
||||
|
||||
export const STORE_USER_INFO = 'STORE_USER_INFO';
|
||||
export const CLEAR_USER_INFO = 'CLEAR_USER_INFO';
|
||||
|
||||
export const STORE_LATEST_EXCHANGE_RATES = 'STORE_LATEST_EXCHANGE_RATES';
|
||||
|
||||
export const LOAD_ACCOUNT_LIST = 'LOAD_ACCOUNT_LIST';
|
||||
export const ADD_ACCOUNT_TO_ACCOUNT_LIST = 'ADD_ACCOUNT_TO_ACCOUNT_LIST';
|
||||
export const SAVE_ACCOUNT_IN_ACCOUNT_LIST = 'SAVE_ACCOUNT_IN_ACCOUNT_LIST';
|
||||
export const CHANGE_ACCOUNT_DISPLAY_ORDER_IN_ACCOUNT_LIST = 'CHANGE_ACCOUNT_DISPLAY_ORDER_IN_ACCOUNT_LIST';
|
||||
export const UPDATE_ACCOUNT_VISIBILITY_IN_ACCOUNT_LIST = 'UPDATE_ACCOUNT_VISIBILITY_IN_ACCOUNT_LIST';
|
||||
export const REMOVE_ACCOUNT_FROM_ACCOUNT_LIST = 'REMOVE_ACCOUNT_FROM_ACCOUNT_LIST';
|
||||
export const UPDATE_ACCOUNT_LIST_INVALID_STATE = 'UPDATE_ACCOUNT_LIST_INVALID_STATE';
|
||||
|
||||
export const LOAD_TRANSACTION_LIST = 'LOAD_TRANSACTION_LIST';
|
||||
export const INIT_TRANSACTION_LIST_FILTER = 'INIT_TRANSACTION_LIST_FILTER';
|
||||
export const UPDATE_TRANSACTION_LIST_FILTER = 'UPDATE_TRANSACTION_LIST_FILTER';
|
||||
export const COLLAPSE_MONTH_IN_TRANSACTION_LIST = 'COLLAPSE_MONTH_IN_TRANSACTION_LIST';
|
||||
export const SAVE_TRANSACTION_IN_TRANSACTION_LIST = 'SAVE_TRANSACTION_IN_TRANSACTION_LIST';
|
||||
export const REMOVE_TRANSACTION_FROM_TRANSACTION_LIST = 'REMOVE_TRANSACTION_FROM_TRANSACTION_LIST';
|
||||
export const UPDATE_TRANSACTION_LIST_INVALID_STATE = 'UPDATE_TRANSACTION_LIST_INVALID_STATE';
|
||||
|
||||
export const LOAD_TRANSACTION_CATEGORY_LIST = 'LOAD_TRANSACTION_CATEGORY_LIST';
|
||||
export const ADD_CATEGORY_TO_TRANSACTION_CATEGORY_LIST = 'ADD_CATEGORY_TO_TRANSACTION_CATEGORY_LIST';
|
||||
export const SAVE_CATEGORY_IN_TRANSACTION_CATEGORY_LIST = 'SAVE_CATEGORY_IN_TRANSACTION_CATEGORY_LIST';
|
||||
export const CHANGE_CATEGORY_DISPLAY_ORDER_IN_CATEGORY_LIST = 'CHANGE_CATEGORY_DISPLAY_ORDER_IN_CATEGORY_LIST';
|
||||
export const UPDATE_CATEGORY_VISIBILITY_IN_TRANSACTION_CATEGORY_LIST = 'UPDATE_CATEGORY_VISIBILITY_IN_TRANSACTION_CATEGORY_LIST';
|
||||
export const REMOVE_CATEGORY_FROM_TRANSACTION_CATEGORYLIST = 'REMOVE_CATEGORY_FROM_TRANSACTION_CATEGORYLIST';
|
||||
export const UPDATE_TRANSACTION_CATEGORY_LIST_INVALID_STATE = 'UPDATE_TRANSACTION_CATEGORY_LIST_INVALID_STATE';
|
||||
|
||||
export const LOAD_TRANSACTION_TAG_LIST = 'LOAD_TRANSACTION_TAG_LIST';
|
||||
export const ADD_TAG_TO_TRANSACTION_TAG_LIST = 'ADD_TAG_TO_TRANSACTION_TAG_LIST';
|
||||
export const SAVE_TAG_IN_TRANSACTION_TAG_LIST = 'SAVE_TAG_IN_TRANSACTION_TAG_LIST';
|
||||
export const CHANGE_TAG_DISPLAY_ORDER_IN_TRANSACTION_TAG_LIST = 'CHANGE_TAG_DISPLAY_ORDER_IN_TRANSACTION_TAG_LIST';
|
||||
export const UPDATE_TAG_VISIBILITY_IN_TRANSACTION_TAG_LIST = 'UPDATE_TAG_VISIBILITY_IN_TRANSACTION_TAG_LIST';
|
||||
export const REMOVE_TAG_FROM_TRANSACTION_TAG_LIST = 'REMOVE_TAG_FROM_TRANSACTION_TAG_LIST';
|
||||
export const UPDATE_TRANSACTION_TAG_LIST_INVALID_STATE = 'UPDATE_TRANSACTION_TAG_LIST_INVALID_STATE';
|
||||
|
||||
export const LOAD_TRANSACTION_OVERVIEW = 'LOAD_TRANSACTION_OVERVIEW';
|
||||
export const UPDATE_TRANSACTION_OVERVIEW_INVALID_STATE = 'UPDATE_TRANSACTION_OVERVIEW_INVALID_STATE';
|
||||
|
||||
export const LOAD_TRANSACTION_STATISTICS = 'LOAD_TRANSACTION_STATISTICS';
|
||||
export const INIT_TRANSACTION_STATISTICS_FILTER = 'INIT_TRANSACTION_STATISTICS_FILTER';
|
||||
export const UPDATE_TRANSACTION_STATISTICS_FILTER = 'UPDATE_TRANSACTION_STATISTICS_FILTER';
|
||||
export const UPDATE_TRANSACTION_STATISTICS_INVALID_STATE = 'UPDATE_TRANSACTION_STATISTICS_INVALID_STATE';
|
||||
@@ -1,104 +0,0 @@
|
||||
import services from '../lib/services.js';
|
||||
import logger from '../lib/logger.js';
|
||||
import utilities from '../lib/utilities/index.js';
|
||||
|
||||
import { getExchangedAmount } from './exchangeRates.js';
|
||||
|
||||
import {
|
||||
LOAD_TRANSACTION_OVERVIEW,
|
||||
UPDATE_TRANSACTION_OVERVIEW_INVALID_STATE,
|
||||
} from './mutations.js';
|
||||
|
||||
export function loadTransactionOverview(context, { defaultCurrency, dateRange, force }) {
|
||||
if (!force && !context.state.transactionOverviewStateInvalid) {
|
||||
return new Promise((resolve) => {
|
||||
resolve(context.state.transactionOverview);
|
||||
});
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getTransactionAmounts({
|
||||
today: dateRange.today,
|
||||
thisWeek: dateRange.thisWeek,
|
||||
thisMonth: dateRange.thisMonth,
|
||||
thisYear: dateRange.thisYear
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to get transaction overview' });
|
||||
return;
|
||||
}
|
||||
|
||||
const overview = data.result;
|
||||
|
||||
for (let field in overview) {
|
||||
if (!Object.prototype.hasOwnProperty.call(overview, field)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const item = overview[field];
|
||||
|
||||
if (!item.amounts || !item.amounts.length) {
|
||||
item.amounts = [];
|
||||
}
|
||||
|
||||
let totalIncomeAmount = 0;
|
||||
let totalExpenseAmount = 0;
|
||||
let hasUnCalculatedTotalIncome = false;
|
||||
let hasUnCalculatedTotalExpense = false;
|
||||
|
||||
for (let i = 0; i < item.amounts.length; i++) {
|
||||
const amount = item.amounts[i];
|
||||
|
||||
if (amount.currency !== defaultCurrency) {
|
||||
const incomeAmount = getExchangedAmount(context.state)(amount.incomeAmount, amount.currency, defaultCurrency);
|
||||
const expenseAmount = getExchangedAmount(context.state)(amount.expenseAmount, amount.currency, defaultCurrency);
|
||||
|
||||
if (utilities.isNumber(incomeAmount)) {
|
||||
totalIncomeAmount += Math.floor(incomeAmount);
|
||||
} else {
|
||||
hasUnCalculatedTotalIncome = true;
|
||||
}
|
||||
|
||||
if (utilities.isNumber(expenseAmount)) {
|
||||
totalExpenseAmount += Math.floor(expenseAmount);
|
||||
} else {
|
||||
hasUnCalculatedTotalExpense = true;
|
||||
}
|
||||
} else {
|
||||
totalIncomeAmount += amount.incomeAmount;
|
||||
totalExpenseAmount += amount.expenseAmount;
|
||||
}
|
||||
}
|
||||
|
||||
item.incomeAmount = totalIncomeAmount;
|
||||
item.expenseAmount = totalExpenseAmount;
|
||||
item.incompleteIncomeAmount = hasUnCalculatedTotalIncome;
|
||||
item.incompleteExpenseAmount = hasUnCalculatedTotalExpense;
|
||||
}
|
||||
|
||||
context.commit(LOAD_TRANSACTION_OVERVIEW, overview);
|
||||
|
||||
if (context.state.transactionOverviewStateInvalid) {
|
||||
context.commit(UPDATE_TRANSACTION_OVERVIEW_INVALID_STATE, false);
|
||||
}
|
||||
|
||||
resolve(overview);
|
||||
}).catch(error => {
|
||||
if (force) {
|
||||
logger.error('failed to force load transaction overview', error);
|
||||
} else {
|
||||
logger.error('failed to load transaction overview', error);
|
||||
}
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to get transaction overview' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import {
|
||||
UPDATE_DEFAULT_SETTING
|
||||
} from './mutations.js';
|
||||
|
||||
export function updateLocalizedDefaultSettings(context, { defaultCurrency, defaultFirstDayOfWeek }) {
|
||||
context.commit(UPDATE_DEFAULT_SETTING, {
|
||||
defaultCurrency,
|
||||
defaultFirstDayOfWeek,
|
||||
});
|
||||
}
|
||||
@@ -1,268 +0,0 @@
|
||||
import statisticsConstants from '../consts/statistics.js';
|
||||
import categoryConstants from '../consts/category.js';
|
||||
import iconConstants from '../consts/icon.js';
|
||||
import colorConstants from '../consts/color.js';
|
||||
import services from '../lib/services.js';
|
||||
import logger from '../lib/logger.js';
|
||||
import utilities from '../lib/utilities/index.js';
|
||||
|
||||
import { getExchangedAmount } from './exchangeRates.js';
|
||||
|
||||
import {
|
||||
LOAD_TRANSACTION_STATISTICS,
|
||||
INIT_TRANSACTION_STATISTICS_FILTER,
|
||||
UPDATE_TRANSACTION_STATISTICS_FILTER,
|
||||
UPDATE_TRANSACTION_STATISTICS_INVALID_STATE
|
||||
} from './mutations.js';
|
||||
|
||||
export function loadTransactionStatistics(context, { defaultCurrency }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getTransactionStatistics({
|
||||
startTime: context.state.transactionStatisticsFilter.startTime,
|
||||
endTime: context.state.transactionStatisticsFilter.endTime
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to get transaction statistics' });
|
||||
return;
|
||||
}
|
||||
|
||||
context.commit(LOAD_TRANSACTION_STATISTICS, {
|
||||
statistics: data.result,
|
||||
defaultCurrency: defaultCurrency
|
||||
});
|
||||
|
||||
if (context.state.transactionStatisticsStateInvalid) {
|
||||
context.commit(UPDATE_TRANSACTION_STATISTICS_INVALID_STATE, false);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to get transaction statistics', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to get transaction statistics' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function initTransactionStatisticsFilter(context, filter) {
|
||||
context.commit(INIT_TRANSACTION_STATISTICS_FILTER, filter);
|
||||
}
|
||||
|
||||
export function updateTransactionStatisticsFilter(context, filter) {
|
||||
context.commit(UPDATE_TRANSACTION_STATISTICS_FILTER, filter);
|
||||
}
|
||||
|
||||
export function statisticsItemsByTransactionStatisticsData(state) {
|
||||
if (!state.transactionStatistics || !state.transactionStatistics.items) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const allDataItems = {};
|
||||
let totalAmount = 0;
|
||||
let totalNonNegativeAmount = 0;
|
||||
|
||||
for (let i = 0; i < state.transactionStatistics.items.length; i++) {
|
||||
const item = state.transactionStatistics.items[i];
|
||||
|
||||
if (!item.primaryAccount || !item.account || !item.primaryCategory || !item.category) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (state.transactionStatisticsFilter.chartDataType === statisticsConstants.allChartDataTypes.ExpenseByAccount.type ||
|
||||
state.transactionStatisticsFilter.chartDataType === statisticsConstants.allChartDataTypes.ExpenseByPrimaryCategory.type ||
|
||||
state.transactionStatisticsFilter.chartDataType === statisticsConstants.allChartDataTypes.ExpenseBySecondaryCategory.type) {
|
||||
if (item.category.type !== categoryConstants.allCategoryTypes.Expense) {
|
||||
continue;
|
||||
}
|
||||
} else if (state.transactionStatisticsFilter.chartDataType === statisticsConstants.allChartDataTypes.IncomeByAccount.type ||
|
||||
state.transactionStatisticsFilter.chartDataType === statisticsConstants.allChartDataTypes.IncomeByPrimaryCategory.type ||
|
||||
state.transactionStatisticsFilter.chartDataType === statisticsConstants.allChartDataTypes.IncomeBySecondaryCategory.type) {
|
||||
if (item.category.type !== categoryConstants.allCategoryTypes.Income) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (state.transactionStatisticsFilter.filterAccountIds && state.transactionStatisticsFilter.filterAccountIds[item.account.id]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (state.transactionStatisticsFilter.filterCategoryIds && state.transactionStatisticsFilter.filterCategoryIds[item.category.id]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (state.transactionStatisticsFilter.chartDataType === statisticsConstants.allChartDataTypes.ExpenseByAccount.type ||
|
||||
state.transactionStatisticsFilter.chartDataType === statisticsConstants.allChartDataTypes.IncomeByAccount.type) {
|
||||
if (utilities.isNumber(item.amountInDefaultCurrency)) {
|
||||
let data = allDataItems[item.account.id];
|
||||
|
||||
if (data) {
|
||||
data.totalAmount += item.amountInDefaultCurrency;
|
||||
} else {
|
||||
data = {
|
||||
name: item.account.name,
|
||||
type: 'account',
|
||||
id: item.account.id,
|
||||
icon: item.account.icon || iconConstants.defaultAccountIcon.icon,
|
||||
color: item.account.color || colorConstants.defaultAccountColor,
|
||||
hidden: item.primaryAccount.hidden || item.account.hidden,
|
||||
displayOrders: [item.primaryAccount.category, item.primaryAccount.displayOrder, item.account.displayOrder],
|
||||
totalAmount: item.amountInDefaultCurrency
|
||||
}
|
||||
}
|
||||
|
||||
totalAmount += item.amountInDefaultCurrency;
|
||||
|
||||
if (item.amountInDefaultCurrency > 0) {
|
||||
totalNonNegativeAmount += item.amountInDefaultCurrency;
|
||||
}
|
||||
|
||||
allDataItems[item.account.id] = data;
|
||||
}
|
||||
} else if (state.transactionStatisticsFilter.chartDataType === statisticsConstants.allChartDataTypes.ExpenseByPrimaryCategory.type ||
|
||||
state.transactionStatisticsFilter.chartDataType === statisticsConstants.allChartDataTypes.IncomeByPrimaryCategory.type) {
|
||||
if (utilities.isNumber(item.amountInDefaultCurrency)) {
|
||||
let data = allDataItems[item.primaryCategory.id];
|
||||
|
||||
if (data) {
|
||||
data.totalAmount += item.amountInDefaultCurrency;
|
||||
} else {
|
||||
data = {
|
||||
name: item.primaryCategory.name,
|
||||
type: 'category',
|
||||
id: item.primaryCategory.id,
|
||||
icon: item.primaryCategory.icon || iconConstants.defaultCategoryIcon.icon,
|
||||
color: item.primaryCategory.color || colorConstants.defaultCategoryColor,
|
||||
hidden: item.primaryCategory.hidden,
|
||||
displayOrders: [item.primaryCategory.type, item.primaryCategory.displayOrder],
|
||||
totalAmount: item.amountInDefaultCurrency
|
||||
}
|
||||
}
|
||||
|
||||
totalAmount += item.amountInDefaultCurrency;
|
||||
|
||||
if (item.amountInDefaultCurrency > 0) {
|
||||
totalNonNegativeAmount += item.amountInDefaultCurrency;
|
||||
}
|
||||
|
||||
allDataItems[item.primaryCategory.id] = data;
|
||||
}
|
||||
} else if (state.transactionStatisticsFilter.chartDataType === statisticsConstants.allChartDataTypes.ExpenseBySecondaryCategory.type ||
|
||||
state.transactionStatisticsFilter.chartDataType === statisticsConstants.allChartDataTypes.IncomeBySecondaryCategory.type) {
|
||||
if (utilities.isNumber(item.amountInDefaultCurrency)) {
|
||||
let data = allDataItems[item.category.id];
|
||||
|
||||
if (data) {
|
||||
data.totalAmount += item.amountInDefaultCurrency;
|
||||
} else {
|
||||
data = {
|
||||
name: item.category.name,
|
||||
type: 'category',
|
||||
id: item.category.id,
|
||||
icon: item.category.icon || iconConstants.defaultCategoryIcon.icon,
|
||||
color: item.category.color || colorConstants.defaultCategoryColor,
|
||||
hidden: item.primaryCategory.hidden || item.category.hidden,
|
||||
displayOrders: [item.primaryCategory.type, item.primaryCategory.displayOrder, item.category.displayOrder],
|
||||
totalAmount: item.amountInDefaultCurrency
|
||||
}
|
||||
}
|
||||
|
||||
totalAmount += item.amountInDefaultCurrency;
|
||||
|
||||
if (item.amountInDefaultCurrency > 0) {
|
||||
totalNonNegativeAmount += item.amountInDefaultCurrency;
|
||||
}
|
||||
|
||||
allDataItems[item.category.id] = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
totalAmount: totalAmount,
|
||||
totalNonNegativeAmount: totalNonNegativeAmount,
|
||||
items: allDataItems
|
||||
}
|
||||
}
|
||||
|
||||
export function statisticsItemsByAccountsData(state, getters) {
|
||||
if (!getters.allPlainAccounts) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const allDataItems = {};
|
||||
let totalAmount = 0;
|
||||
let totalNonNegativeAmount = 0;
|
||||
|
||||
for (let i = 0; i < getters.allPlainAccounts.length; i++) {
|
||||
const account = getters.allPlainAccounts[i];
|
||||
|
||||
if (state.transactionStatisticsFilter.chartDataType === statisticsConstants.allChartDataTypes.AccountTotalAssets.type) {
|
||||
if (!account.isAsset) {
|
||||
continue;
|
||||
}
|
||||
} else if (state.transactionStatisticsFilter.chartDataType === statisticsConstants.allChartDataTypes.AccountTotalLiabilities.type) {
|
||||
if (!account.isLiability) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (state.transactionStatisticsFilter.filterAccountIds && state.transactionStatisticsFilter.filterAccountIds[account.id]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let primaryAccount = state.allAccountsMap[account.parentId];
|
||||
|
||||
if (!primaryAccount) {
|
||||
primaryAccount = account;
|
||||
}
|
||||
|
||||
let amount = account.balance;
|
||||
|
||||
if (account.currency !== getters.currentUserDefaultCurrency) {
|
||||
amount = Math.floor(getExchangedAmount(state)(amount, account.currency, getters.currentUserDefaultCurrency));
|
||||
|
||||
if (!utilities.isNumber(amount)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (account.isLiability) {
|
||||
amount = -amount;
|
||||
}
|
||||
|
||||
const data = {
|
||||
name: account.name,
|
||||
type: 'account',
|
||||
id: account.id,
|
||||
icon: account.icon || iconConstants.defaultAccountIcon.icon,
|
||||
color: account.color || colorConstants.defaultAccountColor,
|
||||
hidden: primaryAccount.hidden || account.hidden,
|
||||
displayOrders: [primaryAccount.category, primaryAccount.displayOrder, account.displayOrder],
|
||||
totalAmount: amount
|
||||
};
|
||||
|
||||
totalAmount += amount;
|
||||
|
||||
if (amount > 0) {
|
||||
totalNonNegativeAmount += amount;
|
||||
}
|
||||
|
||||
allDataItems[account.id] = data;
|
||||
}
|
||||
|
||||
return {
|
||||
totalAmount: totalAmount,
|
||||
totalNonNegativeAmount: totalNonNegativeAmount,
|
||||
items: allDataItems
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
import userState from '../lib/userstate.js';
|
||||
import services from '../lib/services.js';
|
||||
import logger from '../lib/logger.js';
|
||||
import utilities from '../lib/utilities/index.js';
|
||||
|
||||
import {
|
||||
STORE_USER_INFO
|
||||
} from './mutations.js';
|
||||
|
||||
export function getAllTokens() {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getTokens().then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to get session list' });
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to load token list', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to get session list' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function refreshTokenAndRevokeOldToken(context) {
|
||||
return new Promise((resolve) => {
|
||||
services.refreshToken().then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (data && data.success && data.result && data.result.newToken) {
|
||||
userState.updateToken(data.result.newToken);
|
||||
|
||||
if (data.result.user && utilities.isObject(data.result.user)) {
|
||||
context.commit(STORE_USER_INFO, data.result.user);
|
||||
}
|
||||
|
||||
if (data.result.oldTokenId) {
|
||||
revokeToken(context, {
|
||||
tokenId: data.result.oldTokenId,
|
||||
ignoreError: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function revokeToken(context, { tokenId, ignoreError }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.revokeToken({
|
||||
tokenId: tokenId,
|
||||
ignoreError: !!ignoreError
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to logout from this session' });
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to revoke token', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to logout from this session' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function revokeAllTokens() {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.revokeAllTokens().then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to logout all other sessions' });
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to revoke all tokens', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to logout all other sessions' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,374 +0,0 @@
|
||||
import transactionConstants from '../consts/transaction.js';
|
||||
import services from '../lib/services.js';
|
||||
import logger from '../lib/logger.js';
|
||||
import utilities from '../lib/utilities/index.js';
|
||||
|
||||
import { getExchangedAmount } from './exchangeRates.js';
|
||||
|
||||
import {
|
||||
LOAD_TRANSACTION_LIST,
|
||||
INIT_TRANSACTION_LIST_FILTER,
|
||||
UPDATE_TRANSACTION_LIST_FILTER,
|
||||
COLLAPSE_MONTH_IN_TRANSACTION_LIST,
|
||||
SAVE_TRANSACTION_IN_TRANSACTION_LIST,
|
||||
REMOVE_TRANSACTION_FROM_TRANSACTION_LIST,
|
||||
UPDATE_TRANSACTION_LIST_INVALID_STATE,
|
||||
UPDATE_ACCOUNT_LIST_INVALID_STATE,
|
||||
UPDATE_TRANSACTION_OVERVIEW_INVALID_STATE,
|
||||
UPDATE_TRANSACTION_STATISTICS_INVALID_STATE,
|
||||
} from './mutations.js';
|
||||
|
||||
const emptyTransactionResult = {
|
||||
items: [],
|
||||
transactionsNextTimeId: 0
|
||||
};
|
||||
|
||||
export function initTransactionListFilter(context, filter) {
|
||||
context.commit(INIT_TRANSACTION_LIST_FILTER, filter);
|
||||
}
|
||||
|
||||
export function updateTransactionListFilter(context, filter) {
|
||||
context.commit(UPDATE_TRANSACTION_LIST_FILTER, filter);
|
||||
}
|
||||
|
||||
export function loadTransactions(context, { reload, autoExpand, defaultCurrency }) {
|
||||
let actualMaxTime = context.state.transactionsNextTimeId;
|
||||
|
||||
if (reload && context.state.transactionsFilter.maxTime > 0) {
|
||||
actualMaxTime = context.state.transactionsFilter.maxTime * 1000 + 999;
|
||||
} else if (reload && context.state.transactionsFilter.maxTime <= 0) {
|
||||
actualMaxTime = 0;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getTransactions({
|
||||
maxTime: actualMaxTime,
|
||||
minTime: context.state.transactionsFilter.minTime * 1000,
|
||||
type: context.state.transactionsFilter.type,
|
||||
categoryId: context.state.transactionsFilter.categoryId,
|
||||
accountId: context.state.transactionsFilter.accountId,
|
||||
keyword: context.state.transactionsFilter.keyword
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
if (reload) {
|
||||
context.commit(LOAD_TRANSACTION_LIST, {
|
||||
transactions: emptyTransactionResult,
|
||||
reload: reload,
|
||||
autoExpand: autoExpand,
|
||||
defaultCurrency: defaultCurrency
|
||||
});
|
||||
|
||||
if (!context.state.transactionListStateInvalid) {
|
||||
context.commit(UPDATE_TRANSACTION_LIST_INVALID_STATE, true);
|
||||
}
|
||||
}
|
||||
|
||||
reject({ message: 'Unable to get transaction list' });
|
||||
return;
|
||||
}
|
||||
|
||||
context.commit(LOAD_TRANSACTION_LIST, {
|
||||
transactions: data.result,
|
||||
reload: reload,
|
||||
autoExpand: autoExpand,
|
||||
defaultCurrency: defaultCurrency
|
||||
});
|
||||
|
||||
if (reload) {
|
||||
if (context.state.transactionListStateInvalid) {
|
||||
context.commit(UPDATE_TRANSACTION_LIST_INVALID_STATE, false);
|
||||
}
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to load transaction list', error);
|
||||
|
||||
if (reload) {
|
||||
context.commit(LOAD_TRANSACTION_LIST, {
|
||||
transactions: emptyTransactionResult,
|
||||
reload: reload,
|
||||
autoExpand: autoExpand,
|
||||
defaultCurrency: defaultCurrency
|
||||
});
|
||||
|
||||
if (!context.state.transactionListStateInvalid) {
|
||||
context.commit(UPDATE_TRANSACTION_LIST_INVALID_STATE, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to get transaction list' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function getTransaction(context, { transactionId }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getTransaction({
|
||||
id: transactionId
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to get transaction' });
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to load transaction info', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to get transaction' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function saveTransaction(context, { transaction, defaultCurrency }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let promise = null;
|
||||
|
||||
if (!transaction.id) {
|
||||
promise = services.addTransaction(transaction);
|
||||
} else {
|
||||
promise = services.modifyTransaction(transaction);
|
||||
}
|
||||
|
||||
promise.then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
if (!transaction.id) {
|
||||
reject({ message: 'Unable to add transaction' });
|
||||
} else {
|
||||
reject({ message: 'Unable to save transaction' });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!transaction.id) {
|
||||
if (!context.state.transactionListStateInvalid) {
|
||||
context.commit(UPDATE_TRANSACTION_LIST_INVALID_STATE, true);
|
||||
}
|
||||
} else {
|
||||
context.commit(SAVE_TRANSACTION_IN_TRANSACTION_LIST, {
|
||||
transaction: data.result,
|
||||
defaultCurrency: defaultCurrency
|
||||
});
|
||||
}
|
||||
|
||||
if (!context.state.accountListStateInvalid) {
|
||||
context.commit(UPDATE_ACCOUNT_LIST_INVALID_STATE, true);
|
||||
}
|
||||
|
||||
if (!context.state.transactionOverviewStateInvalid) {
|
||||
context.commit(UPDATE_TRANSACTION_OVERVIEW_INVALID_STATE, true);
|
||||
}
|
||||
|
||||
if (!context.state.transactionStatisticsStateInvalid) {
|
||||
context.commit(UPDATE_TRANSACTION_STATISTICS_INVALID_STATE, true);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to save transaction', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
if (!transaction.id) {
|
||||
reject({ message: 'Unable to add transaction' });
|
||||
} else {
|
||||
reject({ message: 'Unable to save transaction' });
|
||||
}
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteTransaction(context, { transaction, defaultCurrency, beforeResolve }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.deleteTransaction({
|
||||
id: transaction.id
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to delete this transaction' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (beforeResolve) {
|
||||
beforeResolve(() => {
|
||||
context.commit(REMOVE_TRANSACTION_FROM_TRANSACTION_LIST, {
|
||||
transaction: transaction,
|
||||
defaultCurrency: defaultCurrency
|
||||
});
|
||||
});
|
||||
} else {
|
||||
context.commit(REMOVE_TRANSACTION_FROM_TRANSACTION_LIST, {
|
||||
transaction: transaction,
|
||||
defaultCurrency: defaultCurrency
|
||||
});
|
||||
}
|
||||
|
||||
if (!context.state.accountListStateInvalid) {
|
||||
context.commit(UPDATE_ACCOUNT_LIST_INVALID_STATE, true);
|
||||
}
|
||||
|
||||
if (!context.state.transactionOverviewStateInvalid) {
|
||||
context.commit(UPDATE_TRANSACTION_OVERVIEW_INVALID_STATE, true);
|
||||
}
|
||||
|
||||
if (!context.state.transactionStatisticsStateInvalid) {
|
||||
context.commit(UPDATE_TRANSACTION_STATISTICS_INVALID_STATE, true);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to delete transaction', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to delete this transaction' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function collapseMonthInTransactionList(context, { month, collapse }) {
|
||||
context.commit(COLLAPSE_MONTH_IN_TRANSACTION_LIST, {
|
||||
month: month,
|
||||
collapse: collapse
|
||||
});
|
||||
}
|
||||
|
||||
export function noTransaction(state) {
|
||||
for (let i = 0; i < state.transactions.length; i++) {
|
||||
const transactionMonthList = state.transactions[i];
|
||||
|
||||
for (let j = 0; j < transactionMonthList.items.length; j++) {
|
||||
if (transactionMonthList.items[j]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function hasMoreTransaction(state) {
|
||||
return state.transactionsNextTimeId > 0;
|
||||
}
|
||||
|
||||
export function fillTransactionObject(state, transaction, currentUtcOffset) {
|
||||
if (!transaction) {
|
||||
return;
|
||||
}
|
||||
|
||||
const transactionTime = utilities.parseDateFromUnixTime(transaction.time, transaction.utcOffset, currentUtcOffset);
|
||||
|
||||
transaction.day = utilities.getDay(transactionTime);
|
||||
transaction.dayOfWeek = utilities.getDayOfWeekName(transactionTime);
|
||||
|
||||
if (transaction.sourceAccountId) {
|
||||
transaction.sourceAccount = state.allAccountsMap[transaction.sourceAccountId];
|
||||
}
|
||||
|
||||
if (transaction.destinationAccountId) {
|
||||
transaction.destinationAccount = state.allAccountsMap[transaction.destinationAccountId];
|
||||
}
|
||||
|
||||
if (transaction.categoryId) {
|
||||
transaction.category = state.allTransactionCategoriesMap[transaction.categoryId];
|
||||
}
|
||||
|
||||
return transaction;
|
||||
}
|
||||
|
||||
export function calculateMonthTotalAmount(state, transactionMonthList, defaultCurrency, accountId, incomplete) {
|
||||
if (!transactionMonthList) {
|
||||
return;
|
||||
}
|
||||
|
||||
let totalExpense = 0;
|
||||
let totalIncome = 0;
|
||||
let hasUnCalculatedTotalExpense = false;
|
||||
let hasUnCalculatedTotalIncome = false;
|
||||
|
||||
for (let i = 0; i < transactionMonthList.items.length; i++) {
|
||||
const transaction = transactionMonthList.items[i];
|
||||
|
||||
let amount = transaction.sourceAmount;
|
||||
let account = transaction.sourceAccount;
|
||||
|
||||
if (accountId && transaction.destinationAccount && (transaction.destinationAccount.id === accountId || transaction.destinationAccount.parentId === accountId)) {
|
||||
amount = transaction.destinationAmount;
|
||||
account = transaction.destinationAccount;
|
||||
}
|
||||
|
||||
if (!account) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (account.currency !== defaultCurrency) {
|
||||
const balance = getExchangedAmount(state)(amount, account.currency, defaultCurrency);
|
||||
|
||||
if (!utilities.isNumber(balance)) {
|
||||
if (transaction.type === transactionConstants.allTransactionTypes.Expense) {
|
||||
hasUnCalculatedTotalExpense = true;
|
||||
} else if (transaction.type === transactionConstants.allTransactionTypes.Income) {
|
||||
hasUnCalculatedTotalIncome = true;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
amount = Math.floor(balance);
|
||||
}
|
||||
|
||||
if (transaction.type === transactionConstants.allTransactionTypes.Expense) {
|
||||
totalExpense += amount;
|
||||
} else if (transaction.type === transactionConstants.allTransactionTypes.Income) {
|
||||
totalIncome += amount;
|
||||
} else if (transaction.type === transactionConstants.allTransactionTypes.Transfer && accountId) {
|
||||
if (accountId === transaction.sourceAccountId) {
|
||||
totalExpense += amount;
|
||||
} else if (accountId === transaction.destinationAccountId) {
|
||||
totalIncome += amount;
|
||||
} else if (transaction.sourceAccount && accountId === transaction.sourceAccount.parentId &&
|
||||
transaction.destinationAccount && accountId === transaction.destinationAccount.parentId) {
|
||||
// Do Nothing
|
||||
} else if (transaction.sourceAccount && accountId === transaction.sourceAccount.parentId) {
|
||||
totalExpense += amount;
|
||||
} else if (transaction.destinationAccount && accountId === transaction.destinationAccount.parentId) {
|
||||
totalIncome += amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
transactionMonthList.totalAmount = {
|
||||
expense: totalExpense,
|
||||
incompleteExpense: incomplete || hasUnCalculatedTotalExpense,
|
||||
income: totalIncome,
|
||||
incompleteIncome: incomplete || hasUnCalculatedTotalIncome
|
||||
};
|
||||
}
|
||||
@@ -1,355 +0,0 @@
|
||||
import categoryContants from '../consts/category.js';
|
||||
import services from '../lib/services.js';
|
||||
import logger from '../lib/logger.js';
|
||||
|
||||
import {
|
||||
LOAD_TRANSACTION_CATEGORY_LIST,
|
||||
ADD_CATEGORY_TO_TRANSACTION_CATEGORY_LIST,
|
||||
SAVE_CATEGORY_IN_TRANSACTION_CATEGORY_LIST,
|
||||
CHANGE_CATEGORY_DISPLAY_ORDER_IN_CATEGORY_LIST,
|
||||
UPDATE_CATEGORY_VISIBILITY_IN_TRANSACTION_CATEGORY_LIST,
|
||||
REMOVE_CATEGORY_FROM_TRANSACTION_CATEGORYLIST,
|
||||
UPDATE_TRANSACTION_CATEGORY_LIST_INVALID_STATE,
|
||||
} from './mutations.js';
|
||||
|
||||
export function loadAllCategories(context, { force }) {
|
||||
if (!force && !context.state.transactionCategoryListStateInvalid) {
|
||||
return new Promise((resolve) => {
|
||||
resolve(context.state.allTransactionCategories);
|
||||
});
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getAllTransactionCategories().then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to get category list' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data.result[categoryContants.allCategoryTypes.Income]) {
|
||||
data.result[categoryContants.allCategoryTypes.Income] = [];
|
||||
}
|
||||
|
||||
if (!data.result[categoryContants.allCategoryTypes.Expense]) {
|
||||
data.result[categoryContants.allCategoryTypes.Expense] = [];
|
||||
}
|
||||
|
||||
if (!data.result[categoryContants.allCategoryTypes.Transfer]) {
|
||||
data.result[categoryContants.allCategoryTypes.Transfer] = [];
|
||||
}
|
||||
|
||||
for (let categoryType in data.result) {
|
||||
if (!Object.prototype.hasOwnProperty.call(data.result, categoryType)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const categories = data.result[categoryType];
|
||||
|
||||
for (let i = 0; i < categories.length; i++) {
|
||||
const category = categories[i];
|
||||
|
||||
if (!category.subCategories) {
|
||||
category.subCategories = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.commit(LOAD_TRANSACTION_CATEGORY_LIST, data.result);
|
||||
|
||||
if (context.state.transactionCategoryListStateInvalid) {
|
||||
context.commit(UPDATE_TRANSACTION_CATEGORY_LIST_INVALID_STATE, false);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
if (force) {
|
||||
logger.error('failed to force load category list', error);
|
||||
} else {
|
||||
logger.error('failed to load category list', error);
|
||||
}
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to get category list' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function getCategory(context, { categoryId }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getTransactionCategory({
|
||||
id: categoryId
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to get category' });
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to load category info', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to get category' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function saveCategory(context, { category }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let promise = null;
|
||||
|
||||
if (!category.id) {
|
||||
promise = services.addTransactionCategory(category);
|
||||
} else {
|
||||
promise = services.modifyTransactionCategory(category);
|
||||
}
|
||||
|
||||
promise.then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
if (!category.id) {
|
||||
reject({ message: 'Unable to add category' });
|
||||
} else {
|
||||
reject({ message: 'Unable to save category' });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data.result.subCategories) {
|
||||
data.result.subCategories = [];
|
||||
}
|
||||
|
||||
if (!category.id) {
|
||||
context.commit(ADD_CATEGORY_TO_TRANSACTION_CATEGORY_LIST, data.result);
|
||||
} else {
|
||||
context.commit(SAVE_CATEGORY_IN_TRANSACTION_CATEGORY_LIST, data.result);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to save category', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
if (!category.id) {
|
||||
reject({ message: 'Unable to add category' });
|
||||
} else {
|
||||
reject({ message: 'Unable to save category' });
|
||||
}
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function addCategories(context, { categories }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.addTransactionCategoryBatch({
|
||||
categories: categories
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to add preset categories' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!context.state.transactionCategoryListStateInvalid) {
|
||||
context.commit(UPDATE_TRANSACTION_CATEGORY_LIST_INVALID_STATE, true);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to add preset categories', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to add preset categories' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function changeCategoryDisplayOrder(context, { categoryId, from, to }) {
|
||||
const category = context.state.allTransactionCategoriesMap[categoryId];
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!category) {
|
||||
reject({ message: 'Unable to move category' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!category.parentId || category.parentId === '0') {
|
||||
if (!context.state.allTransactionCategories[category.type] ||
|
||||
!context.state.allTransactionCategories[category.type][to]) {
|
||||
reject({ message: 'Unable to move category' });
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!context.state.allTransactionCategoriesMap[category.parentId].subCategories ||
|
||||
!context.state.allTransactionCategoriesMap[category.parentId].subCategories[to]) {
|
||||
reject({ message: 'Unable to move category' });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!context.state.transactionCategoryListStateInvalid) {
|
||||
context.commit(UPDATE_TRANSACTION_CATEGORY_LIST_INVALID_STATE, true);
|
||||
}
|
||||
|
||||
context.commit(CHANGE_CATEGORY_DISPLAY_ORDER_IN_CATEGORY_LIST, {
|
||||
category: category,
|
||||
from: from,
|
||||
to: to
|
||||
});
|
||||
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
export function updateCategoryDisplayOrders(context, { type, parentId }) {
|
||||
const newDisplayOrders = [];
|
||||
|
||||
let categoryList = null;
|
||||
|
||||
if (!parentId || parentId === '0') {
|
||||
categoryList = context.state.allTransactionCategories[type];
|
||||
} else if (context.state.allTransactionCategoriesMap[parentId]) {
|
||||
categoryList = context.state.allTransactionCategoriesMap[parentId].subCategories;
|
||||
}
|
||||
|
||||
if (categoryList) {
|
||||
for (let i = 0; i < categoryList.length; i++) {
|
||||
newDisplayOrders.push({
|
||||
id: categoryList[i].id,
|
||||
displayOrder: i + 1
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.moveTransactionCategory({
|
||||
newDisplayOrders: newDisplayOrders
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to move category' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.state.transactionCategoryListStateInvalid) {
|
||||
context.commit(UPDATE_TRANSACTION_CATEGORY_LIST_INVALID_STATE, false);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to save categories display order', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to move category' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function hideCategory(context, { category, hidden }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.hideTransactionCategory({
|
||||
id: category.id,
|
||||
hidden: hidden
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
if (hidden) {
|
||||
reject({ message: 'Unable to hide this category' });
|
||||
} else {
|
||||
reject({ message: 'Unable to unhide this category' });
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
context.commit(UPDATE_CATEGORY_VISIBILITY_IN_TRANSACTION_CATEGORY_LIST, {
|
||||
category: category,
|
||||
hidden: hidden
|
||||
});
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to change category visibility', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
if (hidden) {
|
||||
reject({ message: 'Unable to hide this category' });
|
||||
} else {
|
||||
reject({ message: 'Unable to unhide this category' });
|
||||
}
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteCategory(context, { category, beforeResolve }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.deleteTransactionCategory({
|
||||
id: category.id
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to delete this category' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (beforeResolve) {
|
||||
beforeResolve(() => {
|
||||
context.commit(REMOVE_CATEGORY_FROM_TRANSACTION_CATEGORYLIST, category);
|
||||
});
|
||||
} else {
|
||||
context.commit(REMOVE_CATEGORY_FROM_TRANSACTION_CATEGORYLIST, category);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to delete category', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to delete this category' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,247 +0,0 @@
|
||||
import services from '../lib/services.js';
|
||||
import logger from '../lib/logger.js';
|
||||
|
||||
import {
|
||||
LOAD_TRANSACTION_TAG_LIST,
|
||||
ADD_TAG_TO_TRANSACTION_TAG_LIST,
|
||||
SAVE_TAG_IN_TRANSACTION_TAG_LIST,
|
||||
CHANGE_TAG_DISPLAY_ORDER_IN_TRANSACTION_TAG_LIST,
|
||||
UPDATE_TAG_VISIBILITY_IN_TRANSACTION_TAG_LIST,
|
||||
REMOVE_TAG_FROM_TRANSACTION_TAG_LIST,
|
||||
UPDATE_TRANSACTION_TAG_LIST_INVALID_STATE,
|
||||
} from './mutations.js';
|
||||
|
||||
export function loadAllTags(context, { force }) {
|
||||
if (!force && !context.state.transactionTagListStateInvalid) {
|
||||
return new Promise((resolve) => {
|
||||
resolve(context.state.allTransactionTags);
|
||||
});
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getAllTransactionTags().then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to get tag list' });
|
||||
return;
|
||||
}
|
||||
|
||||
context.commit(LOAD_TRANSACTION_TAG_LIST, data.result);
|
||||
|
||||
if (context.state.transactionTagListStateInvalid) {
|
||||
context.commit(UPDATE_TRANSACTION_TAG_LIST_INVALID_STATE, false);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
if (force) {
|
||||
logger.error('failed to force load tag list', error);
|
||||
} else {
|
||||
logger.error('failed to load tag list', error);
|
||||
}
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to get tag list' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function saveTag(context, { tag }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let promise = null;
|
||||
|
||||
if (!tag.id) {
|
||||
promise = services.addTransactionTag(tag);
|
||||
} else {
|
||||
promise = services.modifyTransactionTag(tag);
|
||||
}
|
||||
|
||||
promise.then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
if (!tag.id) {
|
||||
reject({ message: 'Unable to add tag' });
|
||||
} else {
|
||||
reject({ message: 'Unable to save tag' });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tag.id) {
|
||||
context.commit(ADD_TAG_TO_TRANSACTION_TAG_LIST, data.result);
|
||||
} else {
|
||||
context.commit(SAVE_TAG_IN_TRANSACTION_TAG_LIST, data.result);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to save tag', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
if (!tag.id) {
|
||||
reject({ message: 'Unable to add tag' });
|
||||
} else {
|
||||
reject({ message: 'Unable to save tag' });
|
||||
}
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function changeTagDisplayOrder(context, { tagId, from, to }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let tag = null;
|
||||
|
||||
for (let i = 0; i < context.state.allTransactionTags.length; i++) {
|
||||
if (context.state.allTransactionTags[i].id === tagId) {
|
||||
tag = context.state.allTransactionTags[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tag || !context.state.allTransactionTags[to]) {
|
||||
reject({ message: 'Unable to move tag' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!context.state.transactionTagListStateInvalid) {
|
||||
context.commit(UPDATE_TRANSACTION_TAG_LIST_INVALID_STATE, true);
|
||||
}
|
||||
|
||||
context.commit(CHANGE_TAG_DISPLAY_ORDER_IN_TRANSACTION_TAG_LIST, {
|
||||
tag: tag,
|
||||
from: from,
|
||||
to: to
|
||||
});
|
||||
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
export function updateTagDisplayOrders(context) {
|
||||
const newDisplayOrders = [];
|
||||
|
||||
for (let i = 0; i < context.state.allTransactionTags.length; i++) {
|
||||
newDisplayOrders.push({
|
||||
id: context.state.allTransactionTags[i].id,
|
||||
displayOrder: i + 1
|
||||
});
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.moveTransactionTag({
|
||||
newDisplayOrders: newDisplayOrders
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to move tag' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.state.transactionTagListStateInvalid) {
|
||||
context.commit(UPDATE_TRANSACTION_TAG_LIST_INVALID_STATE, false);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to save tags display order', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to move tag' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function hideTag(context, { tag, hidden }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.hideTransactionTag({
|
||||
id: tag.id,
|
||||
hidden: hidden
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
if (hidden) {
|
||||
reject({ message: 'Unable to hide this tag' });
|
||||
} else {
|
||||
reject({ message: 'Unable to unhide this tag' });
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
context.commit(UPDATE_TAG_VISIBILITY_IN_TRANSACTION_TAG_LIST, {
|
||||
tag: tag,
|
||||
hidden: hidden
|
||||
});
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to change tag visibility', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
if (hidden) {
|
||||
reject({ message: 'Unable to hide this tag' });
|
||||
} else {
|
||||
reject({ message: 'Unable to unhide this tag' });
|
||||
}
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteTag(context, { tag, beforeResolve }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.deleteTransactionTag({
|
||||
id: tag.id
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to delete this tag' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (beforeResolve) {
|
||||
beforeResolve(() => {
|
||||
context.commit(REMOVE_TAG_FROM_TRANSACTION_TAG_LIST, tag);
|
||||
});
|
||||
} else {
|
||||
context.commit(REMOVE_TAG_FROM_TRANSACTION_TAG_LIST, tag);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to delete tag', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to delete this tag' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
import userState from '../lib/userstate.js';
|
||||
import services from '../lib/services.js';
|
||||
import logger from '../lib/logger.js';
|
||||
import utilities from '../lib/utilities/index.js';
|
||||
|
||||
export function get2FAStatus() {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.get2FAStatus().then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result || !utilities.isBoolean(data.result.enable)) {
|
||||
reject({ message: 'Unable to get current two factor authentication status' });
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to get 2fa status', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to get current two factor authentication status' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function enable2FA() {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.enable2FA().then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result || !data.result.qrcode || !data.result.secret) {
|
||||
reject({ message: 'Unable to enable two factor authentication' });
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to request to enable 2fa', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to enable two factor authentication' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function confirmEnable2FA(context, { secret, passcode }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.confirmEnable2FA({
|
||||
secret: secret,
|
||||
passcode: passcode
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result || !data.result.token) {
|
||||
reject({ message: 'Unable to enable two factor authentication' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.result.token) {
|
||||
userState.updateToken(data.result.token);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to confirm to enable 2fa', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to enable two factor authentication' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function disable2FA(context, { password }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.disable2FA({
|
||||
password: password
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to disable two factor authentication' });
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to disable 2fa', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to disable two factor authentication' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function regenerate2FARecoveryCode(context, { password }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.regenerate2FARecoveryCode({
|
||||
password: password
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result || !data.result.recoveryCodes || !data.result.recoveryCodes.length) {
|
||||
reject({ message: 'Unable to regenerate two factor authentication backup codes' });
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to regenerate 2fa recovery code', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to regenerate two factor authentication backup codes' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,438 +0,0 @@
|
||||
import userState from '../lib/userstate.js';
|
||||
import services from '../lib/services.js';
|
||||
import settings from '../lib/settings.js';
|
||||
import logger from '../lib/logger.js';
|
||||
import utilities from '../lib/utilities/index.js';
|
||||
|
||||
import {
|
||||
RESET_STATE,
|
||||
|
||||
STORE_USER_INFO,
|
||||
CLEAR_USER_INFO,
|
||||
|
||||
UPDATE_ACCOUNT_LIST_INVALID_STATE,
|
||||
UPDATE_TRANSACTION_CATEGORY_LIST_INVALID_STATE,
|
||||
UPDATE_TRANSACTION_TAG_LIST_INVALID_STATE,
|
||||
UPDATE_TRANSACTION_OVERVIEW_INVALID_STATE,
|
||||
UPDATE_TRANSACTION_STATISTICS_INVALID_STATE
|
||||
} from './mutations.js';
|
||||
|
||||
export function authorize(context, { loginName, password }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.authorize({
|
||||
loginName: loginName,
|
||||
password: password
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result || !data.result.token) {
|
||||
reject({ message: 'Unable to login' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.result.need2FA) {
|
||||
resolve(data.result);
|
||||
return;
|
||||
}
|
||||
|
||||
if (settings.isEnableApplicationLock() || userState.getUserAppLockState()) {
|
||||
const appLockState = userState.getUserAppLockState();
|
||||
|
||||
if (!appLockState || appLockState.username !== data.result.user.username) {
|
||||
userState.clearTokenAndUserInfo(true);
|
||||
settings.setEnableApplicationLock(false);
|
||||
settings.setEnableApplicationLockWebAuthn(false);
|
||||
userState.clearWebAuthnConfig();
|
||||
}
|
||||
}
|
||||
|
||||
userState.updateToken(data.result.token);
|
||||
|
||||
if (data.result.user && utilities.isObject(data.result.user)) {
|
||||
context.commit(STORE_USER_INFO, data.result.user);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to login', error);
|
||||
|
||||
if (error && error.processed) {
|
||||
reject(error);
|
||||
} else if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else {
|
||||
reject({ message: 'Unable to login' });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function authorize2FA(context, { token, passcode, recoveryCode }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let promise = null;
|
||||
|
||||
if (passcode) {
|
||||
promise = services.authorize2FA({
|
||||
passcode: passcode,
|
||||
token: token
|
||||
});
|
||||
} else if (recoveryCode) {
|
||||
promise = services.authorize2FAByBackupCode({
|
||||
recoveryCode: recoveryCode,
|
||||
token: token
|
||||
});
|
||||
} else {
|
||||
reject({ message: 'An error has occurred' });
|
||||
return;
|
||||
}
|
||||
|
||||
promise.then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result || !data.result.token) {
|
||||
reject({ message: 'Unable to verify' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (settings.isEnableApplicationLock() || userState.getUserAppLockState()) {
|
||||
const appLockState = userState.getUserAppLockState();
|
||||
|
||||
if (!appLockState || appLockState.username !== data.result.user.username) {
|
||||
userState.clearTokenAndUserInfo(true);
|
||||
settings.setEnableApplicationLock(false);
|
||||
settings.setEnableApplicationLockWebAuthn(false);
|
||||
userState.clearWebAuthnConfig();
|
||||
}
|
||||
}
|
||||
|
||||
userState.updateToken(data.result.token);
|
||||
|
||||
if (data.result.user && utilities.isObject(data.result.user)) {
|
||||
context.commit(STORE_USER_INFO, data.result.user);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to verify 2fa', error);
|
||||
|
||||
if (error && error.processed) {
|
||||
reject(error);
|
||||
} else if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else {
|
||||
reject({ message: 'Unable to verify' });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function register(context, { user }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.register({
|
||||
username: user.username,
|
||||
password: user.password,
|
||||
email: user.email,
|
||||
nickname: user.nickname,
|
||||
language: user.language,
|
||||
defaultCurrency: user.defaultCurrency,
|
||||
firstDayOfWeek: user.firstDayOfWeek
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to sign up' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (settings.isEnableApplicationLock()) {
|
||||
settings.setEnableApplicationLock(false);
|
||||
settings.setEnableApplicationLockWebAuthn(false);
|
||||
userState.clearWebAuthnConfig();
|
||||
}
|
||||
|
||||
if (data.result.token && utilities.isString(data.result.token)) {
|
||||
userState.updateToken(data.result.token);
|
||||
}
|
||||
|
||||
if (data.result.user && utilities.isObject(data.result.user)) {
|
||||
context.commit(STORE_USER_INFO, data.result.user);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to sign up', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to sign up' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function logout(context) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.logout().then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to logout' });
|
||||
return;
|
||||
}
|
||||
|
||||
context.commit(CLEAR_USER_INFO);
|
||||
userState.clearTokenAndUserInfo(true);
|
||||
userState.clearWebAuthnConfig();
|
||||
|
||||
context.commit(RESET_STATE);
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to log out', error);
|
||||
|
||||
if (error && error.processed) {
|
||||
reject(error);
|
||||
} else if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else {
|
||||
reject({ message: 'Unable to logout' });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function getCurrentUserProfile() {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getProfile().then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to get user profile' });
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to get user profile', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to get user profile' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function updateUserProfile(context, { profile, currentPassword }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.updateProfile({
|
||||
password: profile.password,
|
||||
oldPassword: currentPassword,
|
||||
email: profile.email,
|
||||
nickname: profile.nickname,
|
||||
defaultAccountId: profile.defaultAccountId,
|
||||
transactionEditScope: profile.transactionEditScope,
|
||||
language: profile.language,
|
||||
defaultCurrency: profile.defaultCurrency,
|
||||
firstDayOfWeek: profile.firstDayOfWeek,
|
||||
longDateFormat: profile.longDateFormat,
|
||||
shortDateFormat: profile.shortDateFormat,
|
||||
longTimeFormat: profile.longTimeFormat,
|
||||
shortTimeFormat: profile.shortTimeFormat
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to update user profile' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.result.newToken && utilities.isString(data.result.newToken)) {
|
||||
userState.updateToken(data.result.newToken);
|
||||
}
|
||||
|
||||
if (data.result.user && utilities.isObject(data.result.user)) {
|
||||
context.commit(STORE_USER_INFO, data.result.user);
|
||||
}
|
||||
|
||||
if (!context.state.accountListStateInvalid) {
|
||||
context.commit(UPDATE_ACCOUNT_LIST_INVALID_STATE, true);
|
||||
}
|
||||
|
||||
if (!context.state.transactionOverviewStateInvalid) {
|
||||
context.commit(UPDATE_TRANSACTION_OVERVIEW_INVALID_STATE, true);
|
||||
}
|
||||
|
||||
if (!context.state.transactionStatisticsStateInvalid) {
|
||||
context.commit(UPDATE_TRANSACTION_STATISTICS_INVALID_STATE, true);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to save user profile', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to update user profile' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function getUserDataStatistics() {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getUserDataStatistics().then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to get user statistics data' });
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to get user statistics data', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to get user statistics data' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function getExportedUserData() {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getExportedUserData().then(response => {
|
||||
if (response && response.headers && response.headers['content-type'] !== 'text/csv') {
|
||||
reject({ message: 'Unable to get exported user data' });
|
||||
return;
|
||||
}
|
||||
|
||||
const blob = new Blob([response.data], { type: response.headers['content-type'] });
|
||||
resolve(blob);
|
||||
}).catch(error => {
|
||||
logger.error('failed to get user statistics data', error);
|
||||
|
||||
if (error.response && error.response.headers['content-type'] === 'text/text' && error.response && error.response.data) {
|
||||
reject({ message: 'error.' + error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to get exported user data' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function clearUserData(context, { password }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.clearData({
|
||||
password: password
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to clear user data' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!context.state.accountListStateInvalid) {
|
||||
context.commit(UPDATE_ACCOUNT_LIST_INVALID_STATE, true);
|
||||
}
|
||||
|
||||
if (!context.state.transactionCategoryListStateInvalid) {
|
||||
context.commit(UPDATE_TRANSACTION_CATEGORY_LIST_INVALID_STATE, true);
|
||||
}
|
||||
|
||||
if (!context.state.transactionTagListStateInvalid) {
|
||||
context.commit(UPDATE_TRANSACTION_TAG_LIST_INVALID_STATE, true);
|
||||
}
|
||||
|
||||
if (!context.state.transactionOverviewStateInvalid) {
|
||||
context.commit(UPDATE_TRANSACTION_OVERVIEW_INVALID_STATE, true);
|
||||
}
|
||||
|
||||
if (!context.state.transactionStatisticsStateInvalid) {
|
||||
context.commit(UPDATE_TRANSACTION_STATISTICS_INVALID_STATE, true);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to clear user data', error);
|
||||
|
||||
if (error && error.processed) {
|
||||
reject(error);
|
||||
} else if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else {
|
||||
reject({ message: 'Unable to clear user data' });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function clearUserInfoState(context) {
|
||||
context.commit(CLEAR_USER_INFO);
|
||||
}
|
||||
|
||||
export function resetState(context) {
|
||||
context.commit(RESET_STATE);
|
||||
}
|
||||
|
||||
export function currentUserNickname(state) {
|
||||
const userInfo = state.currentUserInfo || {};
|
||||
return userInfo.nickname || userInfo.username || null;
|
||||
}
|
||||
|
||||
export function currentUserDefaultAccountId(state) {
|
||||
const userInfo = state.currentUserInfo || {};
|
||||
return userInfo.defaultAccountId || '';
|
||||
}
|
||||
|
||||
export function currentUserLanguage(state) {
|
||||
const userInfo = state.currentUserInfo || {};
|
||||
return userInfo.language || state.defaultSetting.language;
|
||||
}
|
||||
|
||||
export function currentUserDefaultCurrency(state) {
|
||||
const userInfo = state.currentUserInfo || {};
|
||||
return userInfo.defaultCurrency || state.defaultSetting.currency;
|
||||
}
|
||||
|
||||
export function currentUserFirstDayOfWeek(state) {
|
||||
const userInfo = state.currentUserInfo || {};
|
||||
return utilities.isNumber(userInfo.firstDayOfWeek) ? userInfo.firstDayOfWeek : state.defaultSetting.firstDayOfWeek;
|
||||
}
|
||||
|
||||
export function currentUserLongDateFormat(state) {
|
||||
const userInfo = state.currentUserInfo || {};
|
||||
return utilities.isNumber(userInfo.longDateFormat) ? userInfo.longDateFormat : state.defaultSetting.longDateFormat;
|
||||
}
|
||||
|
||||
export function currentUserShortDateFormat(state) {
|
||||
const userInfo = state.currentUserInfo || {};
|
||||
return utilities.isNumber(userInfo.shortDateFormat) ? userInfo.shortDateFormat : state.defaultSetting.shortDateFormat;
|
||||
}
|
||||
|
||||
export function currentUserLongTimeFormat(state) {
|
||||
const userInfo = state.currentUserInfo || {};
|
||||
return utilities.isNumber(userInfo.longTimeFormat) ? userInfo.longTimeFormat : state.defaultSetting.longTimeFormat;
|
||||
}
|
||||
|
||||
export function currentUserShortTimeFormat(state) {
|
||||
const userInfo = state.currentUserInfo || {};
|
||||
return utilities.isNumber(userInfo.shortTimeFormat) ? userInfo.shortTimeFormat : state.defaultSetting.shortTimeFormat;
|
||||
}
|
||||
@@ -0,0 +1,797 @@
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
import { useUserStore } from './user.js';
|
||||
import { useExchangeRatesStore } from './exchangeRates.js';
|
||||
|
||||
import accountConstants from '@/consts/account.js';
|
||||
import services from '@/lib/services.js';
|
||||
import logger from '@/lib/logger.js';
|
||||
import { isNumber } from '@/lib/common.js';
|
||||
import { getCategorizedAccounts, getAllFilteredAccountsBalance } from '@/lib/account.js';
|
||||
|
||||
function loadAccountList(state, accounts) {
|
||||
state.allAccounts = accounts;
|
||||
state.allAccountsMap = {};
|
||||
|
||||
for (let i = 0; i < accounts.length; i++) {
|
||||
const account = accounts[i];
|
||||
state.allAccountsMap[account.id] = account;
|
||||
|
||||
if (account.subAccounts) {
|
||||
for (let j = 0; j < account.subAccounts.length; j++) {
|
||||
const subAccount = account.subAccounts[j];
|
||||
state.allAccountsMap[subAccount.id] = subAccount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state.allCategorizedAccounts = getCategorizedAccounts(accounts);
|
||||
}
|
||||
|
||||
function addAccountToAccountList(state, account) {
|
||||
let insertIndexToAllList = 0;
|
||||
|
||||
for (let i = 0; i < state.allAccounts.length; i++) {
|
||||
if (state.allAccounts[i].category > account.category) {
|
||||
insertIndexToAllList = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
state.allAccounts.splice(insertIndexToAllList, 0, account);
|
||||
|
||||
state.allAccountsMap[account.id] = account;
|
||||
|
||||
if (account.subAccounts) {
|
||||
for (let i = 0; i < account.subAccounts.length; i++) {
|
||||
const subAccount = account.subAccounts[i];
|
||||
state.allAccountsMap[subAccount.id] = subAccount;
|
||||
}
|
||||
}
|
||||
|
||||
if (state.allCategorizedAccounts[account.category]) {
|
||||
const accountList = state.allCategorizedAccounts[account.category].accounts;
|
||||
accountList.push(account);
|
||||
} else {
|
||||
state.allCategorizedAccounts = getCategorizedAccounts(state.allAccounts);
|
||||
}
|
||||
}
|
||||
|
||||
function updateAccountToAccountList(state, account) {
|
||||
for (let i = 0; i < state.allAccounts.length; i++) {
|
||||
if (state.allAccounts[i].id === account.id) {
|
||||
state.allAccounts.splice(i, 1, account);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
state.allAccountsMap[account.id] = account;
|
||||
|
||||
if (account.subAccounts) {
|
||||
for (let i = 0; i < account.subAccounts.length; i++) {
|
||||
const subAccount = account.subAccounts[i];
|
||||
state.allAccountsMap[subAccount.id] = subAccount;
|
||||
}
|
||||
}
|
||||
|
||||
if (state.allCategorizedAccounts[account.category]) {
|
||||
const accountList = state.allCategorizedAccounts[account.category].accounts;
|
||||
|
||||
for (let i = 0; i < accountList.length; i++) {
|
||||
if (accountList[i].id === account.id) {
|
||||
accountList.splice(i, 1, account);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateAccountDisplayOrderInAccountList(state, { account, from, to }) {
|
||||
let fromAccount = null;
|
||||
let toAccount = null;
|
||||
|
||||
if (state.allCategorizedAccounts[account.category]) {
|
||||
const accountList = state.allCategorizedAccounts[account.category].accounts;
|
||||
fromAccount = accountList[from];
|
||||
toAccount = accountList[to];
|
||||
|
||||
accountList.splice(to, 0, accountList.splice(from, 1)[0]);
|
||||
}
|
||||
|
||||
if (fromAccount && toAccount) {
|
||||
let globalFromIndex = -1;
|
||||
let globalToIndex = -1;
|
||||
|
||||
for (let i = 0; i < state.allAccounts.length; i++) {
|
||||
if (state.allAccounts[i].id === fromAccount.id) {
|
||||
globalFromIndex = i;
|
||||
} else if (state.allAccounts[i].id === toAccount.id) {
|
||||
globalToIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (globalFromIndex >= 0 && globalToIndex >= 0) {
|
||||
state.allAccounts.splice(globalToIndex, 0, state.allAccounts.splice(globalFromIndex, 1)[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateAccountVisibilityInAccountList(state, { account, hidden }) {
|
||||
if (state.allAccountsMap[account.id]) {
|
||||
state.allAccountsMap[account.id].hidden = hidden;
|
||||
}
|
||||
}
|
||||
|
||||
function removeAccountFromAccountList(state, account) {
|
||||
for (let i = 0; i < state.allAccounts.length; i++) {
|
||||
if (state.allAccounts[i].id === account.id) {
|
||||
state.allAccounts.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (state.allAccountsMap[account.id] && state.allAccountsMap[account.id].subAccounts) {
|
||||
const subAccounts = state.allAccountsMap[account.id].subAccounts;
|
||||
|
||||
for (let i = 0; i < subAccounts.length; i++) {
|
||||
const subAccount = subAccounts[i];
|
||||
if (state.allAccountsMap[subAccount.id]) {
|
||||
delete state.allAccountsMap[subAccount.id];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state.allAccountsMap[account.id]) {
|
||||
delete state.allAccountsMap[account.id];
|
||||
}
|
||||
|
||||
if (state.allCategorizedAccounts[account.category]) {
|
||||
const accountList = state.allCategorizedAccounts[account.category].accounts;
|
||||
|
||||
for (let i = 0; i < accountList.length; i++) {
|
||||
if (accountList[i].id === account.id) {
|
||||
accountList.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const useAccountsStore = defineStore('accounts', {
|
||||
state: () => ({
|
||||
allAccounts: [],
|
||||
allAccountsMap: {},
|
||||
allCategorizedAccounts: {},
|
||||
accountListStateInvalid: true,
|
||||
}),
|
||||
getters: {
|
||||
allPlainAccounts(state) {
|
||||
const allAccounts = [];
|
||||
|
||||
for (let i = 0; i < state.allAccounts.length; i++) {
|
||||
const account = state.allAccounts[i];
|
||||
|
||||
if (account.type === accountConstants.allAccountTypes.SingleAccount) {
|
||||
allAccounts.push(account);
|
||||
} else if (account.type === accountConstants.allAccountTypes.MultiSubAccounts) {
|
||||
for (let j = 0; j < account.subAccounts.length; j++) {
|
||||
const subAccount = account.subAccounts[j];
|
||||
allAccounts.push(subAccount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allAccounts;
|
||||
},
|
||||
allVisiblePlainAccounts(state) {
|
||||
const allVisibleAccounts = [];
|
||||
|
||||
for (let i = 0; i < state.allAccounts.length; i++) {
|
||||
const account = state.allAccounts[i];
|
||||
|
||||
if (account.hidden) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (account.type === accountConstants.allAccountTypes.SingleAccount) {
|
||||
allVisibleAccounts.push(account);
|
||||
} else if (account.type === accountConstants.allAccountTypes.MultiSubAccounts) {
|
||||
for (let j = 0; j < account.subAccounts.length; j++) {
|
||||
const subAccount = account.subAccounts[j];
|
||||
allVisibleAccounts.push(subAccount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allVisibleAccounts;
|
||||
},
|
||||
allAvailableAccountsCount(state) {
|
||||
let allAccountCount = 0;
|
||||
|
||||
for (let category in state.allCategorizedAccounts) {
|
||||
if (!Object.prototype.hasOwnProperty.call(state.allCategorizedAccounts, category)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
allAccountCount += state.allCategorizedAccounts[category].accounts.length;
|
||||
}
|
||||
|
||||
return allAccountCount;
|
||||
},
|
||||
allVisibleAccountsCount(state) {
|
||||
let shownAccountCount = 0;
|
||||
|
||||
for (let category in state.allCategorizedAccounts) {
|
||||
if (!Object.prototype.hasOwnProperty.call(state.allCategorizedAccounts, category)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const accountList = state.allCategorizedAccounts[category].accounts;
|
||||
|
||||
for (let i = 0; i < accountList.length; i++) {
|
||||
if (!accountList[i].hidden) {
|
||||
shownAccountCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return shownAccountCount;
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
updateAccountListInvalidState(invalidState) {
|
||||
this.accountListStateInvalid = invalidState;
|
||||
},
|
||||
resetAccounts() {
|
||||
this.allAccounts = [];
|
||||
this.allAccountsMap = {};
|
||||
this.allCategorizedAccounts = {};
|
||||
this.accountListStateInvalid = true;
|
||||
},
|
||||
getFirstShowingIds(showHidden) {
|
||||
const ret = {
|
||||
accounts: {},
|
||||
subAccounts: {}
|
||||
};
|
||||
|
||||
for (let category in this.allCategorizedAccounts) {
|
||||
if (!Object.prototype.hasOwnProperty.call(this.allCategorizedAccounts, category)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!this.allCategorizedAccounts[category] || !this.allCategorizedAccounts[category].accounts) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const accounts = this.allCategorizedAccounts[category].accounts;
|
||||
|
||||
for (let i = 0; i < accounts.length; i++) {
|
||||
const account = accounts[i];
|
||||
|
||||
if (account.type === accountConstants.allAccountTypes.MultiSubAccounts && account.subAccounts) {
|
||||
for (let j = 0; j < account.subAccounts.length; j++) {
|
||||
const subAccount = account.subAccounts[j];
|
||||
|
||||
if (showHidden || !subAccount.hidden) {
|
||||
ret.subAccounts[account.id] = subAccount.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (showHidden || !account.hidden) {
|
||||
ret.accounts[category] = account.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
},
|
||||
getLastShowingIds(showHidden) {
|
||||
const ret = {
|
||||
accounts: {},
|
||||
subAccounts: {}
|
||||
};
|
||||
|
||||
for (let category in this.allCategorizedAccounts) {
|
||||
if (!Object.prototype.hasOwnProperty.call(this.allCategorizedAccounts, category)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!this.allCategorizedAccounts[category] || !this.allCategorizedAccounts[category].accounts) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const accounts = this.allCategorizedAccounts[category].accounts;
|
||||
|
||||
for (let i = accounts.length - 1; i >= 0; i--) {
|
||||
const account = accounts[i];
|
||||
|
||||
if (account.type === accountConstants.allAccountTypes.MultiSubAccounts && account.subAccounts) {
|
||||
for (let j = account.subAccounts.length - 1; j >= 0; j--) {
|
||||
const subAccount = account.subAccounts[j];
|
||||
|
||||
if (showHidden || !subAccount.hidden) {
|
||||
ret.subAccounts[account.id] = subAccount.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (showHidden || !account.hidden) {
|
||||
ret.accounts[category] = account.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
},
|
||||
getNetAssets(showAccountBalance) {
|
||||
if (!showAccountBalance) {
|
||||
return '***';
|
||||
}
|
||||
|
||||
const userStore = useUserStore();
|
||||
const exchangeRatesStore = useExchangeRatesStore();
|
||||
const accountsBalance = getAllFilteredAccountsBalance(this.allCategorizedAccounts, () => true);
|
||||
let netAssets = 0;
|
||||
let hasUnCalculatedAmount = false;
|
||||
|
||||
for (let i = 0; i < accountsBalance.length; i++) {
|
||||
if (accountsBalance[i].currency === userStore.currentUserDefaultCurrency) {
|
||||
netAssets += accountsBalance[i].balance;
|
||||
} else {
|
||||
const balance = exchangeRatesStore.getExchangedAmount(accountsBalance[i].balance, accountsBalance[i].currency, userStore.currentUserDefaultCurrency);
|
||||
|
||||
if (!isNumber(balance)) {
|
||||
hasUnCalculatedAmount = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
netAssets += Math.floor(balance);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasUnCalculatedAmount) {
|
||||
return netAssets + '+';
|
||||
} else {
|
||||
return netAssets;
|
||||
}
|
||||
},
|
||||
getTotalAssets(showAccountBalance) {
|
||||
if (!showAccountBalance) {
|
||||
return '***';
|
||||
}
|
||||
|
||||
const userStore = useUserStore();
|
||||
const exchangeRatesStore = useExchangeRatesStore();
|
||||
const accountsBalance = getAllFilteredAccountsBalance(this.allCategorizedAccounts, account => account.isAsset);
|
||||
let totalAssets = 0;
|
||||
let hasUnCalculatedAmount = false;
|
||||
|
||||
for (let i = 0; i < accountsBalance.length; i++) {
|
||||
if (accountsBalance[i].currency === userStore.currentUserDefaultCurrency) {
|
||||
totalAssets += accountsBalance[i].balance;
|
||||
} else {
|
||||
const balance = exchangeRatesStore.getExchangedAmount(accountsBalance[i].balance, accountsBalance[i].currency, userStore.currentUserDefaultCurrency);
|
||||
|
||||
if (!isNumber(balance)) {
|
||||
hasUnCalculatedAmount = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
totalAssets += Math.floor(balance);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasUnCalculatedAmount) {
|
||||
return totalAssets + '+';
|
||||
} else {
|
||||
return totalAssets;
|
||||
}
|
||||
},
|
||||
getTotalLiabilities(showAccountBalance) {
|
||||
if (!showAccountBalance) {
|
||||
return '***';
|
||||
}
|
||||
|
||||
const userStore = useUserStore();
|
||||
const exchangeRatesStore = useExchangeRatesStore();
|
||||
const accountsBalance = getAllFilteredAccountsBalance(this.allCategorizedAccounts, account => account.isLiability);
|
||||
let totalLiabilities = 0;
|
||||
let hasUnCalculatedAmount = false;
|
||||
|
||||
for (let i = 0; i < accountsBalance.length; i++) {
|
||||
if (accountsBalance[i].currency === userStore.currentUserDefaultCurrency) {
|
||||
totalLiabilities -= accountsBalance[i].balance;
|
||||
} else {
|
||||
const balance = exchangeRatesStore.getExchangedAmount(accountsBalance[i].balance, accountsBalance[i].currency, userStore.currentUserDefaultCurrency);
|
||||
|
||||
if (!isNumber(balance)) {
|
||||
hasUnCalculatedAmount = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
totalLiabilities -= Math.floor(balance);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasUnCalculatedAmount) {
|
||||
return totalLiabilities + '+';
|
||||
} else {
|
||||
return totalLiabilities;
|
||||
}
|
||||
},
|
||||
getAccountCategoryTotalBalance(showAccountBalance, accountCategory) {
|
||||
if (!showAccountBalance) {
|
||||
return '***';
|
||||
}
|
||||
|
||||
const userStore = useUserStore();
|
||||
const exchangeRatesStore = useExchangeRatesStore();
|
||||
const accountsBalance = getAllFilteredAccountsBalance(this.allCategorizedAccounts, account => account.category === accountCategory.id);
|
||||
let totalBalance = 0;
|
||||
let hasUnCalculatedAmount = false;
|
||||
|
||||
for (let i = 0; i < accountsBalance.length; i++) {
|
||||
if (accountsBalance[i].currency === userStore.currentUserDefaultCurrency) {
|
||||
if (accountsBalance[i].isAsset) {
|
||||
totalBalance += accountsBalance[i].balance;
|
||||
} else if (accountsBalance[i].isLiability) {
|
||||
totalBalance -= accountsBalance[i].balance;
|
||||
} else {
|
||||
totalBalance += accountsBalance[i].balance;
|
||||
}
|
||||
} else {
|
||||
const balance = exchangeRatesStore.getExchangedAmount(accountsBalance[i].balance, accountsBalance[i].currency, userStore.currentUserDefaultCurrency);
|
||||
|
||||
if (!isNumber(balance)) {
|
||||
hasUnCalculatedAmount = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (accountsBalance[i].isAsset) {
|
||||
totalBalance += Math.floor(balance);
|
||||
} else if (accountsBalance[i].isLiability) {
|
||||
totalBalance -= Math.floor(balance);
|
||||
} else {
|
||||
totalBalance += Math.floor(balance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasUnCalculatedAmount) {
|
||||
return totalBalance + '+';
|
||||
} else {
|
||||
return totalBalance;
|
||||
}
|
||||
},
|
||||
getAccountBalance(showAccountBalance, account) {
|
||||
if (account.type !== accountConstants.allAccountTypes.SingleAccount) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (showAccountBalance) {
|
||||
if (account.isAsset) {
|
||||
return account.balance;
|
||||
} else if (account.isLiability) {
|
||||
return -account.balance;
|
||||
} else {
|
||||
return account.balance;
|
||||
}
|
||||
} else {
|
||||
return '***';
|
||||
}
|
||||
},
|
||||
hasAccount(accountCategory, visibleOnly) {
|
||||
if (!this.allCategorizedAccounts[accountCategory.id] ||
|
||||
!this.allCategorizedAccounts[accountCategory.id].accounts ||
|
||||
!this.allCategorizedAccounts[accountCategory.id].accounts.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let shownCount = 0;
|
||||
|
||||
for (let i = 0; i < this.allCategorizedAccounts[accountCategory.id].accounts.length; i++) {
|
||||
const account = this.allCategorizedAccounts[accountCategory.id].accounts[i];
|
||||
|
||||
if (!visibleOnly || !account.hidden) {
|
||||
shownCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return shownCount > 0;
|
||||
},
|
||||
hasVisibleSubAccount(showHidden, account) {
|
||||
if (!account || account.type !== accountConstants.allAccountTypes.MultiSubAccounts || !account.subAccounts) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < account.subAccounts.length; i++) {
|
||||
if (showHidden || !account.subAccounts[i].hidden) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
loadAllAccounts({ force }) {
|
||||
const self = this;
|
||||
|
||||
if (!force && !self.accountListStateInvalid) {
|
||||
return new Promise((resolve) => {
|
||||
resolve(self.allAccounts);
|
||||
});
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getAllAccounts({
|
||||
visibleOnly: false
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to get account list' });
|
||||
return;
|
||||
}
|
||||
|
||||
loadAccountList(self, data.result);
|
||||
|
||||
if (self.accountListStateInvalid) {
|
||||
self.updateAccountListInvalidState(false);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
if (force) {
|
||||
logger.error('failed to force load account list', error);
|
||||
} else {
|
||||
logger.error('failed to load account list', error);
|
||||
}
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to get account list' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
getAccount({ accountId }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getAccount({
|
||||
id: accountId
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to get account' });
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to load account info', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to get account' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
saveAccount({ account }) {
|
||||
const self = this;
|
||||
const oldAccount = account.id ? self.allAccountsMap[account.id] : null;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let promise = null;
|
||||
|
||||
if (!account.id) {
|
||||
promise = services.addAccount(account);
|
||||
} else {
|
||||
promise = services.modifyAccount(account);
|
||||
}
|
||||
|
||||
promise.then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
if (!account.id) {
|
||||
reject({ message: 'Unable to add account' });
|
||||
} else {
|
||||
reject({ message: 'Unable to save account' });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!account.id) {
|
||||
addAccountToAccountList(self, data.result);
|
||||
} else {
|
||||
if (oldAccount && oldAccount.category === data.result.category) {
|
||||
updateAccountToAccountList(self, data.result);
|
||||
} else {
|
||||
self.updateAccountListInvalidState(true);
|
||||
}
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to save account', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
if (!account.id) {
|
||||
reject({ message: 'Unable to add account' });
|
||||
} else {
|
||||
reject({ message: 'Unable to save account' });
|
||||
}
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
changeAccountDisplayOrder({ accountId, from, to }) {
|
||||
const self = this;
|
||||
const account = self.allAccountsMap[accountId];
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!account ||
|
||||
!self.allCategorizedAccounts[account.category] ||
|
||||
!self.allCategorizedAccounts[account.category].accounts ||
|
||||
!self.allCategorizedAccounts[account.category].accounts[to]) {
|
||||
reject({ message: 'Unable to move account' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self.accountListStateInvalid) {
|
||||
self.updateAccountListInvalidState(true);
|
||||
}
|
||||
|
||||
updateAccountDisplayOrderInAccountList(self, {
|
||||
account: account,
|
||||
from: from,
|
||||
to: to
|
||||
});
|
||||
|
||||
resolve();
|
||||
});
|
||||
},
|
||||
updateAccountDisplayOrders() {
|
||||
const self = this;
|
||||
const newDisplayOrders = [];
|
||||
|
||||
for (let category in self.allCategorizedAccounts) {
|
||||
if (!Object.prototype.hasOwnProperty.call(self.allCategorizedAccounts, category)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const accountList = self.allCategorizedAccounts[category].accounts;
|
||||
|
||||
for (let i = 0; i < accountList.length; i++) {
|
||||
newDisplayOrders.push({
|
||||
id: accountList[i].id,
|
||||
displayOrder: i + 1
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.moveAccount({
|
||||
newDisplayOrders: newDisplayOrders
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to move account' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.accountListStateInvalid) {
|
||||
self.updateAccountListInvalidState(false);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to save accounts display order', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to move account' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
hideAccount({ account, hidden }) {
|
||||
const self = this;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.hideAccount({
|
||||
id: account.id,
|
||||
hidden: hidden
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
if (hidden) {
|
||||
reject({ message: 'Unable to hide this account' });
|
||||
} else {
|
||||
reject({ message: 'Unable to unhide this account' });
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
updateAccountVisibilityInAccountList(self, {
|
||||
account: account,
|
||||
hidden: hidden
|
||||
});
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to change account visibility', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
if (hidden) {
|
||||
reject({ message: 'Unable to hide this account' });
|
||||
} else {
|
||||
reject({ message: 'Unable to unhide this account' });
|
||||
}
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
deleteAccount({ account, beforeResolve }) {
|
||||
const self = this;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.deleteAccount({
|
||||
id: account.id
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to delete this account' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (beforeResolve) {
|
||||
beforeResolve(() => {
|
||||
removeAccountFromAccountList(self, account);
|
||||
});
|
||||
} else {
|
||||
removeAccountFromAccountList(self, account);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to delete account', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to delete this account' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,132 @@
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
import services from '@/lib/services.js';
|
||||
import logger from '@/lib/logger.js';
|
||||
import { isEquals } from '@/lib/common.js';
|
||||
import { getCurrentUnixTime, formatUnixTime } from '@/lib/datetime.js';
|
||||
import { getExchangedAmount } from '@/lib/currency.js';
|
||||
|
||||
const exchangeRatesLocalStorageKey = 'ebk_app_exchange_rates';
|
||||
|
||||
function getExchangeRatesFromLocalStorage() {
|
||||
const storageData = localStorage.getItem(exchangeRatesLocalStorageKey) || '{}';
|
||||
return JSON.parse(storageData);
|
||||
}
|
||||
|
||||
function setExchangeRatesToLocalStorage(value) {
|
||||
const storageData = JSON.stringify(value);
|
||||
localStorage.setItem(exchangeRatesLocalStorageKey, storageData);
|
||||
}
|
||||
|
||||
function clearExchangeRatesFromLocalStorage() {
|
||||
localStorage.removeItem(exchangeRatesLocalStorageKey);
|
||||
}
|
||||
|
||||
export const useExchangeRatesStore = defineStore('exchangeRates', {
|
||||
state: () => ({
|
||||
latestExchangeRates: getExchangeRatesFromLocalStorage()
|
||||
}),
|
||||
getters: {
|
||||
exchangeRatesLastUpdateTime(state) {
|
||||
const exchangeRates = state.latestExchangeRates || {};
|
||||
return exchangeRates && exchangeRates.data ? exchangeRates.data.updateTime : null;
|
||||
},
|
||||
latestExchangeRateMap(state) {
|
||||
const exchangeRateMap = {};
|
||||
|
||||
if (!state.latestExchangeRates || !state.latestExchangeRates.data || !state.latestExchangeRates.data.exchangeRates) {
|
||||
return exchangeRateMap;
|
||||
}
|
||||
|
||||
for (let i = 0; i < state.latestExchangeRates.data.exchangeRates.length; i++) {
|
||||
const exchangeRate = state.latestExchangeRates.data.exchangeRates[i];
|
||||
exchangeRateMap[exchangeRate.currency] = exchangeRate;
|
||||
}
|
||||
|
||||
return exchangeRateMap;
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
resetLatestExchangeRates() {
|
||||
this.latestExchangeRates = {};
|
||||
clearExchangeRatesFromLocalStorage();
|
||||
},
|
||||
getLatestExchangeRates({ silent, force }) {
|
||||
const self = this;
|
||||
const currentExchangeRateData = self.latestExchangeRates;
|
||||
const now = getCurrentUnixTime();
|
||||
|
||||
if (!force) {
|
||||
if (currentExchangeRateData && currentExchangeRateData.time && currentExchangeRateData.data &&
|
||||
formatUnixTime(currentExchangeRateData.data.updateTime, 'YYYY-MM-DD') === formatUnixTime(now, 'YYYY-MM-DD')) {
|
||||
return currentExchangeRateData.data;
|
||||
}
|
||||
|
||||
if (currentExchangeRateData && currentExchangeRateData.time && currentExchangeRateData.data &&
|
||||
formatUnixTime(currentExchangeRateData.time, 'YYYY-MM-DD HH') === formatUnixTime(now, 'YYYY-MM-DD HH')) {
|
||||
return currentExchangeRateData.data;
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getLatestExchangeRates({
|
||||
ignoreError: silent
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to get exchange rates data' });
|
||||
return;
|
||||
}
|
||||
|
||||
const currentData = getExchangeRatesFromLocalStorage();
|
||||
|
||||
if (currentData && currentData.data && isEquals(currentData.data, data.result)) {
|
||||
reject({ message: 'Exchange rates data is up to date' });
|
||||
return;
|
||||
}
|
||||
|
||||
this.latestExchangeRates = {
|
||||
time: now,
|
||||
data: data.result
|
||||
};
|
||||
setExchangeRatesToLocalStorage(this.latestExchangeRates);
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to get latest exchange rates data', error);
|
||||
|
||||
if (error && error.processed) {
|
||||
reject(error);
|
||||
} else if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else {
|
||||
reject({ message: 'Unable to get exchange rates data' });
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
getExchangedAmount(amount, fromCurrency, toCurrency) {
|
||||
if (!this.latestExchangeRates || !this.latestExchangeRates.data || !this.latestExchangeRates.data.exchangeRates) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const exchangeRates = this.latestExchangeRates.data.exchangeRates;
|
||||
const exchangeRateMap = {};
|
||||
|
||||
for (let i = 0; i < exchangeRates.length; i++) {
|
||||
const exchangeRate = exchangeRates[i];
|
||||
exchangeRateMap[exchangeRate.currency] = exchangeRate;
|
||||
}
|
||||
|
||||
const fromCurrencyExchangeRate = exchangeRateMap[fromCurrency];
|
||||
const toCurrencyExchangeRate = exchangeRateMap[toCurrency];
|
||||
|
||||
if (!fromCurrencyExchangeRate || !toCurrencyExchangeRate) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return getExchangedAmount(amount, fromCurrencyExchangeRate.rate, toCurrencyExchangeRate.rate);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,350 @@
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
import { useUserStore } from './user.js';
|
||||
import { useAccountsStore } from './account.js';
|
||||
import { useTransactionCategoriesStore } from './transactionCategory.js';
|
||||
import { useTransactionTagsStore } from './transactionTag.js';
|
||||
import { useTransactionsStore } from './transaction.js';
|
||||
import { useOverviewStore } from './overview.js';
|
||||
import { useStatisticsStore } from './statistics.js';
|
||||
import { useExchangeRatesStore } from './exchangeRates.js';
|
||||
|
||||
import userState from '@/lib/userstate.js';
|
||||
import services from '@/lib/services.js';
|
||||
import settings from '@/lib/settings.js';
|
||||
import logger from '@/lib/logger.js';
|
||||
import { isObject, isString } from '@/lib/common.js';
|
||||
|
||||
export const useRootStore = defineStore('root', {
|
||||
actions: {
|
||||
resetAllStates() {
|
||||
const exchangeRatesStore = useExchangeRatesStore();
|
||||
exchangeRatesStore.resetLatestExchangeRates();
|
||||
|
||||
const statisticsStore = useStatisticsStore();
|
||||
statisticsStore.resetTransactionStatistics();
|
||||
|
||||
const overviewStore = useOverviewStore();
|
||||
overviewStore.resetTransactionOverview();
|
||||
|
||||
const transactionsStore = useTransactionsStore();
|
||||
transactionsStore.resetTransactions();
|
||||
|
||||
const transactionTagsStore = useTransactionTagsStore();
|
||||
transactionTagsStore.resetTransactionTags();
|
||||
|
||||
const transactionCategoriesStore = useTransactionCategoriesStore();
|
||||
transactionCategoriesStore.resetTransactionCategories();
|
||||
|
||||
const accountsStore = useAccountsStore();
|
||||
accountsStore.resetAccounts();
|
||||
|
||||
const userStore = useUserStore();
|
||||
userStore.resetUserInfo();
|
||||
},
|
||||
authorize({ loginName, password }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.authorize({
|
||||
loginName: loginName,
|
||||
password: password
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result || !data.result.token) {
|
||||
reject({ message: 'Unable to login' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.result.need2FA) {
|
||||
resolve(data.result);
|
||||
return;
|
||||
}
|
||||
|
||||
if (settings.isEnableApplicationLock() || userState.getUserAppLockState()) {
|
||||
const appLockState = userState.getUserAppLockState();
|
||||
|
||||
if (!appLockState || appLockState.username !== data.result.user.username) {
|
||||
userState.clearTokenAndUserInfo(true);
|
||||
settings.setEnableApplicationLock(false);
|
||||
settings.setEnableApplicationLockWebAuthn(false);
|
||||
userState.clearWebAuthnConfig();
|
||||
}
|
||||
}
|
||||
|
||||
userState.updateToken(data.result.token);
|
||||
|
||||
if (data.result.user && isObject(data.result.user)) {
|
||||
const userStore = useUserStore();
|
||||
userStore.storeUserInfo(data.result.user);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to login', error);
|
||||
|
||||
if (error && error.processed) {
|
||||
reject(error);
|
||||
} else if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else {
|
||||
reject({ message: 'Unable to login' });
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
authorize2FA({ token, passcode, recoveryCode }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let promise = null;
|
||||
|
||||
if (passcode) {
|
||||
promise = services.authorize2FA({
|
||||
passcode: passcode,
|
||||
token: token
|
||||
});
|
||||
} else if (recoveryCode) {
|
||||
promise = services.authorize2FAByBackupCode({
|
||||
recoveryCode: recoveryCode,
|
||||
token: token
|
||||
});
|
||||
} else {
|
||||
reject({ message: 'An error has occurred' });
|
||||
return;
|
||||
}
|
||||
|
||||
promise.then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result || !data.result.token) {
|
||||
reject({ message: 'Unable to verify' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (settings.isEnableApplicationLock() || userState.getUserAppLockState()) {
|
||||
const appLockState = userState.getUserAppLockState();
|
||||
|
||||
if (!appLockState || appLockState.username !== data.result.user.username) {
|
||||
userState.clearTokenAndUserInfo(true);
|
||||
settings.setEnableApplicationLock(false);
|
||||
settings.setEnableApplicationLockWebAuthn(false);
|
||||
userState.clearWebAuthnConfig();
|
||||
}
|
||||
}
|
||||
|
||||
userState.updateToken(data.result.token);
|
||||
|
||||
if (data.result.user && isObject(data.result.user)) {
|
||||
const userStore = useUserStore();
|
||||
userStore.storeUserInfo(data.result.user);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to verify 2fa', error);
|
||||
|
||||
if (error && error.processed) {
|
||||
reject(error);
|
||||
} else if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else {
|
||||
reject({ message: 'Unable to verify' });
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
register({ user }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.register({
|
||||
username: user.username,
|
||||
password: user.password,
|
||||
email: user.email,
|
||||
nickname: user.nickname,
|
||||
language: user.language,
|
||||
defaultCurrency: user.defaultCurrency,
|
||||
firstDayOfWeek: user.firstDayOfWeek
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to sign up' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (settings.isEnableApplicationLock()) {
|
||||
settings.setEnableApplicationLock(false);
|
||||
settings.setEnableApplicationLockWebAuthn(false);
|
||||
userState.clearWebAuthnConfig();
|
||||
}
|
||||
|
||||
if (data.result.token && isString(data.result.token)) {
|
||||
userState.updateToken(data.result.token);
|
||||
}
|
||||
|
||||
if (data.result.user && isObject(data.result.user)) {
|
||||
const userStore = useUserStore();
|
||||
userStore.storeUserInfo(data.result.user);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to sign up', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to sign up' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
logout() {
|
||||
const self = this;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.logout().then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to logout' });
|
||||
return;
|
||||
}
|
||||
|
||||
userState.clearTokenAndUserInfo(true);
|
||||
userState.clearWebAuthnConfig();
|
||||
self.resetAllStates();
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to log out', error);
|
||||
|
||||
if (error && error.processed) {
|
||||
reject(error);
|
||||
} else if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else {
|
||||
reject({ message: 'Unable to logout' });
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
forceLogout() {
|
||||
userState.clearTokenAndUserInfo(true);
|
||||
userState.clearWebAuthnConfig();
|
||||
this.resetAllStates();
|
||||
},
|
||||
updateUserProfile({ profile, currentPassword }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.updateProfile({
|
||||
password: profile.password,
|
||||
oldPassword: currentPassword,
|
||||
email: profile.email,
|
||||
nickname: profile.nickname,
|
||||
defaultAccountId: profile.defaultAccountId,
|
||||
transactionEditScope: profile.transactionEditScope,
|
||||
language: profile.language,
|
||||
defaultCurrency: profile.defaultCurrency,
|
||||
firstDayOfWeek: profile.firstDayOfWeek,
|
||||
longDateFormat: profile.longDateFormat,
|
||||
shortDateFormat: profile.shortDateFormat,
|
||||
longTimeFormat: profile.longTimeFormat,
|
||||
shortTimeFormat: profile.shortTimeFormat
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to update user profile' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.result.newToken && isString(data.result.newToken)) {
|
||||
userState.updateToken(data.result.newToken);
|
||||
}
|
||||
|
||||
if (data.result.user && isObject(data.result.user)) {
|
||||
const userStore = useUserStore();
|
||||
userStore.storeUserInfo(data.result.user);
|
||||
}
|
||||
|
||||
const accountsStore = useAccountsStore();
|
||||
if (!accountsStore.accountListStateInvalid) {
|
||||
accountsStore.updateAccountListInvalidState(true);
|
||||
}
|
||||
|
||||
const overviewStore = useOverviewStore();
|
||||
if (!overviewStore.transactionOverviewStateInvalid) {
|
||||
overviewStore.updateTransactionOverviewInvalidState(true);
|
||||
}
|
||||
|
||||
const statisticsStore = useStatisticsStore();
|
||||
if (!statisticsStore.transactionStatisticsStateInvalid) {
|
||||
statisticsStore.updateTransactionStatisticsInvalidState(true);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to save user profile', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to update user profile' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
clearUserData({ password }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.clearData({
|
||||
password: password
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to clear user data' });
|
||||
return;
|
||||
}
|
||||
|
||||
const accountsStore = useAccountsStore();
|
||||
if (!accountsStore.accountListStateInvalid) {
|
||||
accountsStore.updateAccountListInvalidState(true);
|
||||
}
|
||||
|
||||
const transactionCategoriesStore = useTransactionCategoriesStore();
|
||||
if (!transactionCategoriesStore.transactionCategoryListStateInvalid) {
|
||||
transactionCategoriesStore.updateTransactionCategoryListInvalidState(true);
|
||||
}
|
||||
|
||||
const transactionTagsStore = useTransactionTagsStore();
|
||||
if (!transactionTagsStore.transactionTagListStateInvalid) {
|
||||
transactionTagsStore.updateTransactionTagListInvalidState(true);
|
||||
}
|
||||
|
||||
const overviewStore = useOverviewStore();
|
||||
if (!overviewStore.transactionOverviewStateInvalid) {
|
||||
overviewStore.updateTransactionOverviewInvalidState(true);
|
||||
}
|
||||
|
||||
const statisticsStore = useStatisticsStore();
|
||||
if (!statisticsStore.transactionStatisticsStateInvalid) {
|
||||
statisticsStore.updateTransactionStatisticsInvalidState(true);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to clear user data', error);
|
||||
|
||||
if (error && error.processed) {
|
||||
reject(error);
|
||||
} else if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else {
|
||||
reject({ message: 'Unable to clear user data' });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,119 @@
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
import { useExchangeRatesStore } from './exchangeRates.js';
|
||||
|
||||
import services from '@/lib/services.js';
|
||||
import logger from '@/lib/logger.js';
|
||||
import { isNumber } from '@/lib/common.js';
|
||||
|
||||
export const useOverviewStore = defineStore('overview', {
|
||||
state: () => ({
|
||||
transactionOverview: {},
|
||||
transactionOverviewStateInvalid: true
|
||||
}),
|
||||
actions: {
|
||||
updateTransactionOverviewInvalidState(invalidState) {
|
||||
this.transactionOverviewStateInvalid = invalidState;
|
||||
},
|
||||
resetTransactionOverview() {
|
||||
this.transactionOverview = {};
|
||||
this.transactionOverviewStateInvalid = true;
|
||||
},
|
||||
loadTransactionOverview({ defaultCurrency, dateRange, force }) {
|
||||
const self = this;
|
||||
const exchangeRatesStore = useExchangeRatesStore();
|
||||
|
||||
if (!force && !self.transactionOverviewStateInvalid) {
|
||||
return new Promise((resolve) => {
|
||||
resolve(self.transactionOverview);
|
||||
});
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getTransactionAmounts({
|
||||
today: dateRange.today,
|
||||
thisWeek: dateRange.thisWeek,
|
||||
thisMonth: dateRange.thisMonth,
|
||||
thisYear: dateRange.thisYear
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to get transaction overview' });
|
||||
return;
|
||||
}
|
||||
|
||||
const overview = data.result;
|
||||
|
||||
for (let field in overview) {
|
||||
if (!Object.prototype.hasOwnProperty.call(overview, field)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const item = overview[field];
|
||||
|
||||
if (!item.amounts || !item.amounts.length) {
|
||||
item.amounts = [];
|
||||
}
|
||||
|
||||
let totalIncomeAmount = 0;
|
||||
let totalExpenseAmount = 0;
|
||||
let hasUnCalculatedTotalIncome = false;
|
||||
let hasUnCalculatedTotalExpense = false;
|
||||
|
||||
for (let i = 0; i < item.amounts.length; i++) {
|
||||
const amount = item.amounts[i];
|
||||
|
||||
if (amount.currency !== defaultCurrency) {
|
||||
const incomeAmount = exchangeRatesStore.getExchangedAmount(amount.incomeAmount, amount.currency, defaultCurrency);
|
||||
const expenseAmount = exchangeRatesStore.getExchangedAmount(amount.expenseAmount, amount.currency, defaultCurrency);
|
||||
|
||||
if (isNumber(incomeAmount)) {
|
||||
totalIncomeAmount += Math.floor(incomeAmount);
|
||||
} else {
|
||||
hasUnCalculatedTotalIncome = true;
|
||||
}
|
||||
|
||||
if (isNumber(expenseAmount)) {
|
||||
totalExpenseAmount += Math.floor(expenseAmount);
|
||||
} else {
|
||||
hasUnCalculatedTotalExpense = true;
|
||||
}
|
||||
} else {
|
||||
totalIncomeAmount += amount.incomeAmount;
|
||||
totalExpenseAmount += amount.expenseAmount;
|
||||
}
|
||||
}
|
||||
|
||||
item.incomeAmount = totalIncomeAmount;
|
||||
item.expenseAmount = totalExpenseAmount;
|
||||
item.incompleteIncomeAmount = hasUnCalculatedTotalIncome;
|
||||
item.incompleteExpenseAmount = hasUnCalculatedTotalExpense;
|
||||
}
|
||||
|
||||
self.transactionOverview = overview;
|
||||
|
||||
if (self.transactionOverviewStateInvalid) {
|
||||
self.updateTransactionOverviewInvalidState(false);
|
||||
}
|
||||
|
||||
resolve(overview);
|
||||
}).catch(error => {
|
||||
if (force) {
|
||||
logger.error('failed to force load transaction overview', error);
|
||||
} else {
|
||||
logger.error('failed to load transaction overview', error);
|
||||
}
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to get transaction overview' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
import currencyConstants from '@/consts/currency.js';
|
||||
import datetimeConstants from '@/consts/datetime.js';
|
||||
|
||||
export const useSettingsStore = defineStore('settings', {
|
||||
state: () => ({
|
||||
defaultSetting: {
|
||||
language: '',
|
||||
currency: currencyConstants.defaultCurrency,
|
||||
firstDayOfWeek: datetimeConstants.defaultFirstDayOfWeek,
|
||||
longDateFormat: 0,
|
||||
shortDateFormat: 0,
|
||||
longTimeFormat: 0,
|
||||
shortTimeFormat: 0
|
||||
}
|
||||
}),
|
||||
actions: {
|
||||
updateLocalizedDefaultSettings({ defaultCurrency, defaultFirstDayOfWeek }) {
|
||||
this.defaultSetting.currency = defaultCurrency;
|
||||
this.defaultSetting.firstDayOfWeek = defaultFirstDayOfWeek;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,423 @@
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
import { useUserStore } from './user.js';
|
||||
import { useAccountsStore } from './account.js';
|
||||
import { useTransactionCategoriesStore } from './transactionCategory.js';
|
||||
import { useExchangeRatesStore } from './exchangeRates.js';
|
||||
|
||||
import statisticsConstants from '@/consts/statistics.js';
|
||||
import categoryConstants from '@/consts/category.js';
|
||||
import iconConstants from '@/consts/icon.js';
|
||||
import colorConstants from '@/consts/color.js';
|
||||
import services from '@/lib/services.js';
|
||||
import logger from '@/lib/logger.js';
|
||||
import { isNumber, isObject } from '@/lib/common.js';
|
||||
|
||||
function loadTransactionStatistics(state, { statistics, defaultCurrency }) {
|
||||
if (statistics && statistics.items && statistics.items.length) {
|
||||
const accountsStore = useAccountsStore();
|
||||
const transactionCategoriesStore = useTransactionCategoriesStore();
|
||||
const exchangeRatesStore = useExchangeRatesStore();
|
||||
|
||||
for (let i = 0; i < statistics.items.length; i++) {
|
||||
const item = statistics.items[i];
|
||||
|
||||
if (item.accountId) {
|
||||
item.account = accountsStore.allAccountsMap[item.accountId];
|
||||
}
|
||||
|
||||
if (item.account && item.account.parentId !== '0') {
|
||||
item.primaryAccount = accountsStore.allAccountsMap[item.account.parentId];
|
||||
} else {
|
||||
item.primaryAccount = item.account;
|
||||
}
|
||||
|
||||
if (item.categoryId) {
|
||||
item.category = transactionCategoriesStore.allTransactionCategoriesMap[item.categoryId];
|
||||
}
|
||||
|
||||
if (item.category && item.category.parentId !== '0') {
|
||||
item.primaryCategory = transactionCategoriesStore.allTransactionCategoriesMap[item.category.parentId];
|
||||
} else {
|
||||
item.primaryCategory = item.category;
|
||||
}
|
||||
|
||||
if (item.account && item.account.currency !== defaultCurrency) {
|
||||
const amount = exchangeRatesStore.getExchangedAmount(item.amount, item.account.currency, defaultCurrency);
|
||||
|
||||
if (isNumber(amount)) {
|
||||
item.amountInDefaultCurrency = Math.floor(amount);
|
||||
}
|
||||
} else if (item.account && item.account.currency === defaultCurrency) {
|
||||
item.amountInDefaultCurrency = item.amount;
|
||||
} else {
|
||||
item.amountInDefaultCurrency = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state.transactionStatistics = statistics;
|
||||
}
|
||||
|
||||
export const useStatisticsStore = defineStore('statistics', {
|
||||
state: () => ({
|
||||
transactionStatisticsFilter: {
|
||||
dateType: statisticsConstants.defaultDataRangeType,
|
||||
startTime: 0,
|
||||
endTime: 0,
|
||||
chartType: statisticsConstants.defaultChartType,
|
||||
chartDataType: statisticsConstants.defaultChartDataType,
|
||||
filterAccountIds: {},
|
||||
filterCategoryIds: {}
|
||||
},
|
||||
transactionStatistics: [],
|
||||
transactionStatisticsStateInvalid: true
|
||||
}),
|
||||
getters: {
|
||||
statisticsItemsByTransactionStatisticsData(state) {
|
||||
if (!state.transactionStatistics || !state.transactionStatistics.items) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const allDataItems = {};
|
||||
let totalAmount = 0;
|
||||
let totalNonNegativeAmount = 0;
|
||||
|
||||
for (let i = 0; i < state.transactionStatistics.items.length; i++) {
|
||||
const item = state.transactionStatistics.items[i];
|
||||
|
||||
if (!item.primaryAccount || !item.account || !item.primaryCategory || !item.category) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (state.transactionStatisticsFilter.chartDataType === statisticsConstants.allChartDataTypes.ExpenseByAccount.type ||
|
||||
state.transactionStatisticsFilter.chartDataType === statisticsConstants.allChartDataTypes.ExpenseByPrimaryCategory.type ||
|
||||
state.transactionStatisticsFilter.chartDataType === statisticsConstants.allChartDataTypes.ExpenseBySecondaryCategory.type) {
|
||||
if (item.category.type !== categoryConstants.allCategoryTypes.Expense) {
|
||||
continue;
|
||||
}
|
||||
} else if (state.transactionStatisticsFilter.chartDataType === statisticsConstants.allChartDataTypes.IncomeByAccount.type ||
|
||||
state.transactionStatisticsFilter.chartDataType === statisticsConstants.allChartDataTypes.IncomeByPrimaryCategory.type ||
|
||||
state.transactionStatisticsFilter.chartDataType === statisticsConstants.allChartDataTypes.IncomeBySecondaryCategory.type) {
|
||||
if (item.category.type !== categoryConstants.allCategoryTypes.Income) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (state.transactionStatisticsFilter.filterAccountIds && state.transactionStatisticsFilter.filterAccountIds[item.account.id]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (state.transactionStatisticsFilter.filterCategoryIds && state.transactionStatisticsFilter.filterCategoryIds[item.category.id]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (state.transactionStatisticsFilter.chartDataType === statisticsConstants.allChartDataTypes.ExpenseByAccount.type ||
|
||||
state.transactionStatisticsFilter.chartDataType === statisticsConstants.allChartDataTypes.IncomeByAccount.type) {
|
||||
if (isNumber(item.amountInDefaultCurrency)) {
|
||||
let data = allDataItems[item.account.id];
|
||||
|
||||
if (data) {
|
||||
data.totalAmount += item.amountInDefaultCurrency;
|
||||
} else {
|
||||
data = {
|
||||
name: item.account.name,
|
||||
type: 'account',
|
||||
id: item.account.id,
|
||||
icon: item.account.icon || iconConstants.defaultAccountIcon.icon,
|
||||
color: item.account.color || colorConstants.defaultAccountColor,
|
||||
hidden: item.primaryAccount.hidden || item.account.hidden,
|
||||
displayOrders: [item.primaryAccount.category, item.primaryAccount.displayOrder, item.account.displayOrder],
|
||||
totalAmount: item.amountInDefaultCurrency
|
||||
}
|
||||
}
|
||||
|
||||
totalAmount += item.amountInDefaultCurrency;
|
||||
|
||||
if (item.amountInDefaultCurrency > 0) {
|
||||
totalNonNegativeAmount += item.amountInDefaultCurrency;
|
||||
}
|
||||
|
||||
allDataItems[item.account.id] = data;
|
||||
}
|
||||
} else if (state.transactionStatisticsFilter.chartDataType === statisticsConstants.allChartDataTypes.ExpenseByPrimaryCategory.type ||
|
||||
state.transactionStatisticsFilter.chartDataType === statisticsConstants.allChartDataTypes.IncomeByPrimaryCategory.type) {
|
||||
if (isNumber(item.amountInDefaultCurrency)) {
|
||||
let data = allDataItems[item.primaryCategory.id];
|
||||
|
||||
if (data) {
|
||||
data.totalAmount += item.amountInDefaultCurrency;
|
||||
} else {
|
||||
data = {
|
||||
name: item.primaryCategory.name,
|
||||
type: 'category',
|
||||
id: item.primaryCategory.id,
|
||||
icon: item.primaryCategory.icon || iconConstants.defaultCategoryIcon.icon,
|
||||
color: item.primaryCategory.color || colorConstants.defaultCategoryColor,
|
||||
hidden: item.primaryCategory.hidden,
|
||||
displayOrders: [item.primaryCategory.type, item.primaryCategory.displayOrder],
|
||||
totalAmount: item.amountInDefaultCurrency
|
||||
}
|
||||
}
|
||||
|
||||
totalAmount += item.amountInDefaultCurrency;
|
||||
|
||||
if (item.amountInDefaultCurrency > 0) {
|
||||
totalNonNegativeAmount += item.amountInDefaultCurrency;
|
||||
}
|
||||
|
||||
allDataItems[item.primaryCategory.id] = data;
|
||||
}
|
||||
} else if (state.transactionStatisticsFilter.chartDataType === statisticsConstants.allChartDataTypes.ExpenseBySecondaryCategory.type ||
|
||||
state.transactionStatisticsFilter.chartDataType === statisticsConstants.allChartDataTypes.IncomeBySecondaryCategory.type) {
|
||||
if (isNumber(item.amountInDefaultCurrency)) {
|
||||
let data = allDataItems[item.category.id];
|
||||
|
||||
if (data) {
|
||||
data.totalAmount += item.amountInDefaultCurrency;
|
||||
} else {
|
||||
data = {
|
||||
name: item.category.name,
|
||||
type: 'category',
|
||||
id: item.category.id,
|
||||
icon: item.category.icon || iconConstants.defaultCategoryIcon.icon,
|
||||
color: item.category.color || colorConstants.defaultCategoryColor,
|
||||
hidden: item.primaryCategory.hidden || item.category.hidden,
|
||||
displayOrders: [item.primaryCategory.type, item.primaryCategory.displayOrder, item.category.displayOrder],
|
||||
totalAmount: item.amountInDefaultCurrency
|
||||
}
|
||||
}
|
||||
|
||||
totalAmount += item.amountInDefaultCurrency;
|
||||
|
||||
if (item.amountInDefaultCurrency > 0) {
|
||||
totalNonNegativeAmount += item.amountInDefaultCurrency;
|
||||
}
|
||||
|
||||
allDataItems[item.category.id] = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
totalAmount: totalAmount,
|
||||
totalNonNegativeAmount: totalNonNegativeAmount,
|
||||
items: allDataItems
|
||||
}
|
||||
},
|
||||
statisticsItemsByAccountsData(state) {
|
||||
const userStore = useUserStore();
|
||||
const accountsStore = useAccountsStore();
|
||||
const exchangeRatesStore = useExchangeRatesStore();
|
||||
|
||||
if (!accountsStore.allPlainAccounts) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const allDataItems = {};
|
||||
let totalAmount = 0;
|
||||
let totalNonNegativeAmount = 0;
|
||||
|
||||
for (let i = 0; i < accountsStore.allPlainAccounts.length; i++) {
|
||||
const account = accountsStore.allPlainAccounts[i];
|
||||
|
||||
if (state.transactionStatisticsFilter.chartDataType === statisticsConstants.allChartDataTypes.AccountTotalAssets.type) {
|
||||
if (!account.isAsset) {
|
||||
continue;
|
||||
}
|
||||
} else if (state.transactionStatisticsFilter.chartDataType === statisticsConstants.allChartDataTypes.AccountTotalLiabilities.type) {
|
||||
if (!account.isLiability) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (state.transactionStatisticsFilter.filterAccountIds && state.transactionStatisticsFilter.filterAccountIds[account.id]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let primaryAccount = accountsStore.allAccountsMap[account.parentId];
|
||||
|
||||
if (!primaryAccount) {
|
||||
primaryAccount = account;
|
||||
}
|
||||
|
||||
let amount = account.balance;
|
||||
|
||||
if (account.currency !== userStore.currentUserDefaultCurrency) {
|
||||
amount = Math.floor(exchangeRatesStore.getExchangedAmount(amount, account.currency, userStore.currentUserDefaultCurrency));
|
||||
|
||||
if (!isNumber(amount)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (account.isLiability) {
|
||||
amount = -amount;
|
||||
}
|
||||
|
||||
const data = {
|
||||
name: account.name,
|
||||
type: 'account',
|
||||
id: account.id,
|
||||
icon: account.icon || iconConstants.defaultAccountIcon.icon,
|
||||
color: account.color || colorConstants.defaultAccountColor,
|
||||
hidden: primaryAccount.hidden || account.hidden,
|
||||
displayOrders: [primaryAccount.category, primaryAccount.displayOrder, account.displayOrder],
|
||||
totalAmount: amount
|
||||
};
|
||||
|
||||
totalAmount += amount;
|
||||
|
||||
if (amount > 0) {
|
||||
totalNonNegativeAmount += amount;
|
||||
}
|
||||
|
||||
allDataItems[account.id] = data;
|
||||
}
|
||||
|
||||
return {
|
||||
totalAmount: totalAmount,
|
||||
totalNonNegativeAmount: totalNonNegativeAmount,
|
||||
items: allDataItems
|
||||
}
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
updateTransactionStatisticsInvalidState(invalidState) {
|
||||
this.transactionStatisticsStateInvalid = invalidState;
|
||||
},
|
||||
resetTransactionStatistics() {
|
||||
this.transactionStatisticsFilter.dateType = statisticsConstants.defaultDataRangeType;
|
||||
this.transactionStatisticsFilter.startTime = 0;
|
||||
this.transactionStatisticsFilter.endTime = 0;
|
||||
this.transactionStatisticsFilter.chartType = statisticsConstants.defaultChartType;
|
||||
this.transactionStatisticsFilter.chartDataType = statisticsConstants.defaultChartDataType;
|
||||
this.transactionStatisticsFilter.filterAccountIds = {};
|
||||
this.transactionStatisticsFilter.filterCategoryIds = {};
|
||||
this.transactionStatistics = {};
|
||||
this.transactionStatisticsStateInvalid = true;
|
||||
},
|
||||
initTransactionStatisticsFilter(filter) {
|
||||
if (filter && isNumber(filter.dateType)) {
|
||||
this.transactionStatisticsFilter.dateType = filter.dateType;
|
||||
} else {
|
||||
this.transactionStatisticsFilter.dateType = statisticsConstants.defaultDataRangeType;
|
||||
}
|
||||
|
||||
if (filter && isNumber(filter.startTime)) {
|
||||
this.transactionStatisticsFilter.startTime = filter.startTime;
|
||||
} else {
|
||||
this.transactionStatisticsFilter.startTime = 0;
|
||||
}
|
||||
|
||||
if (filter && isNumber(filter.endTime)) {
|
||||
this.transactionStatisticsFilter.endTime = filter.endTime;
|
||||
} else {
|
||||
this.transactionStatisticsFilter.endTime = 0;
|
||||
}
|
||||
|
||||
if (filter && isNumber(filter.chartType)) {
|
||||
this.transactionStatisticsFilter.chartType = filter.chartType;
|
||||
} else {
|
||||
this.transactionStatisticsFilter.chartType = statisticsConstants.defaultChartType;
|
||||
}
|
||||
|
||||
if (filter && isNumber(filter.chartDataType)) {
|
||||
this.transactionStatisticsFilter.chartDataType = filter.chartDataType;
|
||||
} else {
|
||||
this.transactionStatisticsFilter.chartDataType = statisticsConstants.defaultChartDataType;
|
||||
}
|
||||
|
||||
if (filter && isObject(filter.filterAccountIds)) {
|
||||
this.transactionStatisticsFilter.filterAccountIds = filter.filterAccountIds;
|
||||
} else {
|
||||
this.transactionStatisticsFilter.filterAccountIds = {};
|
||||
}
|
||||
|
||||
if (filter && isObject(filter.filterCategoryIds)) {
|
||||
this.transactionStatisticsFilter.filterCategoryIds = filter.filterCategoryIds;
|
||||
} else {
|
||||
this.transactionStatisticsFilter.filterCategoryIds = {};
|
||||
}
|
||||
|
||||
if (filter && isNumber(filter.sortingType)) {
|
||||
this.transactionStatisticsFilter.sortingType = filter.sortingType;
|
||||
} else {
|
||||
this.transactionStatisticsFilter.sortingType = statisticsConstants.defaultSortingType;
|
||||
}
|
||||
},
|
||||
updateTransactionStatisticsFilter(filter) {
|
||||
if (filter && isNumber(filter.dateType)) {
|
||||
this.transactionStatisticsFilter.dateType = filter.dateType;
|
||||
}
|
||||
|
||||
if (filter && isNumber(filter.startTime)) {
|
||||
this.transactionStatisticsFilter.startTime = filter.startTime;
|
||||
}
|
||||
|
||||
if (filter && isNumber(filter.endTime)) {
|
||||
this.transactionStatisticsFilter.endTime = filter.endTime;
|
||||
}
|
||||
|
||||
if (filter && isNumber(filter.chartType)) {
|
||||
this.transactionStatisticsFilter.chartType = filter.chartType;
|
||||
}
|
||||
|
||||
if (filter && isNumber(filter.chartDataType)) {
|
||||
this.transactionStatisticsFilter.chartDataType = filter.chartDataType;
|
||||
}
|
||||
|
||||
if (filter && isObject(filter.filterAccountIds)) {
|
||||
this.transactionStatisticsFilter.filterAccountIds = filter.filterAccountIds;
|
||||
}
|
||||
|
||||
if (filter && isObject(filter.filterCategoryIds)) {
|
||||
this.transactionStatisticsFilter.filterCategoryIds = filter.filterCategoryIds;
|
||||
}
|
||||
|
||||
if (filter && isNumber(filter.sortingType)) {
|
||||
this.transactionStatisticsFilter.sortingType = filter.sortingType;
|
||||
}
|
||||
},
|
||||
loadTransactionStatistics({ defaultCurrency }) {
|
||||
const self = this;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getTransactionStatistics({
|
||||
startTime: self.transactionStatisticsFilter.startTime,
|
||||
endTime: self.transactionStatisticsFilter.endTime
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to get transaction statistics' });
|
||||
return;
|
||||
}
|
||||
|
||||
loadTransactionStatistics(self, {
|
||||
statistics: data.result,
|
||||
defaultCurrency: defaultCurrency
|
||||
});
|
||||
|
||||
if (self.transactionStatisticsStateInvalid) {
|
||||
self.updateTransactionStatisticsInvalidState(false);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to get transaction statistics', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to get transaction statistics' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,115 @@
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
import { useUserStore } from './user.js';
|
||||
|
||||
import userState from '@/lib/userstate.js';
|
||||
import services from '@/lib/services.js';
|
||||
import logger from '@/lib/logger.js';
|
||||
import { isObject } from '@/lib/common.js';
|
||||
|
||||
export const useTokensStore = defineStore('tokens', {
|
||||
actions: {
|
||||
getAllTokens() {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getTokens().then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to get session list' });
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to load token list', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to get session list' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
refreshTokenAndRevokeOldToken() {
|
||||
const self = this;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
services.refreshToken().then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (data && data.success && data.result && data.result.newToken) {
|
||||
userState.updateToken(data.result.newToken);
|
||||
|
||||
if (data.result.user && isObject(data.result.user)) {
|
||||
const userStore = useUserStore();
|
||||
userStore.storeUserInfo(data.result.user);
|
||||
}
|
||||
|
||||
if (data.result.oldTokenId) {
|
||||
self.revokeToken({
|
||||
tokenId: data.result.oldTokenId,
|
||||
ignoreError: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
});
|
||||
});
|
||||
},
|
||||
revokeToken({ tokenId, ignoreError }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.revokeToken({
|
||||
tokenId: tokenId,
|
||||
ignoreError: !!ignoreError
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to logout from this session' });
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to revoke token', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to logout from this session' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
revokeAllTokens() {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.revokeAllTokens().then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to logout all other sessions' });
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to revoke all tokens', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to logout all other sessions' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,618 @@
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
import { useAccountsStore } from './account.js';
|
||||
import { useTransactionCategoriesStore } from './transactionCategory.js';
|
||||
import { useOverviewStore } from './overview.js';
|
||||
import { useStatisticsStore } from './statistics.js';
|
||||
import { useExchangeRatesStore } from './exchangeRates.js';
|
||||
|
||||
import datetimeConstants from '@/consts/datetime.js';
|
||||
import transactionConstants from '@/consts/transaction.js';
|
||||
import services from '@/lib/services.js';
|
||||
import logger from '@/lib/logger.js';
|
||||
import { isNumber, isString } from '@/lib/common.js';
|
||||
import {
|
||||
getTimezoneOffsetMinutes,
|
||||
parseDateFromUnixTime,
|
||||
getYear,
|
||||
getMonth,
|
||||
getYearAndMonth,
|
||||
getDay,
|
||||
getDayOfWeekName
|
||||
} from '@/lib/datetime.js';
|
||||
|
||||
const emptyTransactionResult = {
|
||||
items: [],
|
||||
transactionsNextTimeId: 0
|
||||
};
|
||||
|
||||
function loadTransactionList(state, exchangeRatesStore, { transactions, reload, autoExpand, defaultCurrency }) {
|
||||
if (reload) {
|
||||
state.transactions = [];
|
||||
}
|
||||
|
||||
if (transactions.items && transactions.items.length) {
|
||||
const currentUtcOffset = getTimezoneOffsetMinutes();
|
||||
let currentMonthListIndex = -1;
|
||||
let currentMonthList = null;
|
||||
|
||||
for (let i = 0; i < transactions.items.length; i++) {
|
||||
const item = transactions.items[i];
|
||||
fillTransactionObject(state, item, currentUtcOffset);
|
||||
|
||||
const transactionTime = parseDateFromUnixTime(item.time, item.utcOffset, currentUtcOffset);
|
||||
const transactionYear = getYear(transactionTime);
|
||||
const transactionMonth = getMonth(transactionTime);
|
||||
const transactionYearMonth = getYearAndMonth(transactionTime);
|
||||
|
||||
if (currentMonthList && currentMonthList.year === transactionYear && currentMonthList.month === transactionMonth) {
|
||||
currentMonthList.items.push(Object.freeze(item));
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let j = currentMonthListIndex + 1; j < state.transactions.length; j++) {
|
||||
if (state.transactions[j].year === transactionYear && state.transactions[j].month === transactionMonth) {
|
||||
currentMonthListIndex = j;
|
||||
currentMonthList = state.transactions[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!currentMonthList || currentMonthList.year !== transactionYear || currentMonthList.month !== transactionMonth) {
|
||||
calculateMonthTotalAmount(state, exchangeRatesStore, currentMonthList, defaultCurrency, state.transactionsFilter.accountId, false);
|
||||
|
||||
state.transactions.push({
|
||||
year: transactionYear,
|
||||
month: transactionMonth,
|
||||
yearMonth: transactionYearMonth,
|
||||
opened: autoExpand,
|
||||
items: []
|
||||
});
|
||||
|
||||
currentMonthListIndex = state.transactions.length - 1;
|
||||
currentMonthList = state.transactions[state.transactions.length - 1];
|
||||
}
|
||||
|
||||
currentMonthList.items.push(Object.freeze(item));
|
||||
calculateMonthTotalAmount(state, exchangeRatesStore, currentMonthList, defaultCurrency, state.transactionsFilter.accountId, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (transactions.nextTimeSequenceId) {
|
||||
state.transactionsNextTimeId = transactions.nextTimeSequenceId;
|
||||
} else {
|
||||
calculateMonthTotalAmount(state, exchangeRatesStore, state.transactions[state.transactions.length - 1], defaultCurrency, state.transactionsFilter.accountId, false);
|
||||
state.transactionsNextTimeId = -1;
|
||||
}
|
||||
}
|
||||
|
||||
function updateTransactionInTransactionList(state, exchangeRatesStore, { transaction, defaultCurrency }) {
|
||||
const currentUtcOffset = getTimezoneOffsetMinutes();
|
||||
const transactionTime = parseDateFromUnixTime(transaction.time, transaction.utcOffset, currentUtcOffset);
|
||||
const transactionYear = getYear(transactionTime);
|
||||
const transactionMonth = getMonth(transactionTime);
|
||||
|
||||
for (let i = 0; i < state.transactions.length; i++) {
|
||||
const transactionMonthList = state.transactions[i];
|
||||
|
||||
if (!transactionMonthList.items) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let j = 0; j < transactionMonthList.items.length; j++) {
|
||||
if (transactionMonthList.items[j].id === transaction.id) {
|
||||
fillTransactionObject(state, transaction, currentUtcOffset);
|
||||
|
||||
if (transactionYear !== transactionMonthList.year ||
|
||||
transactionMonth !== transactionMonthList.month ||
|
||||
transaction.day !== transactionMonthList.items[j].day) {
|
||||
state.transactionListStateInvalid = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((state.transactionsFilter.categoryId && state.transactionsFilter.categoryId !== '0' && state.transactionsFilter.categoryId !== transaction.categoryId) ||
|
||||
(state.transactionsFilter.accountId && state.transactionsFilter.accountId !== '0' &&
|
||||
state.transactionsFilter.accountId !== transaction.sourceAccountId &&
|
||||
state.transactionsFilter.accountId !== transaction.destinationAccountId &&
|
||||
(!transaction.sourceAccount || state.transactionsFilter.accountId !== transaction.sourceAccount.parentId) &&
|
||||
(!transaction.destinationAccount || state.transactionsFilter.accountId !== transaction.destinationAccount.parentId)
|
||||
)
|
||||
) {
|
||||
transactionMonthList.items.splice(j, 1);
|
||||
} else {
|
||||
transactionMonthList.items.splice(j, 1, transaction);
|
||||
}
|
||||
|
||||
if (transactionMonthList.items.length < 1) {
|
||||
state.transactions.splice(i, 1);
|
||||
} else {
|
||||
calculateMonthTotalAmount(state, exchangeRatesStore, transactionMonthList, defaultCurrency, state.transactionsFilter.accountId, i >= state.transactions.length - 1 && state.transactionsNextTimeId > 0);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function removeTransactionFromTransactionList(state, exchangeRatesStore, { transaction, defaultCurrency }) {
|
||||
for (let i = 0; i < state.transactions.length; i++) {
|
||||
const transactionMonthList = state.transactions[i];
|
||||
|
||||
if (!transactionMonthList.items ||
|
||||
transactionMonthList.items[0].time < transaction.time ||
|
||||
transactionMonthList.items[transactionMonthList.items.length - 1].time > transaction.time) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let j = 0; j < transactionMonthList.items.length; j++) {
|
||||
if (transactionMonthList.items[j].id === transaction.id) {
|
||||
transactionMonthList.items.splice(j, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (transactionMonthList.items.length < 1) {
|
||||
state.transactions.splice(i, 1);
|
||||
} else {
|
||||
calculateMonthTotalAmount(state, exchangeRatesStore, transactionMonthList, defaultCurrency, state.transactionsFilter.accountId, i >= state.transactions.length - 1 && state.transactionsNextTimeId > 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function calculateMonthTotalAmount(state, exchangeRatesStore, transactionMonthList, defaultCurrency, accountId, incomplete) {
|
||||
if (!transactionMonthList) {
|
||||
return;
|
||||
}
|
||||
|
||||
let totalExpense = 0;
|
||||
let totalIncome = 0;
|
||||
let hasUnCalculatedTotalExpense = false;
|
||||
let hasUnCalculatedTotalIncome = false;
|
||||
|
||||
for (let i = 0; i < transactionMonthList.items.length; i++) {
|
||||
const transaction = transactionMonthList.items[i];
|
||||
|
||||
let amount = transaction.sourceAmount;
|
||||
let account = transaction.sourceAccount;
|
||||
|
||||
if (accountId && transaction.destinationAccount && (transaction.destinationAccount.id === accountId || transaction.destinationAccount.parentId === accountId)) {
|
||||
amount = transaction.destinationAmount;
|
||||
account = transaction.destinationAccount;
|
||||
}
|
||||
|
||||
if (!account) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (account.currency !== defaultCurrency) {
|
||||
const balance = exchangeRatesStore.getExchangedAmount(amount, account.currency, defaultCurrency);
|
||||
|
||||
if (!isNumber(balance)) {
|
||||
if (transaction.type === transactionConstants.allTransactionTypes.Expense) {
|
||||
hasUnCalculatedTotalExpense = true;
|
||||
} else if (transaction.type === transactionConstants.allTransactionTypes.Income) {
|
||||
hasUnCalculatedTotalIncome = true;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
amount = Math.floor(balance);
|
||||
}
|
||||
|
||||
if (transaction.type === transactionConstants.allTransactionTypes.Expense) {
|
||||
totalExpense += amount;
|
||||
} else if (transaction.type === transactionConstants.allTransactionTypes.Income) {
|
||||
totalIncome += amount;
|
||||
} else if (transaction.type === transactionConstants.allTransactionTypes.Transfer && accountId) {
|
||||
if (accountId === transaction.sourceAccountId) {
|
||||
totalExpense += amount;
|
||||
} else if (accountId === transaction.destinationAccountId) {
|
||||
totalIncome += amount;
|
||||
} else if (transaction.sourceAccount && accountId === transaction.sourceAccount.parentId &&
|
||||
transaction.destinationAccount && accountId === transaction.destinationAccount.parentId) {
|
||||
// Do Nothing
|
||||
} else if (transaction.sourceAccount && accountId === transaction.sourceAccount.parentId) {
|
||||
totalExpense += amount;
|
||||
} else if (transaction.destinationAccount && accountId === transaction.destinationAccount.parentId) {
|
||||
totalIncome += amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
transactionMonthList.totalAmount = {
|
||||
expense: totalExpense,
|
||||
incompleteExpense: incomplete || hasUnCalculatedTotalExpense,
|
||||
income: totalIncome,
|
||||
incompleteIncome: incomplete || hasUnCalculatedTotalIncome
|
||||
};
|
||||
}
|
||||
|
||||
function fillTransactionObject(state, transaction, currentUtcOffset) {
|
||||
if (!transaction) {
|
||||
return;
|
||||
}
|
||||
|
||||
const accountsStore = useAccountsStore();
|
||||
const transactionCategoriesStore = useTransactionCategoriesStore();
|
||||
const transactionTime = parseDateFromUnixTime(transaction.time, transaction.utcOffset, currentUtcOffset);
|
||||
|
||||
transaction.day = getDay(transactionTime);
|
||||
transaction.dayOfWeek = getDayOfWeekName(transactionTime);
|
||||
|
||||
if (transaction.sourceAccountId) {
|
||||
transaction.sourceAccount = accountsStore.allAccountsMap[transaction.sourceAccountId];
|
||||
}
|
||||
|
||||
if (transaction.destinationAccountId) {
|
||||
transaction.destinationAccount = accountsStore.allAccountsMap[transaction.destinationAccountId];
|
||||
}
|
||||
|
||||
if (transaction.categoryId) {
|
||||
transaction.category = transactionCategoriesStore.allTransactionCategoriesMap[transaction.categoryId];
|
||||
}
|
||||
|
||||
return transaction;
|
||||
}
|
||||
|
||||
export const useTransactionsStore = defineStore('transactions', {
|
||||
state: () => ({
|
||||
transactionsFilter: {
|
||||
dateType: datetimeConstants.allDateRanges.All.type,
|
||||
maxTime: 0,
|
||||
minTime: 0,
|
||||
type: 0,
|
||||
categoryId: '0',
|
||||
accountId: '0',
|
||||
keyword: ''
|
||||
},
|
||||
transactions: [],
|
||||
transactionsNextTimeId: 0,
|
||||
transactionListStateInvalid: true,
|
||||
}),
|
||||
getters: {
|
||||
noTransaction(state) {
|
||||
for (let i = 0; i < state.transactions.length; i++) {
|
||||
const transactionMonthList = state.transactions[i];
|
||||
|
||||
for (let j = 0; j < transactionMonthList.items.length; j++) {
|
||||
if (transactionMonthList.items[j]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
hasMoreTransaction(state) {
|
||||
return state.transactionsNextTimeId > 0;
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
updateTransactionListInvalidState(invalidState) {
|
||||
this.transactionListStateInvalid = invalidState;
|
||||
},
|
||||
resetTransactions() {
|
||||
this.transactionsFilter.dateType = datetimeConstants.allDateRanges.All.type;
|
||||
this.transactionsFilter.maxTime = 0;
|
||||
this.transactionsFilter.minTime = 0;
|
||||
this.transactionsFilter.type = 0;
|
||||
this.transactionsFilter.categoryId = '0';
|
||||
this.transactionsFilter.accountId = '0';
|
||||
this.transactionsFilter.keyword = '';
|
||||
this.transactions = [];
|
||||
this.transactionsNextTimeId = 0;
|
||||
this.transactionListStateInvalid = true;
|
||||
},
|
||||
initTransactionListFilter(filter) {
|
||||
if (filter && isNumber(filter.dateType)) {
|
||||
this.transactionsFilter.dateType = filter.dateType;
|
||||
} else {
|
||||
this.transactionsFilter.dateType = datetimeConstants.allDateRanges.All.type;
|
||||
}
|
||||
|
||||
if (filter && isNumber(filter.maxTime)) {
|
||||
this.transactionsFilter.maxTime = filter.maxTime;
|
||||
} else {
|
||||
this.transactionsFilter.maxTime = 0;
|
||||
}
|
||||
|
||||
if (filter && isNumber(filter.minTime)) {
|
||||
this.transactionsFilter.minTime = filter.minTime;
|
||||
} else {
|
||||
this.transactionsFilter.minTime = 0;
|
||||
}
|
||||
|
||||
if (filter && isNumber(filter.type)) {
|
||||
this.transactionsFilter.type = filter.type;
|
||||
} else {
|
||||
this.transactionsFilter.type = 0;
|
||||
}
|
||||
|
||||
if (filter && isString(filter.categoryId)) {
|
||||
this.transactionsFilter.categoryId = filter.categoryId;
|
||||
} else {
|
||||
this.transactionsFilter.categoryId = '0';
|
||||
}
|
||||
|
||||
if (filter && isString(filter.accountId)) {
|
||||
this.transactionsFilter.accountId = filter.accountId;
|
||||
} else {
|
||||
this.transactionsFilter.accountId = '0';
|
||||
}
|
||||
|
||||
if (filter && isString(filter.keyword)) {
|
||||
this.transactionsFilter.keyword = filter.keyword;
|
||||
} else {
|
||||
this.transactionsFilter.keyword = '';
|
||||
}
|
||||
},
|
||||
updateTransactionListFilter(filter) {
|
||||
if (filter && isNumber(filter.dateType)) {
|
||||
this.transactionsFilter.dateType = filter.dateType;
|
||||
}
|
||||
|
||||
if (filter && isNumber(filter.maxTime)) {
|
||||
this.transactionsFilter.maxTime = filter.maxTime;
|
||||
}
|
||||
|
||||
if (filter && isNumber(filter.minTime)) {
|
||||
this.transactionsFilter.minTime = filter.minTime;
|
||||
}
|
||||
|
||||
if (filter && isNumber(filter.type)) {
|
||||
this.transactionsFilter.type = filter.type;
|
||||
}
|
||||
|
||||
if (filter && isString(filter.categoryId)) {
|
||||
this.transactionsFilter.categoryId = filter.categoryId;
|
||||
}
|
||||
|
||||
if (filter && isString(filter.accountId)) {
|
||||
this.transactionsFilter.accountId = filter.accountId;
|
||||
}
|
||||
|
||||
if (filter && isString(filter.keyword)) {
|
||||
this.transactionsFilter.keyword = filter.keyword;
|
||||
}
|
||||
},
|
||||
loadTransactions({ reload, autoExpand, defaultCurrency }) {
|
||||
const self = this;
|
||||
const exchangeRatesStore = useExchangeRatesStore();
|
||||
let actualMaxTime = self.transactionsNextTimeId;
|
||||
|
||||
if (reload && self.transactionsFilter.maxTime > 0) {
|
||||
actualMaxTime = self.transactionsFilter.maxTime * 1000 + 999;
|
||||
} else if (reload && self.transactionsFilter.maxTime <= 0) {
|
||||
actualMaxTime = 0;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getTransactions({
|
||||
maxTime: actualMaxTime,
|
||||
minTime: self.transactionsFilter.minTime * 1000,
|
||||
type: self.transactionsFilter.type,
|
||||
categoryId: self.transactionsFilter.categoryId,
|
||||
accountId: self.transactionsFilter.accountId,
|
||||
keyword: self.transactionsFilter.keyword
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
if (reload) {
|
||||
loadTransactionList(self, exchangeRatesStore, {
|
||||
transactions: emptyTransactionResult,
|
||||
reload: reload,
|
||||
autoExpand: autoExpand,
|
||||
defaultCurrency: defaultCurrency
|
||||
});
|
||||
|
||||
if (!self.transactionListStateInvalid) {
|
||||
self.updateTransactionListInvalidState(true);
|
||||
}
|
||||
}
|
||||
|
||||
reject({ message: 'Unable to get transaction list' });
|
||||
return;
|
||||
}
|
||||
|
||||
loadTransactionList(self, exchangeRatesStore, {
|
||||
transactions: data.result,
|
||||
reload: reload,
|
||||
autoExpand: autoExpand,
|
||||
defaultCurrency: defaultCurrency
|
||||
});
|
||||
|
||||
if (reload) {
|
||||
if (self.transactionListStateInvalid) {
|
||||
self.updateTransactionListInvalidState(false);
|
||||
}
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to load transaction list', error);
|
||||
|
||||
if (reload) {
|
||||
loadTransactionList(self, exchangeRatesStore, {
|
||||
transactions: emptyTransactionResult,
|
||||
reload: reload,
|
||||
autoExpand: autoExpand,
|
||||
defaultCurrency: defaultCurrency
|
||||
});
|
||||
|
||||
if (!self.transactionListStateInvalid) {
|
||||
self.updateTransactionListInvalidState(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to get transaction list' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
getTransaction({ transactionId }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getTransaction({
|
||||
id: transactionId
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to get transaction' });
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to load transaction info', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to get transaction' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
saveTransaction({ transaction, defaultCurrency }) {
|
||||
const self = this;
|
||||
const exchangeRatesStore = useExchangeRatesStore();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let promise = null;
|
||||
|
||||
if (!transaction.id) {
|
||||
promise = services.addTransaction(transaction);
|
||||
} else {
|
||||
promise = services.modifyTransaction(transaction);
|
||||
}
|
||||
|
||||
promise.then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
if (!transaction.id) {
|
||||
reject({ message: 'Unable to add transaction' });
|
||||
} else {
|
||||
reject({ message: 'Unable to save transaction' });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!transaction.id) {
|
||||
if (!self.transactionListStateInvalid) {
|
||||
self.updateTransactionListInvalidState(true);
|
||||
}
|
||||
} else {
|
||||
updateTransactionInTransactionList(self, exchangeRatesStore, {
|
||||
transaction: data.result,
|
||||
defaultCurrency: defaultCurrency
|
||||
});
|
||||
}
|
||||
|
||||
const accountsStore = useAccountsStore();
|
||||
if (!accountsStore.accountListStateInvalid) {
|
||||
accountsStore.updateAccountListInvalidState(true);
|
||||
}
|
||||
|
||||
const overviewStore = useOverviewStore();
|
||||
if (!overviewStore.transactionOverviewStateInvalid) {
|
||||
overviewStore.updateTransactionOverviewInvalidState(true);
|
||||
}
|
||||
|
||||
const statisticsStore = useStatisticsStore();
|
||||
if (!statisticsStore.transactionStatisticsStateInvalid) {
|
||||
statisticsStore.updateTransactionStatisticsInvalidState(true);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to save transaction', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
if (!transaction.id) {
|
||||
reject({ message: 'Unable to add transaction' });
|
||||
} else {
|
||||
reject({ message: 'Unable to save transaction' });
|
||||
}
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
deleteTransaction({ transaction, defaultCurrency, beforeResolve }) {
|
||||
const self = this;
|
||||
const exchangeRatesStore = useExchangeRatesStore();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.deleteTransaction({
|
||||
id: transaction.id
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to delete this transaction' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (beforeResolve) {
|
||||
beforeResolve(() => {
|
||||
removeTransactionFromTransactionList(self, exchangeRatesStore, {
|
||||
transaction: transaction,
|
||||
defaultCurrency: defaultCurrency
|
||||
});
|
||||
});
|
||||
} else {
|
||||
removeTransactionFromTransactionList(self, exchangeRatesStore, {
|
||||
transaction: transaction,
|
||||
defaultCurrency: defaultCurrency
|
||||
});
|
||||
}
|
||||
|
||||
const accountsStore = useAccountsStore();
|
||||
if (!accountsStore.accountListStateInvalid) {
|
||||
accountsStore.updateAccountListInvalidState(true);
|
||||
}
|
||||
|
||||
const overviewStore = useOverviewStore();
|
||||
if (!overviewStore.transactionOverviewStateInvalid) {
|
||||
overviewStore.updateTransactionOverviewInvalidState(true);
|
||||
}
|
||||
|
||||
const statisticsStore = useStatisticsStore();
|
||||
if (!statisticsStore.transactionStatisticsStateInvalid) {
|
||||
statisticsStore.updateTransactionStatisticsInvalidState(true);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to delete transaction', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to delete this transaction' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
collapseMonthInTransactionList({ month, collapse }) {
|
||||
if (month) {
|
||||
month.opened = !collapse;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,483 @@
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
import categoryConstants from '@/consts/category.js';
|
||||
import services from '@/lib/services.js';
|
||||
import logger from '@/lib/logger.js';
|
||||
|
||||
function loadTransactionCategoryList(state, allCategories) {
|
||||
state.allTransactionCategories = allCategories;
|
||||
state.allTransactionCategoriesMap = {};
|
||||
|
||||
for (let categoryType in allCategories) {
|
||||
if (!Object.prototype.hasOwnProperty.call(allCategories, categoryType)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const categories = allCategories[categoryType];
|
||||
|
||||
for (let i = 0; i < categories.length; i++) {
|
||||
const category = categories[i];
|
||||
state.allTransactionCategoriesMap[category.id] = category;
|
||||
|
||||
for (let j = 0; j < category.subCategories.length; j++) {
|
||||
const subCategory = category.subCategories[j];
|
||||
state.allTransactionCategoriesMap[subCategory.id] = subCategory;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addCategoryToTransactionCategoryList(state, category) {
|
||||
let categoryList = null;
|
||||
|
||||
if (!category.parentId || category.parentId === '0') {
|
||||
categoryList = state.allTransactionCategories[category.type];
|
||||
} else if (state.allTransactionCategoriesMap[category.parentId]) {
|
||||
categoryList = state.allTransactionCategoriesMap[category.parentId].subCategories;
|
||||
}
|
||||
|
||||
if (categoryList) {
|
||||
categoryList.push(category);
|
||||
}
|
||||
|
||||
state.allTransactionCategoriesMap[category.id] = category;
|
||||
}
|
||||
|
||||
function updateCategoryInTransactionCategoryList(state, category) {
|
||||
let categoryList = null;
|
||||
|
||||
if (!category.parentId || category.parentId === '0') {
|
||||
categoryList = state.allTransactionCategories[category.type];
|
||||
} else if (state.allTransactionCategoriesMap[category.parentId]) {
|
||||
categoryList = state.allTransactionCategoriesMap[category.parentId].subCategories;
|
||||
}
|
||||
|
||||
if (categoryList) {
|
||||
for (let i = 0; i < categoryList.length; i++) {
|
||||
if (categoryList[i].id === category.id) {
|
||||
categoryList.splice(i, 1, category);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state.allTransactionCategoriesMap[category.id] = category;
|
||||
}
|
||||
|
||||
function updateCategoryDisplayOrderInCategoryList(state, { category, from, to }) {
|
||||
let categoryList = null;
|
||||
|
||||
if (!category.parentId || category.parentId === '0') {
|
||||
categoryList = state.allTransactionCategories[category.type];
|
||||
} else if (state.allTransactionCategoriesMap[category.parentId]) {
|
||||
categoryList = state.allTransactionCategoriesMap[category.parentId].subCategories;
|
||||
}
|
||||
|
||||
if (categoryList) {
|
||||
categoryList.splice(to, 0, categoryList.splice(from, 1)[0]);
|
||||
}
|
||||
}
|
||||
|
||||
function updateCategoryVisibilityInTransactionCategoryList(state, { category, hidden }) {
|
||||
if (state.allTransactionCategoriesMap[category.id]) {
|
||||
state.allTransactionCategoriesMap[category.id].hidden = hidden;
|
||||
}
|
||||
}
|
||||
|
||||
function removeCategoryFromTransactionCategoryList(state, category) {
|
||||
let categoryList = null;
|
||||
|
||||
if (!category.parentId || category.parentId === '0') {
|
||||
categoryList = state.allTransactionCategories[category.type];
|
||||
} else if (state.allTransactionCategoriesMap[category.parentId]) {
|
||||
categoryList = state.allTransactionCategoriesMap[category.parentId].subCategories;
|
||||
}
|
||||
|
||||
if (categoryList) {
|
||||
for (let i = 0; i < categoryList.length; i++) {
|
||||
if (categoryList[i].id === category.id) {
|
||||
categoryList.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state.allTransactionCategoriesMap[category.id] && state.allTransactionCategoriesMap[category.id].subCategories) {
|
||||
const subCategories = state.allTransactionCategoriesMap[category.id].subCategories;
|
||||
|
||||
for (let i = 0; i < subCategories.length; i++) {
|
||||
const subCategory = subCategories[i];
|
||||
if (state.allTransactionCategoriesMap[subCategory.id]) {
|
||||
delete state.allTransactionCategoriesMap[subCategory.id];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state.allTransactionCategoriesMap[category.id]) {
|
||||
delete state.allTransactionCategoriesMap[category.id];
|
||||
}
|
||||
}
|
||||
|
||||
export const useTransactionCategoriesStore = defineStore('transactionCategories', {
|
||||
state: () => ({
|
||||
allTransactionCategories: {},
|
||||
allTransactionCategoriesMap: {},
|
||||
transactionCategoryListStateInvalid: true,
|
||||
}),
|
||||
actions: {
|
||||
updateTransactionCategoryListInvalidState(invalidState) {
|
||||
this.transactionCategoryListStateInvalid = invalidState;
|
||||
},
|
||||
resetTransactionCategories() {
|
||||
this.allTransactionCategories = {};
|
||||
this.allTransactionCategoriesMap = {};
|
||||
this.transactionCategoryListStateInvalid = true;
|
||||
},
|
||||
loadAllCategories({ force }) {
|
||||
const self = this;
|
||||
|
||||
if (!force && !self.transactionCategoryListStateInvalid) {
|
||||
return new Promise((resolve) => {
|
||||
resolve(self.allTransactionCategories);
|
||||
});
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getAllTransactionCategories().then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to get category list' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data.result[categoryConstants.allCategoryTypes.Income]) {
|
||||
data.result[categoryConstants.allCategoryTypes.Income] = [];
|
||||
}
|
||||
|
||||
if (!data.result[categoryConstants.allCategoryTypes.Expense]) {
|
||||
data.result[categoryConstants.allCategoryTypes.Expense] = [];
|
||||
}
|
||||
|
||||
if (!data.result[categoryConstants.allCategoryTypes.Transfer]) {
|
||||
data.result[categoryConstants.allCategoryTypes.Transfer] = [];
|
||||
}
|
||||
|
||||
for (let categoryType in data.result) {
|
||||
if (!Object.prototype.hasOwnProperty.call(data.result, categoryType)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const categories = data.result[categoryType];
|
||||
|
||||
for (let i = 0; i < categories.length; i++) {
|
||||
const category = categories[i];
|
||||
|
||||
if (!category.subCategories) {
|
||||
category.subCategories = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loadTransactionCategoryList(self, data.result);
|
||||
|
||||
if (self.transactionCategoryListStateInvalid) {
|
||||
self.updateTransactionCategoryListInvalidState(false);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
if (force) {
|
||||
logger.error('failed to force load category list', error);
|
||||
} else {
|
||||
logger.error('failed to load category list', error);
|
||||
}
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to get category list' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
getCategory({ categoryId }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getTransactionCategory({
|
||||
id: categoryId
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to get category' });
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to load category info', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to get category' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
saveCategory({ category }) {
|
||||
const self = this;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let promise = null;
|
||||
|
||||
if (!category.id) {
|
||||
promise = services.addTransactionCategory(category);
|
||||
} else {
|
||||
promise = services.modifyTransactionCategory(category);
|
||||
}
|
||||
|
||||
promise.then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
if (!category.id) {
|
||||
reject({ message: 'Unable to add category' });
|
||||
} else {
|
||||
reject({ message: 'Unable to save category' });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data.result.subCategories) {
|
||||
data.result.subCategories = [];
|
||||
}
|
||||
|
||||
if (!category.id) {
|
||||
addCategoryToTransactionCategoryList(self, data.result);
|
||||
} else {
|
||||
updateCategoryInTransactionCategoryList(self, data.result);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to save category', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
if (!category.id) {
|
||||
reject({ message: 'Unable to add category' });
|
||||
} else {
|
||||
reject({ message: 'Unable to save category' });
|
||||
}
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
addCategories({ categories }) {
|
||||
const self = this;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.addTransactionCategoryBatch({
|
||||
categories: categories
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to add preset categories' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self.transactionCategoryListStateInvalid) {
|
||||
self.updateTransactionCategoryListInvalidState(true);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to add preset categories', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to add preset categories' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
changeCategoryDisplayOrder({ categoryId, from, to }) {
|
||||
const self = this;
|
||||
const category = self.allTransactionCategoriesMap[categoryId];
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!category) {
|
||||
reject({ message: 'Unable to move category' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!category.parentId || category.parentId === '0') {
|
||||
if (!self.allTransactionCategories[category.type] ||
|
||||
!self.allTransactionCategories[category.type][to]) {
|
||||
reject({ message: 'Unable to move category' });
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!self.allTransactionCategoriesMap[category.parentId].subCategories ||
|
||||
!self.allTransactionCategoriesMap[category.parentId].subCategories[to]) {
|
||||
reject({ message: 'Unable to move category' });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!self.transactionCategoryListStateInvalid) {
|
||||
self.updateTransactionCategoryListInvalidState(true);
|
||||
}
|
||||
|
||||
updateCategoryDisplayOrderInCategoryList(self, {
|
||||
category: category,
|
||||
from: from,
|
||||
to: to
|
||||
});
|
||||
|
||||
resolve();
|
||||
});
|
||||
},
|
||||
updateCategoryDisplayOrders({ type, parentId }) {
|
||||
const self = this;
|
||||
const newDisplayOrders = [];
|
||||
|
||||
let categoryList = null;
|
||||
|
||||
if (!parentId || parentId === '0') {
|
||||
categoryList = self.allTransactionCategories[type];
|
||||
} else if (self.allTransactionCategoriesMap[parentId]) {
|
||||
categoryList = self.allTransactionCategoriesMap[parentId].subCategories;
|
||||
}
|
||||
|
||||
if (categoryList) {
|
||||
for (let i = 0; i < categoryList.length; i++) {
|
||||
newDisplayOrders.push({
|
||||
id: categoryList[i].id,
|
||||
displayOrder: i + 1
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.moveTransactionCategory({
|
||||
newDisplayOrders: newDisplayOrders
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to move category' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.transactionCategoryListStateInvalid) {
|
||||
self.updateTransactionCategoryListInvalidState(false);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to save categories display order', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to move category' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
hideCategory({ category, hidden }) {
|
||||
const self = this;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.hideTransactionCategory({
|
||||
id: category.id,
|
||||
hidden: hidden
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
if (hidden) {
|
||||
reject({ message: 'Unable to hide this category' });
|
||||
} else {
|
||||
reject({ message: 'Unable to unhide this category' });
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
updateCategoryVisibilityInTransactionCategoryList(self, {
|
||||
category: category,
|
||||
hidden: hidden
|
||||
});
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to change category visibility', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
if (hidden) {
|
||||
reject({ message: 'Unable to hide this category' });
|
||||
} else {
|
||||
reject({ message: 'Unable to unhide this category' });
|
||||
}
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
deleteCategory({ category, beforeResolve }) {
|
||||
const self = this;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.deleteTransactionCategory({
|
||||
id: category.id
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to delete this category' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (beforeResolve) {
|
||||
beforeResolve(() => {
|
||||
removeCategoryFromTransactionCategoryList(self, category);
|
||||
});
|
||||
} else {
|
||||
removeCategoryFromTransactionCategoryList(self, category);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to delete category', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to delete this category' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,311 @@
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
import services from '@/lib/services.js';
|
||||
import logger from '@/lib/logger.js';
|
||||
|
||||
function loadTransactionTagList(state, tags) {
|
||||
state.allTransactionTags = tags;
|
||||
state.allTransactionTagsMap = {};
|
||||
|
||||
for (let i = 0; i < tags.length; i++) {
|
||||
const tag = tags[i];
|
||||
state.allTransactionTagsMap[tag.id] = tag;
|
||||
}
|
||||
}
|
||||
|
||||
function addTagToTransactionTagList(state, tag) {
|
||||
state.allTransactionTags.push(tag);
|
||||
state.allTransactionTagsMap[tag.id] = tag;
|
||||
}
|
||||
|
||||
function updateTagInTransactionTagList(state, tag) {
|
||||
for (let i = 0; i < state.allTransactionTags.length; i++) {
|
||||
if (state.allTransactionTags[i].id === tag.id) {
|
||||
state.allTransactionTags.splice(i, 1, tag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
state.allTransactionTagsMap[tag.id] = tag;
|
||||
}
|
||||
|
||||
function updateTagDisplayOrderInTransactionTagList(state, { from, to }) {
|
||||
state.allTransactionTags.splice(to, 0, state.allTransactionTags.splice(from, 1)[0]);
|
||||
}
|
||||
|
||||
function updateTagVisibilityInTransactionTagList(state, { tag, hidden }) {
|
||||
if (state.allTransactionTagsMap[tag.id]) {
|
||||
state.allTransactionTagsMap[tag.id].hidden = hidden;
|
||||
}
|
||||
}
|
||||
|
||||
function removeTagFromTransactionTagList(state, tag) {
|
||||
for (let i = 0; i < state.allTransactionTags.length; i++) {
|
||||
if (state.allTransactionTags[i].id === tag.id) {
|
||||
state.allTransactionTags.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (state.allTransactionTagsMap[tag.id]) {
|
||||
delete state.allTransactionTagsMap[tag.id];
|
||||
}
|
||||
}
|
||||
|
||||
export const useTransactionTagsStore = defineStore('transactionTags', {
|
||||
state: () => ({
|
||||
allTransactionTags: [],
|
||||
allTransactionTagsMap: {},
|
||||
transactionTagListStateInvalid: true,
|
||||
}),
|
||||
actions: {
|
||||
updateTransactionTagListInvalidState(invalidState) {
|
||||
this.transactionTagListStateInvalid = invalidState;
|
||||
},
|
||||
resetTransactionTags() {
|
||||
this.allTransactionTags = [];
|
||||
this.allTransactionTagsMap = {};
|
||||
this.transactionTagListStateInvalid = true;
|
||||
},
|
||||
loadAllTags({ force }) {
|
||||
const self = this;
|
||||
|
||||
if (!force && !self.transactionTagListStateInvalid) {
|
||||
return new Promise((resolve) => {
|
||||
resolve(self.allTransactionTags);
|
||||
});
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getAllTransactionTags().then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to get tag list' });
|
||||
return;
|
||||
}
|
||||
|
||||
loadTransactionTagList(self, data.result);
|
||||
|
||||
if (self.transactionTagListStateInvalid) {
|
||||
self.updateTransactionTagListInvalidState(false);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
if (force) {
|
||||
logger.error('failed to force load tag list', error);
|
||||
} else {
|
||||
logger.error('failed to load tag list', error);
|
||||
}
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to get tag list' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
saveTag({ tag }) {
|
||||
const self = this;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let promise = null;
|
||||
|
||||
if (!tag.id) {
|
||||
promise = services.addTransactionTag(tag);
|
||||
} else {
|
||||
promise = services.modifyTransactionTag(tag);
|
||||
}
|
||||
|
||||
promise.then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
if (!tag.id) {
|
||||
reject({ message: 'Unable to add tag' });
|
||||
} else {
|
||||
reject({ message: 'Unable to save tag' });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tag.id) {
|
||||
addTagToTransactionTagList(self, data.result);
|
||||
} else {
|
||||
updateTagInTransactionTagList(self, data.result);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to save tag', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
if (!tag.id) {
|
||||
reject({ message: 'Unable to add tag' });
|
||||
} else {
|
||||
reject({ message: 'Unable to save tag' });
|
||||
}
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
changeTagDisplayOrder({ tagId, from, to }) {
|
||||
const self = this;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let tag = null;
|
||||
|
||||
for (let i = 0; i < self.allTransactionTags.length; i++) {
|
||||
if (self.allTransactionTags[i].id === tagId) {
|
||||
tag = self.allTransactionTags[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tag || !self.allTransactionTags[to]) {
|
||||
reject({ message: 'Unable to move tag' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self.transactionTagListStateInvalid) {
|
||||
self.updateTransactionTagListInvalidState(true);
|
||||
}
|
||||
|
||||
updateTagDisplayOrderInTransactionTagList(self, {
|
||||
tag: tag,
|
||||
from: from,
|
||||
to: to
|
||||
});
|
||||
|
||||
resolve();
|
||||
});
|
||||
},
|
||||
updateTagDisplayOrders() {
|
||||
const self = this;
|
||||
const newDisplayOrders = [];
|
||||
|
||||
for (let i = 0; i < self.allTransactionTags.length; i++) {
|
||||
newDisplayOrders.push({
|
||||
id: self.allTransactionTags[i].id,
|
||||
displayOrder: i + 1
|
||||
});
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.moveTransactionTag({
|
||||
newDisplayOrders: newDisplayOrders
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to move tag' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.transactionTagListStateInvalid) {
|
||||
self.updateTransactionTagListInvalidState(false);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to save tags display order', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to move tag' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
hideTag({ tag, hidden }) {
|
||||
const self = this;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.hideTransactionTag({
|
||||
id: tag.id,
|
||||
hidden: hidden
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
if (hidden) {
|
||||
reject({ message: 'Unable to hide this tag' });
|
||||
} else {
|
||||
reject({ message: 'Unable to unhide this tag' });
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
updateTagVisibilityInTransactionTagList(self, {
|
||||
tag: tag,
|
||||
hidden: hidden
|
||||
});
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to change tag visibility', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
if (hidden) {
|
||||
reject({ message: 'Unable to hide this tag' });
|
||||
} else {
|
||||
reject({ message: 'Unable to unhide this tag' });
|
||||
}
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
deleteTag({ tag, beforeResolve }) {
|
||||
const self = this;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.deleteTransactionTag({
|
||||
id: tag.id
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to delete this tag' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (beforeResolve) {
|
||||
beforeResolve(() => {
|
||||
removeTagFromTransactionTagList(self, tag);
|
||||
});
|
||||
} else {
|
||||
removeTagFromTransactionTagList(self, tag);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to delete tag', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to delete this tag' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,142 @@
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
import userState from '@/lib/userstate.js';
|
||||
import services from '@/lib/services.js';
|
||||
import logger from '@/lib/logger.js';
|
||||
import { isBoolean } from '@/lib/common.js';
|
||||
|
||||
export const useTwoFactorAuthStore = defineStore('twoFactorAuth', {
|
||||
actions: {
|
||||
get2FAStatus() {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.get2FAStatus().then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result || !isBoolean(data.result.enable)) {
|
||||
reject({ message: 'Unable to get current two factor authentication status' });
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to get 2fa status', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to get current two factor authentication status' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
enable2FA() {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.enable2FA().then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result || !data.result.qrcode || !data.result.secret) {
|
||||
reject({ message: 'Unable to enable two factor authentication' });
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to request to enable 2fa', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to enable two factor authentication' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
confirmEnable2FA({ secret, passcode }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.confirmEnable2FA({
|
||||
secret: secret,
|
||||
passcode: passcode
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result || !data.result.token) {
|
||||
reject({ message: 'Unable to enable two factor authentication' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.result.token) {
|
||||
userState.updateToken(data.result.token);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to confirm to enable 2fa', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to enable two factor authentication' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
disable2FA({ password }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.disable2FA({
|
||||
password: password
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to disable two factor authentication' });
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to disable 2fa', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to disable two factor authentication' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
regenerate2FARecoveryCode({ password }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.regenerate2FARecoveryCode({
|
||||
password: password
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result || !data.result.recoveryCodes || !data.result.recoveryCodes.length) {
|
||||
reject({ message: 'Unable to regenerate two factor authentication backup codes' });
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to regenerate 2fa recovery code', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to regenerate two factor authentication backup codes' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,140 @@
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
import { useSettingsStore } from './setting.js';
|
||||
|
||||
import userState from '@/lib/userstate.js';
|
||||
import services from '@/lib/services.js';
|
||||
import logger from '@/lib/logger.js';
|
||||
import { isNumber } from '@/lib/common.js';
|
||||
|
||||
export const useUserStore = defineStore('user', {
|
||||
state: () => ({
|
||||
currentUserInfo: userState.getUserInfo()
|
||||
}),
|
||||
getters: {
|
||||
currentUserNickname(state) {
|
||||
const userInfo = state.currentUserInfo || {};
|
||||
return userInfo.nickname || userInfo.username || null;
|
||||
},
|
||||
currentUserDefaultAccountId(state) {
|
||||
const userInfo = state.currentUserInfo || {};
|
||||
return userInfo.defaultAccountId || '';
|
||||
},
|
||||
currentUserLanguage(state) {
|
||||
const settingsStore = useSettingsStore();
|
||||
const userInfo = state.currentUserInfo || {};
|
||||
return userInfo.language || settingsStore.language;
|
||||
},
|
||||
currentUserDefaultCurrency(state) {
|
||||
const settingsStore = useSettingsStore();
|
||||
const userInfo = state.currentUserInfo || {};
|
||||
return userInfo.defaultCurrency || settingsStore.currency;
|
||||
},
|
||||
currentUserFirstDayOfWeek(state) {
|
||||
const settingsStore = useSettingsStore();
|
||||
const userInfo = state.currentUserInfo || {};
|
||||
return isNumber(userInfo.firstDayOfWeek) ? userInfo.firstDayOfWeek : settingsStore.firstDayOfWeek;
|
||||
},
|
||||
currentUserLongDateFormat(state) {
|
||||
const settingsStore = useSettingsStore();
|
||||
const userInfo = state.currentUserInfo || {};
|
||||
return isNumber(userInfo.longDateFormat) ? userInfo.longDateFormat : settingsStore.longDateFormat;
|
||||
},
|
||||
currentUserShortDateFormat(state) {
|
||||
const settingsStore = useSettingsStore();
|
||||
const userInfo = state.currentUserInfo || {};
|
||||
return isNumber(userInfo.shortDateFormat) ? userInfo.shortDateFormat : settingsStore.shortDateFormat;
|
||||
},
|
||||
currentUserLongTimeFormat(state) {
|
||||
const settingsStore = useSettingsStore();
|
||||
const userInfo = state.currentUserInfo || {};
|
||||
return isNumber(userInfo.longTimeFormat) ? userInfo.longTimeFormat : settingsStore.longTimeFormat;
|
||||
},
|
||||
currentUserShortTimeFormat(state) {
|
||||
const settingsStore = useSettingsStore();
|
||||
const userInfo = state.currentUserInfo || {};
|
||||
return isNumber(userInfo.shortTimeFormat) ? userInfo.shortTimeFormat : settingsStore.shortTimeFormat;
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
storeUserInfo(userInfo) {
|
||||
this.currentUserInfo = userInfo;
|
||||
userState.updateUserInfo(userInfo);
|
||||
},
|
||||
resetUserInfo() {
|
||||
this.currentUserInfo = null;
|
||||
userState.clearUserInfo();
|
||||
},
|
||||
getCurrentUserProfile() {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getProfile().then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to get user profile' });
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to get user profile', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to get user profile' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
getUserDataStatistics() {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getUserDataStatistics().then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to get user statistics data' });
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to get user statistics data', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to get user statistics data' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
getExportedUserData() {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getExportedUserData().then(response => {
|
||||
if (response && response.headers && response.headers['content-type'] !== 'text/csv') {
|
||||
reject({ message: 'Unable to get exported user data' });
|
||||
return;
|
||||
}
|
||||
|
||||
const blob = new Blob([response.data], { type: response.headers['content-type'] });
|
||||
resolve(blob);
|
||||
}).catch(error => {
|
||||
logger.error('failed to get user statistics data', error);
|
||||
|
||||
if (error.response && error.response.headers['content-type'] === 'text/text' && error.response && error.response.data) {
|
||||
reject({ message: 'error.' + error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to get exported user data' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
||||
@@ -41,8 +41,14 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useUserStore } from '@/stores/user.js';
|
||||
|
||||
import licenses from '@/lib/licenses.js';
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
...mapStores(useUserStore),
|
||||
version() {
|
||||
return 'v' + this.$version;
|
||||
},
|
||||
@@ -51,13 +57,13 @@ export default {
|
||||
return this.$buildTime;
|
||||
}
|
||||
|
||||
return this.$utilities.formatUnixTime(this.$buildTime, this.$locale.getLongDateTimeFormat());
|
||||
return this.$locale.formatUnixTimeToLongDateTime(this.userStore, this.$buildTime);
|
||||
},
|
||||
licenseLines() {
|
||||
return this.$licenses.license.replaceAll(/\r/g, '').split('\n');
|
||||
return licenses.getLicense().replaceAll(/\r/g, '').split('\n');
|
||||
},
|
||||
thirdPartyLicenses() {
|
||||
return this.$licenses.thirdPartyLicenses;
|
||||
return licenses.getThirdPartyLicenses();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,12 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useUserStore } from '@/stores/user.js';
|
||||
|
||||
import logger from '@/lib/logger.js';
|
||||
import webauthn from '@/lib/webauthn.js';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
@@ -48,6 +54,9 @@ export default {
|
||||
showInputPinCodeSheetForDisable: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useUserStore)
|
||||
},
|
||||
watch: {
|
||||
isEnableApplicationLockWebAuthn: function (newValue) {
|
||||
const self = this;
|
||||
@@ -55,9 +64,9 @@ export default {
|
||||
if (newValue) {
|
||||
self.$showLoading();
|
||||
|
||||
self.$webauthn.registerCredential(
|
||||
webauthn.registerCredential(
|
||||
self.$user.getUserAppLockState(),
|
||||
self.$store.state.currentUserInfo,
|
||||
self.userStore.currentUserInfo,
|
||||
).then(({ id }) => {
|
||||
self.$hideLoading();
|
||||
|
||||
@@ -65,7 +74,7 @@ export default {
|
||||
self.$settings.setEnableApplicationLockWebAuthn(true);
|
||||
self.$toast('You have enabled WebAuthn successfully');
|
||||
}).catch(error => {
|
||||
self.$logger.error('failed to enable WebAuthn', error);
|
||||
logger.error('failed to enable WebAuthn', error);
|
||||
|
||||
self.$hideLoading();
|
||||
|
||||
@@ -91,7 +100,7 @@ export default {
|
||||
},
|
||||
created() {
|
||||
const self = this;
|
||||
self.$webauthn.isCompletelySupported().then(result => {
|
||||
webauthn.isCompletelySupported().then(result => {
|
||||
self.isSupportedWebAuthn = result;
|
||||
});
|
||||
},
|
||||
@@ -112,7 +121,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
const user = this.$store.state.currentUserInfo;
|
||||
const user = this.userStore.currentUserInfo;
|
||||
|
||||
if (!user || !user.username) {
|
||||
this.$alert('An error has occurred');
|
||||
|
||||
@@ -34,8 +34,8 @@
|
||||
:title="displayBaseAmount"
|
||||
@click="showBaseAmountSheet = true"
|
||||
>
|
||||
<number-pad-sheet :min-value="$constants.transaction.minAmount"
|
||||
:max-value="$constants.transaction.maxAmount"
|
||||
<number-pad-sheet :min-value="allowedMinAmount"
|
||||
:max-value="allowedMaxAmount"
|
||||
v-model:show="showBaseAmountSheet"
|
||||
v-model="baseAmount"
|
||||
></number-pad-sheet>
|
||||
@@ -90,12 +90,20 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useUserStore } from '@/stores/user.js';
|
||||
import { useExchangeRatesStore } from '@/stores/exchangeRates.js';
|
||||
|
||||
import transactionConstants from '@/consts/transaction.js';
|
||||
import { isNumber, appendThousandsSeparator } from '@/lib/common.js';
|
||||
import { stringCurrencyToNumeric, getExchangedAmount } from '@/lib/currency.js';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
const self = this;
|
||||
const userStore = useUserStore();
|
||||
|
||||
return {
|
||||
baseCurrency: self.$store.getters.currentUserDefaultCurrency,
|
||||
baseCurrency: userStore.currentUserDefaultCurrency,
|
||||
baseAmount: 100,
|
||||
updating: false,
|
||||
showMoreActionSheet: false,
|
||||
@@ -103,29 +111,13 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useUserStore, useExchangeRatesStore),
|
||||
exchangeRatesData() {
|
||||
return this.$store.state.latestExchangeRates.data;
|
||||
return this.exchangeRatesStore.latestExchangeRates.data;
|
||||
},
|
||||
exchangeRatesDataUpdateTime() {
|
||||
if (!this.exchangeRatesData) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return this.$utilities.formatUnixTime(this.exchangeRatesData.updateTime, this.$locale.getLongDateFormat());
|
||||
},
|
||||
exchangeRateMap() {
|
||||
const exchangeRateMap = {};
|
||||
|
||||
if (!this.exchangeRatesData.exchangeRates) {
|
||||
return exchangeRateMap;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.exchangeRatesData.exchangeRates.length; i++) {
|
||||
const exchangeRate = this.exchangeRatesData.exchangeRates[i];
|
||||
exchangeRateMap[exchangeRate.currency] = exchangeRate;
|
||||
}
|
||||
|
||||
return exchangeRateMap;
|
||||
const exchangeRatesLastUpdateTime = this.exchangeRatesStore.exchangeRatesLastUpdateTime;
|
||||
return exchangeRatesLastUpdateTime ? this.$locale.formatUnixTimeToLongDate(this.userStore, exchangeRatesLastUpdateTime) : '';
|
||||
},
|
||||
availableExchangeRates() {
|
||||
if (!this.exchangeRatesData || !this.exchangeRatesData.exchangeRates) {
|
||||
@@ -155,6 +147,12 @@ export default {
|
||||
},
|
||||
baseAmountFontSize() {
|
||||
return this.getFontSizeByAmount(this.baseAmount);
|
||||
},
|
||||
allowedMinAmount() {
|
||||
return transactionConstants.minAmount;
|
||||
},
|
||||
allowedMaxAmount() {
|
||||
return transactionConstants.maxAmount;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@@ -189,7 +187,7 @@ export default {
|
||||
self.$showLoading();
|
||||
}
|
||||
|
||||
self.$store.dispatch('getLatestExchangeRates', {
|
||||
self.exchangeRatesStore.getLatestExchangeRates({
|
||||
silent: false,
|
||||
force: true
|
||||
}).then(() => {
|
||||
@@ -215,19 +213,19 @@ export default {
|
||||
});
|
||||
},
|
||||
getConvertedAmount(toExchangeRate) {
|
||||
const fromExchangeRate = this.exchangeRateMap[this.baseCurrency];
|
||||
const fromExchangeRate = this.exchangeRatesStore.latestExchangeRateMap[this.baseCurrency];
|
||||
|
||||
if (!fromExchangeRate) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return this.$utilities.getExchangedAmount(this.baseAmount / 100, fromExchangeRate.rate, toExchangeRate.rate);
|
||||
return getExchangedAmount(this.baseAmount / 100, fromExchangeRate.rate, toExchangeRate.rate);
|
||||
},
|
||||
getDisplayConvertedAmount(toExchangeRate) {
|
||||
const rateStr = this.getConvertedAmount(toExchangeRate).toString();
|
||||
|
||||
if (rateStr.indexOf('.') < 0) {
|
||||
return this.$utilities.appendThousandsSeparator(rateStr);
|
||||
return appendThousandsSeparator(rateStr);
|
||||
} else {
|
||||
let firstNonZeroPos = 0;
|
||||
|
||||
@@ -239,16 +237,16 @@ export default {
|
||||
}
|
||||
|
||||
const trimmedRateStr = rateStr.substring(0, Math.max(6, Math.max(firstNonZeroPos, rateStr.indexOf('.') + 2)));
|
||||
return this.$utilities.appendThousandsSeparator(trimmedRateStr);
|
||||
return appendThousandsSeparator(trimmedRateStr);
|
||||
}
|
||||
},
|
||||
setAsBaseline(currency, amount) {
|
||||
if (!this.$utilities.isNumber(amount)) {
|
||||
if (!isNumber(amount)) {
|
||||
amount = '';
|
||||
}
|
||||
|
||||
this.baseCurrency = currency;
|
||||
this.baseAmount = this.$utilities.stringCurrencyToNumeric(amount.toString());
|
||||
this.baseAmount = stringCurrencyToNumeric(amount.toString());
|
||||
},
|
||||
getFontSizeByAmount(amount) {
|
||||
if (amount >= 100000000 || amount <= -100000000) {
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
</f7-card>
|
||||
|
||||
<f7-list strong inset dividers class="margin-top overview-transaction-list" :class="{ 'skeleton-text': loading }">
|
||||
<f7-list-item :link="'/transaction/list?dateType=' + $constants.datetime.allDateRanges.Today.type" chevron-center>
|
||||
<f7-list-item :link="'/transaction/list?dateType=' + allDateRanges.Today.type" chevron-center>
|
||||
<template #media>
|
||||
<f7-icon f7="calendar_today"></f7-icon>
|
||||
</template>
|
||||
@@ -66,7 +66,7 @@
|
||||
</template>
|
||||
</f7-list-item>
|
||||
|
||||
<f7-list-item :link="'/transaction/list?dateType=' + $constants.datetime.allDateRanges.ThisWeek.type" chevron-center>
|
||||
<f7-list-item :link="'/transaction/list?dateType=' + allDateRanges.ThisWeek.type" chevron-center>
|
||||
<template #media>
|
||||
<f7-icon f7="calendar"></f7-icon>
|
||||
</template>
|
||||
@@ -99,7 +99,7 @@
|
||||
</template>
|
||||
</f7-list-item>
|
||||
|
||||
<f7-list-item :link="'/transaction/list?dateType=' + $constants.datetime.allDateRanges.ThisMonth.type" chevron-center>
|
||||
<f7-list-item :link="'/transaction/list?dateType=' + allDateRanges.ThisMonth.type" chevron-center>
|
||||
<template #media>
|
||||
<f7-icon f7="calendar"></f7-icon>
|
||||
</template>
|
||||
@@ -132,7 +132,7 @@
|
||||
</template>
|
||||
</f7-list-item>
|
||||
|
||||
<f7-list-item :link="'/transaction/list?dateType=' + $constants.datetime.allDateRanges.ThisYear.type" chevron-center>
|
||||
<f7-list-item :link="'/transaction/list?dateType=' + allDateRanges.ThisYear.type" chevron-center>
|
||||
<template #media>
|
||||
<f7-icon f7="square_stack_3d_up"></f7-icon>
|
||||
</template>
|
||||
@@ -188,25 +188,46 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useUserStore } from '@/stores/user.js';
|
||||
import { useOverviewStore } from '@/stores/overview.js';
|
||||
|
||||
import datetimeConstants from '@/consts/datetime.js';
|
||||
import {
|
||||
formatUnixTime,
|
||||
getTodayFirstUnixTime,
|
||||
getTodayLastUnixTime,
|
||||
getThisWeekFirstUnixTime,
|
||||
getThisWeekLastUnixTime,
|
||||
getThisMonthFirstUnixTime,
|
||||
getThisMonthLastUnixTime,
|
||||
getThisYearFirstUnixTime,
|
||||
getThisYearLastUnixTime
|
||||
} from '@/lib/datetime.js';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
const self = this;
|
||||
|
||||
return {
|
||||
loading: true,
|
||||
todayFirstUnixTime: self.$utilities.getTodayFirstUnixTime(),
|
||||
todayLastUnixTime: self.$utilities.getTodayLastUnixTime(),
|
||||
todayFirstUnixTime: getTodayFirstUnixTime(),
|
||||
todayLastUnixTime: getTodayLastUnixTime(),
|
||||
showAmountInHomePage: self.$settings.isShowAmountInHomePage(),
|
||||
isEnableThousandsSeparator: self.$settings.isEnableThousandsSeparator(),
|
||||
currencyDisplayMode: self.$settings.getCurrencyDisplayMode()
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useUserStore, useOverviewStore),
|
||||
defaultCurrency() {
|
||||
return this.$store.getters.currentUserDefaultCurrency;
|
||||
return this.userStore.currentUserDefaultCurrency;
|
||||
},
|
||||
firstDayOfWeek() {
|
||||
return this.$store.getters.currentUserFirstDayOfWeek;
|
||||
return this.userStore.currentUserFirstDayOfWeek;
|
||||
},
|
||||
allDateRanges() {
|
||||
return datetimeConstants.allDateRanges;
|
||||
},
|
||||
dateRange() {
|
||||
const self = this;
|
||||
@@ -217,16 +238,16 @@ export default {
|
||||
endTime: self.todayLastUnixTime
|
||||
},
|
||||
thisWeek: {
|
||||
startTime: self.$utilities.getThisWeekFirstUnixTime(self.firstDayOfWeek),
|
||||
endTime: self.$utilities.getThisWeekLastUnixTime(self.firstDayOfWeek)
|
||||
startTime: getThisWeekFirstUnixTime(self.firstDayOfWeek),
|
||||
endTime: getThisWeekLastUnixTime(self.firstDayOfWeek)
|
||||
},
|
||||
thisMonth: {
|
||||
startTime: self.$utilities.getThisMonthFirstUnixTime(),
|
||||
endTime: self.$utilities.getThisMonthLastUnixTime()
|
||||
startTime: getThisMonthFirstUnixTime(),
|
||||
endTime: getThisMonthLastUnixTime()
|
||||
},
|
||||
thisYear: {
|
||||
startTime: self.$utilities.getThisYearFirstUnixTime(),
|
||||
endTime: self.$utilities.getThisYearLastUnixTime()
|
||||
startTime: getThisYearFirstUnixTime(),
|
||||
endTime: getThisYearLastUnixTime()
|
||||
}
|
||||
};
|
||||
},
|
||||
@@ -235,19 +256,19 @@ export default {
|
||||
|
||||
return {
|
||||
today: {
|
||||
displayTime: self.$utilities.formatUnixTime(self.dateRange.today.startTime, self.$locale.getLongDateFormat()),
|
||||
displayTime: self.$locale.formatUnixTimeToLongDate(self.userStore, self.dateRange.today.startTime),
|
||||
},
|
||||
thisWeek: {
|
||||
startTime: self.$utilities.formatUnixTime(self.dateRange.thisWeek.startTime, self.$locale.getLongMonthDayFormat()),
|
||||
endTime: self.$utilities.formatUnixTime(self.dateRange.thisWeek.endTime, self.$locale.getLongMonthDayFormat())
|
||||
startTime: self.$locale.formatUnixTimeToLongMonthDay(self.userStore, self.dateRange.thisWeek.startTime),
|
||||
endTime: self.$locale.formatUnixTimeToLongMonthDay(self.userStore, self.dateRange.thisWeek.endTime)
|
||||
},
|
||||
thisMonth: {
|
||||
displayTime: self.$utilities.formatUnixTime(self.dateRange.thisMonth.startTime, 'MMMM'),
|
||||
startTime: self.$utilities.formatUnixTime(self.dateRange.thisMonth.startTime, self.$locale.getLongMonthDayFormat()),
|
||||
endTime: self.$utilities.formatUnixTime(self.dateRange.thisMonth.endTime, self.$locale.getLongMonthDayFormat())
|
||||
displayTime: formatUnixTime(self.dateRange.thisMonth.startTime, 'MMMM'),
|
||||
startTime: self.$locale.formatUnixTimeToLongMonthDay(self.userStore, self.dateRange.thisMonth.startTime),
|
||||
endTime: self.$locale.formatUnixTimeToLongMonthDay(self.userStore, self.dateRange.thisMonth.endTime)
|
||||
},
|
||||
thisYear: {
|
||||
displayTime: self.$utilities.formatUnixTime(self.dateRange.thisYear.startTime, self.$locale.getLongYearFormat())
|
||||
displayTime: self.$locale.formatUnixTimeToLongYear(self.userStore, self.dateRange.thisYear.startTime)
|
||||
}
|
||||
};
|
||||
},
|
||||
@@ -256,7 +277,7 @@ export default {
|
||||
const isEnableThousandsSeparator = this.isEnableThousandsSeparator; // eslint-disable-line
|
||||
const currencyDisplayMode = this.currencyDisplayMode; // eslint-disable-line
|
||||
|
||||
if (!this.$store.state.transactionOverview || !this.$store.state.transactionOverview.thisMonth) {
|
||||
if (!this.overviewStore.transactionOverview || !this.overviewStore.transactionOverview.thisMonth) {
|
||||
return {
|
||||
thisMonth: {
|
||||
valid: false,
|
||||
@@ -266,7 +287,7 @@ export default {
|
||||
};
|
||||
}
|
||||
|
||||
const originalOverview = this.$store.state.transactionOverview;
|
||||
const originalOverview = this.overviewStore.transactionOverview;
|
||||
const displayOverview = {};
|
||||
|
||||
[ 'today', 'thisWeek', 'thisMonth', 'thisYear' ].forEach(key => {
|
||||
@@ -292,7 +313,7 @@ export default {
|
||||
if (self.$user.isUserLogined() && self.$user.isUserUnlocked()) {
|
||||
self.loading = true;
|
||||
|
||||
self.$store.dispatch('loadTransactionOverview', {
|
||||
self.overviewStore.loadTransactionOverview({
|
||||
defaultCurrency: self.defaultCurrency,
|
||||
dateRange: self.dateRange,
|
||||
force: false
|
||||
@@ -319,21 +340,21 @@ export default {
|
||||
|
||||
let dateChanged = false;
|
||||
|
||||
if (this.todayFirstUnixTime !== this.$utilities.getTodayFirstUnixTime()) {
|
||||
if (this.todayFirstUnixTime !== getTodayFirstUnixTime()) {
|
||||
dateChanged = true;
|
||||
|
||||
this.todayFirstUnixTime = this.$utilities.getTodayFirstUnixTime();
|
||||
this.todayLastUnixTime = this.$utilities.getTodayLastUnixTime();
|
||||
this.todayFirstUnixTime = getTodayFirstUnixTime();
|
||||
this.todayLastUnixTime = getTodayLastUnixTime();
|
||||
}
|
||||
|
||||
if ((dateChanged || this.$store.state.transactionOverviewStateInvalid) && !this.loading) {
|
||||
if ((dateChanged || this.overviewStore.transactionOverviewStateInvalid) && !this.loading) {
|
||||
this.reload(null);
|
||||
}
|
||||
},
|
||||
reload(done) {
|
||||
const self = this;
|
||||
|
||||
self.$store.dispatch('loadTransactionOverview', {
|
||||
self.overviewStore.loadTransactionOverview({
|
||||
defaultCurrency: self.defaultCurrency,
|
||||
dateRange: self.dateRange,
|
||||
force: true
|
||||
|
||||
@@ -109,6 +109,12 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useRootStore } from '@/stores/index.js';
|
||||
import { useExchangeRatesStore } from '@/stores/exchangeRates.js';
|
||||
|
||||
import { isModalShowing } from '@/lib/ui.mobile.js';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'f7router'
|
||||
@@ -127,6 +133,7 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useRootStore, useExchangeRatesStore),
|
||||
version() {
|
||||
return 'v' + this.$version;
|
||||
},
|
||||
@@ -187,7 +194,7 @@ export default {
|
||||
self.logining = true;
|
||||
self.$showLoading(() => self.logining);
|
||||
|
||||
self.$store.dispatch('authorize', {
|
||||
self.rootStore.authorize({
|
||||
loginName: self.username,
|
||||
password: self.password
|
||||
}).then(authResponse => {
|
||||
@@ -205,7 +212,7 @@ export default {
|
||||
}
|
||||
|
||||
if (self.$settings.isAutoUpdateExchangeRatesData()) {
|
||||
self.$store.dispatch('getLatestExchangeRates', { silent: true, force: false });
|
||||
self.exchangeRatesStore.getLatestExchangeRates({ silent: true, force: false });
|
||||
}
|
||||
|
||||
router.refreshPage();
|
||||
@@ -219,7 +226,7 @@ export default {
|
||||
});
|
||||
},
|
||||
loginByPressEnter() {
|
||||
if (this.$ui.isModalShowing()) {
|
||||
if (isModalShowing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -244,7 +251,7 @@ export default {
|
||||
self.verifying = true;
|
||||
self.$showLoading(() => self.verifying);
|
||||
|
||||
self.$store.dispatch('authorize2FA', {
|
||||
self.rootStore.authorize2FA({
|
||||
token: self.tempToken,
|
||||
passcode: self.twoFAVerifyType === 'passcode' ? self.passcode : null,
|
||||
recoveryCode: self.twoFAVerifyType === 'backupcode' ? self.backupCode : null
|
||||
@@ -257,7 +264,7 @@ export default {
|
||||
}
|
||||
|
||||
if (self.$settings.isAutoUpdateExchangeRatesData()) {
|
||||
self.$store.dispatch('getLatestExchangeRates', { silent: true, force: false });
|
||||
self.exchangeRatesStore.getLatestExchangeRates({ silent: true, force: false });
|
||||
}
|
||||
|
||||
self.show2faSheet = false;
|
||||
|
||||
@@ -61,10 +61,10 @@
|
||||
:title="$t('Currency Display Mode')"
|
||||
smart-select :smart-select-params="{ openIn: 'popup', popupPush: true, closeOnSelect: true, scrollToSelectedItem: true, searchbar: true, searchbarPlaceholder: $t('Currency Display Mode'), searchbarDisableText: $t('Cancel'), appendSearchbarNotFound: $t('No results'), popupCloseLinkText: $t('Done') }">
|
||||
<select v-model="currencyDisplayMode">
|
||||
<option :value="$constants.currency.allCurrencyDisplayModes.None">{{ $t('None') }}</option>
|
||||
<option :value="$constants.currency.allCurrencyDisplayModes.Symbol">{{ $t('Currency Symbol') }}</option>
|
||||
<option :value="$constants.currency.allCurrencyDisplayModes.Code">{{ $t('Currency Code') }}</option>
|
||||
<option :value="$constants.currency.allCurrencyDisplayModes.Name">{{ $t('Currency Name') }}</option>
|
||||
<option :value="allCurrencyDisplayModes.None">{{ $t('None') }}</option>
|
||||
<option :value="allCurrencyDisplayModes.Symbol">{{ $t('Currency Symbol') }}</option>
|
||||
<option :value="allCurrencyDisplayModes.Code">{{ $t('Currency Code') }}</option>
|
||||
<option :value="allCurrencyDisplayModes.Name">{{ $t('Currency Name') }}</option>
|
||||
</select>
|
||||
</f7-list-item>
|
||||
|
||||
@@ -96,6 +96,13 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useRootStore } from '@/stores/index.js';
|
||||
import { useUserStore } from '@/stores/user.js';
|
||||
import { useExchangeRatesStore } from '@/stores/exchangeRates.js';
|
||||
|
||||
import currencyConstants from '@/consts/currency.js';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'f7router'
|
||||
@@ -110,6 +117,7 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useRootStore, useUserStore, useExchangeRatesStore),
|
||||
version() {
|
||||
return 'v' + this.$version;
|
||||
},
|
||||
@@ -136,11 +144,14 @@ export default {
|
||||
}
|
||||
},
|
||||
currentNickName() {
|
||||
return this.$store.getters.currentUserNickname || this.$t('User');
|
||||
return this.userStore.currentUserNickname || this.$t('User');
|
||||
},
|
||||
exchangeRatesLastUpdateDate() {
|
||||
const exchangeRatesLastUpdateTime = this.$store.getters.exchangeRatesLastUpdateTime;
|
||||
return exchangeRatesLastUpdateTime ? this.$utilities.formatUnixTime(exchangeRatesLastUpdateTime, this.$locale.getLongDateFormat()) : '';
|
||||
const exchangeRatesLastUpdateTime = this.exchangeRatesStore.exchangeRatesLastUpdateTime;
|
||||
return exchangeRatesLastUpdateTime ? this.$locale.formatUnixTimeToLongDate(this.userStore, exchangeRatesLastUpdateTime) : '';
|
||||
},
|
||||
allCurrencyDisplayModes() {
|
||||
return currencyConstants.allCurrencyDisplayModes;
|
||||
},
|
||||
isAutoUpdateExchangeRatesData: {
|
||||
get: function () {
|
||||
@@ -223,7 +234,7 @@ export default {
|
||||
self.logouting = true;
|
||||
self.$showLoading(() => self.logouting);
|
||||
|
||||
self.$store.dispatch('logout').then(() => {
|
||||
self.rootStore.logout().then(() => {
|
||||
self.logouting = false;
|
||||
self.$hideLoading();
|
||||
|
||||
|
||||
@@ -174,12 +174,23 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useRootStore } from '@/stores/index.js';
|
||||
import { useSettingsStore } from '@/stores/setting.js';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
|
||||
import { useExchangeRatesStore } from '@/stores/exchangeRates.js';
|
||||
|
||||
import datetimeConstants from '@/consts/datetime.js';
|
||||
import categoryConstants from '@/consts/category.js';
|
||||
import { getNameByKeyValue, copyArrayTo } from '@/lib/common.js';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'f7router'
|
||||
],
|
||||
data() {
|
||||
const self = this;
|
||||
const settingsStore = useSettingsStore();
|
||||
|
||||
return {
|
||||
user: {
|
||||
@@ -189,14 +200,14 @@ export default {
|
||||
email: '',
|
||||
nickname: '',
|
||||
language: self.$i18n.locale,
|
||||
defaultCurrency: self.$store.state.defaultSetting.currency,
|
||||
firstDayOfWeek: self.$constants.datetime.allWeekDays[self.$t('default.firstDayOfWeek')] ? self.$constants.datetime.allWeekDays[self.$t('default.firstDayOfWeek')].type : 0
|
||||
defaultCurrency: settingsStore.defaultSetting.currency,
|
||||
firstDayOfWeek: datetimeConstants.allWeekDays[self.$t('default.firstDayOfWeek')] ? datetimeConstants.allWeekDays[self.$t('default.firstDayOfWeek')].type : 0
|
||||
},
|
||||
submitting: false,
|
||||
presetCategories: {
|
||||
[self.$constants.category.allCategoryTypes.Income]: self.$utilities.copyArrayTo(self.$constants.category.defaultIncomeCategories, []),
|
||||
[self.$constants.category.allCategoryTypes.Expense]: self.$utilities.copyArrayTo(self.$constants.category.defaultExpenseCategories, []),
|
||||
[self.$constants.category.allCategoryTypes.Transfer]: self.$utilities.copyArrayTo(self.$constants.category.defaultTransferCategories, [])
|
||||
[categoryConstants.allCategoryTypes.Income]: copyArrayTo(categoryConstants.defaultIncomeCategories, []),
|
||||
[categoryConstants.allCategoryTypes.Expense]: copyArrayTo(categoryConstants.defaultExpenseCategories, []),
|
||||
[categoryConstants.allCategoryTypes.Transfer]: copyArrayTo(categoryConstants.defaultTransferCategories, [])
|
||||
},
|
||||
usePresetCategories: false,
|
||||
showPresetCategories: false,
|
||||
@@ -205,6 +216,7 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useRootStore, useSettingsStore, useTransactionCategoriesStore, useExchangeRatesStore),
|
||||
allLanguages() {
|
||||
return this.$locale.getAllLanguageInfos();
|
||||
},
|
||||
@@ -212,25 +224,25 @@ export default {
|
||||
return this.$locale.getAllCurrencies();
|
||||
},
|
||||
allWeekDays() {
|
||||
return this.$constants.datetime.allWeekDays;
|
||||
return datetimeConstants.allWeekDays;
|
||||
},
|
||||
currentLocale: {
|
||||
get: function () {
|
||||
return this.$i18n.locale;
|
||||
},
|
||||
set: function (value) {
|
||||
const isCurrencyDefault = this.user.defaultCurrency === this.$store.state.defaultSetting.currency;
|
||||
const isFirstWeekDayDefault = this.user.firstDayOfWeek === (this.$constants.datetime.allWeekDays[this.$t('default.firstDayOfWeek')] ? this.$constants.datetime.allWeekDays[this.$t('default.firstDayOfWeek')].type : 0);
|
||||
const isCurrencyDefault = this.user.defaultCurrency === this.settingsStore.defaultSetting.currency;
|
||||
const isFirstWeekDayDefault = this.user.firstDayOfWeek === (datetimeConstants.allWeekDays[this.$t('default.firstDayOfWeek')] ? datetimeConstants.allWeekDays[this.$t('default.firstDayOfWeek')].type : 0);
|
||||
|
||||
this.user.language = value;
|
||||
this.$locale.setLanguage(value);
|
||||
|
||||
if (isCurrencyDefault) {
|
||||
this.user.defaultCurrency = this.$store.state.defaultSetting.currency;
|
||||
this.user.defaultCurrency = this.settingsStore.defaultSetting.currency;
|
||||
}
|
||||
|
||||
if (isFirstWeekDayDefault) {
|
||||
this.user.firstDayOfWeek = this.$constants.datetime.allWeekDays[this.$t('default.firstDayOfWeek')] ? this.$constants.datetime.allWeekDays[this.$t('default.firstDayOfWeek')].type : 0;
|
||||
this.user.firstDayOfWeek = datetimeConstants.allWeekDays[this.$t('default.firstDayOfWeek')] ? datetimeConstants.allWeekDays[this.$t('default.firstDayOfWeek')].type : 0;
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -324,7 +336,7 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
self.$store.dispatch('register', {
|
||||
self.rootStore.register({
|
||||
user: self.user
|
||||
}).then(response => {
|
||||
if (!self.$user.isUserLogined()) {
|
||||
@@ -346,7 +358,7 @@ export default {
|
||||
}
|
||||
|
||||
if (self.$settings.isAutoUpdateExchangeRatesData()) {
|
||||
self.$store.dispatch('getLatestExchangeRates', { silent: true, force: false });
|
||||
self.exchangeRatesStore.getLatestExchangeRates({ silent: true, force: false });
|
||||
}
|
||||
|
||||
if (!self.usePresetCategories) {
|
||||
@@ -358,7 +370,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
self.$store.dispatch('addCategories', {
|
||||
self.transactionCategoriesStore.addCategories({
|
||||
categories: allCategories
|
||||
}).then(() => {
|
||||
self.submitting = false;
|
||||
@@ -383,17 +395,17 @@ export default {
|
||||
});
|
||||
},
|
||||
getDayOfWeekName(dayOfWeek) {
|
||||
const weekName = this.$utilities.getNameByKeyValue(this.$constants.datetime.allWeekDays, dayOfWeek, 'type', 'name');
|
||||
const weekName = getNameByKeyValue(datetimeConstants.allWeekDays, dayOfWeek, 'type', 'name');
|
||||
const i18nWeekNameKey = `datetime.${weekName}.long`;
|
||||
return this.$t(i18nWeekNameKey);
|
||||
},
|
||||
getCategoryTypeName(categoryType) {
|
||||
switch (categoryType) {
|
||||
case this.$constants.category.allCategoryTypes.Income.toString():
|
||||
case categoryConstants.allCategoryTypes.Income.toString():
|
||||
return this.$t('Income Categories');
|
||||
case this.$constants.category.allCategoryTypes.Expense.toString():
|
||||
case categoryConstants.allCategoryTypes.Expense.toString():
|
||||
return this.$t('Expense Categories');
|
||||
case this.$constants.category.allCategoryTypes.Transfer.toString():
|
||||
case categoryConstants.allCategoryTypes.Transfer.toString():
|
||||
return this.$t('Transfer Categories');
|
||||
default:
|
||||
return this.$t('Transaction Categories');
|
||||
|
||||
@@ -57,6 +57,16 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useRootStore } from '@/stores/index.js';
|
||||
import { useUserStore } from '@/stores/user.js';
|
||||
import { useTokensStore } from '@/stores/token.js';
|
||||
import { useExchangeRatesStore } from '@/stores/exchangeRates.js';
|
||||
|
||||
import logger from '@/lib/logger.js';
|
||||
import webauthn from '@/lib/webauthn.js';
|
||||
import { isModalShowing } from '@/lib/ui.mobile.js';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'f7router'
|
||||
@@ -67,6 +77,7 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useRootStore, useUserStore, useTokensStore, useExchangeRatesStore),
|
||||
version() {
|
||||
return 'v' + this.$version;
|
||||
},
|
||||
@@ -76,7 +87,7 @@ export default {
|
||||
isWebAuthnAvailable() {
|
||||
return this.$settings.isEnableApplicationLockWebAuthn()
|
||||
&& this.$user.getWebAuthnCredentialId()
|
||||
&& this.$webauthn.isSupported();
|
||||
&& webauthn.isSupported();
|
||||
},
|
||||
currentLanguageName() {
|
||||
const currentLocale = this.$i18n.locale;
|
||||
@@ -99,34 +110,34 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self.$webauthn.isSupported()) {
|
||||
if (!webauthn.isSupported()) {
|
||||
self.$toast('This device does not support WebAuthn');
|
||||
return;
|
||||
}
|
||||
|
||||
self.$showLoading();
|
||||
|
||||
self.$webauthn.verifyCredential(
|
||||
self.$store.state.currentUserInfo,
|
||||
webauthn.verifyCredential(
|
||||
self.userStore.currentUserInfo,
|
||||
self.$user.getWebAuthnCredentialId()
|
||||
).then(({ id, userName, userSecret }) => {
|
||||
self.$hideLoading();
|
||||
|
||||
self.$user.unlockTokenByWebAuthn(id, userName, userSecret);
|
||||
self.$store.dispatch('refreshTokenAndRevokeOldToken').then(response => {
|
||||
self.tokensStore.refreshTokenAndRevokeOldToken().then(response => {
|
||||
if (response.user && response.user.language) {
|
||||
self.$locale.setLanguage(response.user.language);
|
||||
}
|
||||
});
|
||||
|
||||
if (self.$settings.isAutoUpdateExchangeRatesData()) {
|
||||
self.$store.dispatch('getLatestExchangeRates', { silent: true, force: false });
|
||||
self.exchangeRatesStore.getLatestExchangeRates({ silent: true, force: false });
|
||||
}
|
||||
|
||||
router.refreshPage();
|
||||
}).catch(error => {
|
||||
self.$hideLoading();
|
||||
self.$logger.error('failed to use webauthn to verify', error);
|
||||
logger.error('failed to use webauthn to verify', error);
|
||||
|
||||
if (error.notSupported) {
|
||||
self.$toast('This device does not support WebAuthn');
|
||||
@@ -146,12 +157,12 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.$ui.isModalShowing()) {
|
||||
if (isModalShowing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const router = self.f7router;
|
||||
const user = self.$store.state.currentUserInfo;
|
||||
const user = self.userStore.currentUserInfo;
|
||||
|
||||
if (!user || !user.username) {
|
||||
self.$alert('An error has occurred');
|
||||
@@ -160,19 +171,19 @@ export default {
|
||||
|
||||
try {
|
||||
self.$user.unlockTokenByPinCode(user.username, pinCode);
|
||||
self.$store.dispatch('refreshTokenAndRevokeOldToken').then(response => {
|
||||
self.tokensStore.refreshTokenAndRevokeOldToken().then(response => {
|
||||
if (response.user && response.user.language) {
|
||||
self.$locale.setLanguage(response.user.language);
|
||||
}
|
||||
});
|
||||
|
||||
if (self.$settings.isAutoUpdateExchangeRatesData()) {
|
||||
self.$store.dispatch('getLatestExchangeRates', { silent: true, force: false });
|
||||
self.exchangeRatesStore.getLatestExchangeRates({ silent: true, force: false });
|
||||
}
|
||||
|
||||
router.refreshPage();
|
||||
} catch (ex) {
|
||||
self.$logger.error('failed to unlock by pin code', ex);
|
||||
logger.error('failed to unlock by pin code', ex);
|
||||
self.$toast('PIN code is wrong');
|
||||
}
|
||||
},
|
||||
@@ -181,10 +192,7 @@ export default {
|
||||
const router = self.f7router;
|
||||
|
||||
self.$confirm('Are you sure you want to re-login?', () => {
|
||||
self.$user.clearTokenAndUserInfo(true);
|
||||
self.$user.clearWebAuthnConfig();
|
||||
self.$store.dispatch('clearUserInfoState');
|
||||
self.$store.dispatch('resetState');
|
||||
self.rootStore.forceLogout();
|
||||
self.$settings.clearSettings();
|
||||
self.$locale.initLocale();
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<f7-nav-left :back-link="$t('Back')"></f7-nav-left>
|
||||
<f7-nav-title :title="$t(title)"></f7-nav-title>
|
||||
<f7-nav-right>
|
||||
<f7-link icon-f7="ellipsis" v-if="!editAccountId && account.type === $constants.account.allAccountTypes.MultiSubAccounts" @click="showMoreActionSheet = true"></f7-link>
|
||||
<f7-link icon-f7="ellipsis" v-if="!editAccountId && account.type === allAccountTypes.MultiSubAccounts" @click="showMoreActionSheet = true"></f7-link>
|
||||
<f7-link :class="{ 'disabled': isInputEmpty() || submitting }" :text="$t(saveButtonTitle)" @click="save"></f7-link>
|
||||
</f7-nav-right>
|
||||
</f7-navbar>
|
||||
@@ -42,7 +42,7 @@
|
||||
>
|
||||
<list-item-selection-sheet value-type="item"
|
||||
key-field="id" value-field="id" title-field="name"
|
||||
:items="allAccountTypes"
|
||||
:items="allAccountTypesArray"
|
||||
:title-i18n="true"
|
||||
v-model:show="showAccountTypeSheet"
|
||||
v-model="account.type">
|
||||
@@ -96,7 +96,7 @@
|
||||
<f7-list-input label="Description" type="textarea" placeholder="Your account description (optional)"></f7-list-input>
|
||||
</f7-list>
|
||||
|
||||
<f7-list form strong inset dividers class="margin-vertical" v-else-if="!loading && account.type === $constants.account.allAccountTypes.SingleAccount">
|
||||
<f7-list form strong inset dividers class="margin-vertical" v-else-if="!loading && account.type === allAccountTypes.SingleAccount">
|
||||
<f7-list-input
|
||||
type="text"
|
||||
clear-button
|
||||
@@ -180,11 +180,11 @@
|
||||
class="list-item-with-header-and-title"
|
||||
:class="{ 'disabled': editAccountId }"
|
||||
:header="$t('Account Balance')"
|
||||
:title="$locale.getDisplayCurrency(account.balance, account.currency)"
|
||||
:title="getAccountBalance(account)"
|
||||
@click="account.showBalanceSheet = true"
|
||||
>
|
||||
<number-pad-sheet :min-value="$constants.transaction.minAmount"
|
||||
:max-value="$constants.transaction.maxAmount"
|
||||
<number-pad-sheet :min-value="allowedMinAmount"
|
||||
:max-value="allowedMaxAmount"
|
||||
v-model:show="account.showBalanceSheet"
|
||||
v-model="account.balance"
|
||||
></number-pad-sheet>
|
||||
@@ -204,7 +204,7 @@
|
||||
></f7-list-input>
|
||||
</f7-list>
|
||||
|
||||
<f7-list form strong inset dividers class="margin-vertical" v-else-if="!loading && account.type === $constants.account.allAccountTypes.MultiSubAccounts">
|
||||
<f7-list form strong inset dividers class="margin-vertical" v-else-if="!loading && account.type === allAccountTypes.MultiSubAccounts">
|
||||
<f7-list-input
|
||||
type="text"
|
||||
clear-button
|
||||
@@ -277,7 +277,7 @@
|
||||
></f7-list-input>
|
||||
</f7-list>
|
||||
|
||||
<f7-block class="no-padding no-margin" v-if="!loading && account.type === $constants.account.allAccountTypes.MultiSubAccounts">
|
||||
<f7-block class="no-padding no-margin" v-if="!loading && account.type === allAccountTypes.MultiSubAccounts">
|
||||
<f7-list strong inset dividers class="subaccount-edit-list margin-vertical"
|
||||
:key="idx"
|
||||
v-for="(subAccount, idx) in subAccounts">
|
||||
@@ -373,11 +373,11 @@
|
||||
class="list-item-with-header-and-title"
|
||||
:class="{ 'disabled': editAccountId }"
|
||||
:header="$t('Sub Account Balance')"
|
||||
:title="$locale.getDisplayCurrency(subAccount.balance, subAccount.currency)"
|
||||
:title="getAccountBalance(subAccount)"
|
||||
@click="subAccount.showBalanceSheet = true"
|
||||
>
|
||||
<number-pad-sheet :min-value="$constants.transaction.minAmount"
|
||||
:max-value="$constants.transaction.maxAmount"
|
||||
<number-pad-sheet :min-value="allowedMinAmount"
|
||||
:max-value="allowedMaxAmount"
|
||||
v-model:show="subAccount.showBalanceSheet"
|
||||
v-model="subAccount.balance"
|
||||
></number-pad-sheet>
|
||||
@@ -420,13 +420,24 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useUserStore } from '@/stores/user.js';
|
||||
import { useAccountsStore } from '@/stores/account.js';
|
||||
|
||||
import accountConstants from '@/consts/account.js';
|
||||
import iconConstants from '@/consts/icon.js';
|
||||
import colorConstants from '@/consts/color.js';
|
||||
import currencyConstants from '@/consts/currency.js';
|
||||
import transactionConstants from '@/consts/transaction.js';
|
||||
import { getNameByKeyValue } from '@/lib/common.js';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'f7route',
|
||||
'f7router'
|
||||
],
|
||||
data() {
|
||||
const self = this;
|
||||
const userStore = useUserStore();
|
||||
|
||||
return {
|
||||
editAccountId: null,
|
||||
@@ -434,11 +445,11 @@ export default {
|
||||
loadingError: null,
|
||||
account: {
|
||||
category: 1,
|
||||
type: self.$constants.account.allAccountTypes.SingleAccount,
|
||||
type: accountConstants.allAccountTypes.SingleAccount,
|
||||
name: '',
|
||||
icon: self.$constants.icons.defaultAccountIconId,
|
||||
color: self.$constants.colors.defaultAccountColor,
|
||||
currency: self.$store.getters.currentUserDefaultCurrency,
|
||||
icon: iconConstants.defaultAccountIconId,
|
||||
color: colorConstants.defaultAccountColor,
|
||||
currency: userStore.currentUserDefaultCurrency,
|
||||
balance: 0,
|
||||
comment: '',
|
||||
visible: true,
|
||||
@@ -456,6 +467,7 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useUserStore, useAccountsStore),
|
||||
title() {
|
||||
if (!this.editAccountId) {
|
||||
return 'Add Account';
|
||||
@@ -470,26 +482,29 @@ export default {
|
||||
return 'Save';
|
||||
}
|
||||
},
|
||||
allAccountCategories() {
|
||||
return this.$constants.account.allCategories;
|
||||
},
|
||||
allAccountTypes() {
|
||||
return [{
|
||||
id: 1,
|
||||
name: 'Single Account'
|
||||
}, {
|
||||
id: 2,
|
||||
name: 'Multi Sub Accounts'
|
||||
}];
|
||||
return accountConstants.allAccountTypes;
|
||||
},
|
||||
allAccountCategories() {
|
||||
return accountConstants.allCategories;
|
||||
},
|
||||
allAccountTypesArray() {
|
||||
return accountConstants.allAccountTypesArray;
|
||||
},
|
||||
allAccountIcons() {
|
||||
return this.$constants.icons.allAccountIcons;
|
||||
return iconConstants.allAccountIcons;
|
||||
},
|
||||
allAccountColors() {
|
||||
return this.$constants.colors.allAccountColors;
|
||||
return colorConstants.allAccountColors;
|
||||
},
|
||||
allCurrencies() {
|
||||
return this.$locale.getAllCurrencies();
|
||||
},
|
||||
allowedMinAmount() {
|
||||
return transactionConstants.minAmount;
|
||||
},
|
||||
allowedMaxAmount() {
|
||||
return transactionConstants.maxAmount;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -511,7 +526,7 @@ export default {
|
||||
|
||||
self.editAccountId = query.id;
|
||||
|
||||
self.$store.dispatch('getAccount', {
|
||||
self.accountsStore.getAccount({
|
||||
accountId: self.editAccountId
|
||||
}).then(account => {
|
||||
self.account.id = account.id;
|
||||
@@ -568,7 +583,7 @@ export default {
|
||||
addSubAccount() {
|
||||
const self = this;
|
||||
|
||||
if (self.account.type !== self.$constants.account.allAccountTypes.MultiSubAccounts) {
|
||||
if (self.account.type !== this.allAccountTypes.MultiSubAccounts) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -578,7 +593,7 @@ export default {
|
||||
name: '',
|
||||
icon: self.account.icon,
|
||||
color: self.account.color,
|
||||
currency: self.$store.getters.currentUserDefaultCurrency,
|
||||
currency: self.userStore.currentUserDefaultCurrency,
|
||||
balance: 0,
|
||||
comment: '',
|
||||
visible: true,
|
||||
@@ -614,7 +629,7 @@ export default {
|
||||
|
||||
let problemMessage = self.getInputEmptyProblemMessage(self.account, false);
|
||||
|
||||
if (!problemMessage && self.account.type === self.$constants.account.allAccountTypes.MultiSubAccounts) {
|
||||
if (!problemMessage && self.account.type === self.allAccountTypes.MultiSubAccounts) {
|
||||
for (let i = 0; i < self.subAccounts.length; i++) {
|
||||
problemMessage = self.getInputEmptyProblemMessage(self.subAccounts[i], true);
|
||||
|
||||
@@ -634,12 +649,12 @@ export default {
|
||||
|
||||
const subAccounts = [];
|
||||
|
||||
if (self.account.type === self.$constants.account.allAccountTypes.MultiSubAccounts) {
|
||||
if (self.account.type === self.allAccountTypes.MultiSubAccounts) {
|
||||
for (let i = 0; i < self.subAccounts.length; i++) {
|
||||
const subAccount = self.subAccounts[i];
|
||||
const submitAccount = {
|
||||
category: self.account.category,
|
||||
type: self.$constants.account.allAccountTypes.SingleAccount,
|
||||
type: self.allAccountTypes.SingleAccount,
|
||||
name: subAccount.name,
|
||||
icon: subAccount.icon,
|
||||
color: subAccount.color,
|
||||
@@ -663,10 +678,10 @@ export default {
|
||||
name: self.account.name,
|
||||
icon: self.account.icon,
|
||||
color: self.account.color,
|
||||
currency: self.account.type === self.$constants.account.allAccountTypes.SingleAccount ? self.account.currency : self.$constants.currency.parentAccountCurrencyPlaceholder,
|
||||
balance: self.account.type === self.$constants.account.allAccountTypes.SingleAccount ? self.account.balance : 0,
|
||||
currency: self.account.type === self.allAccountTypes.SingleAccount ? self.account.currency : currencyConstants.parentAccountCurrencyPlaceholder,
|
||||
balance: self.account.type === self.allAccountTypes.SingleAccount ? self.account.balance : 0,
|
||||
comment: self.account.comment,
|
||||
subAccounts: self.account.type === self.$constants.account.allAccountTypes.SingleAccount ? null : subAccounts,
|
||||
subAccounts: self.account.type === self.allAccountTypes.SingleAccount ? null : subAccounts,
|
||||
};
|
||||
|
||||
if (self.editAccountId) {
|
||||
@@ -674,7 +689,7 @@ export default {
|
||||
submitAccount.hidden = !self.account.visible;
|
||||
}
|
||||
|
||||
self.$store.dispatch('saveAccount', {
|
||||
self.accountsStore.saveAccount({
|
||||
account: submitAccount
|
||||
}).then(() => {
|
||||
self.submitting = false;
|
||||
@@ -697,19 +712,20 @@ export default {
|
||||
});
|
||||
},
|
||||
getAccountTypeName(accountType) {
|
||||
const typeName = this.$utilities.getNameByKeyValue(this.allAccountTypes, accountType, 'id', 'name');
|
||||
const typeName = getNameByKeyValue(this.allAccountTypesArray, accountType, 'id', 'name');
|
||||
return this.$t(typeName);
|
||||
},
|
||||
getAccountCategoryName(accountCategory) {
|
||||
const categoryName = this.$utilities.getNameByKeyValue(this.allAccountCategories, accountCategory, 'id', 'name');
|
||||
const categoryName = getNameByKeyValue(this.allAccountCategories, accountCategory, 'id', 'name');
|
||||
return this.$t(categoryName);
|
||||
},
|
||||
getAccountBalance(account) {
|
||||
return this.$locale.getDisplayCurrency(account.balance, account.currency)
|
||||
},
|
||||
chooseSuitableIcon(oldCategory, newCategory) {
|
||||
const allCategories = this.$constants.account.allCategories;
|
||||
|
||||
for (let i = 0; i < allCategories.length; i++) {
|
||||
if (allCategories[i].id === oldCategory) {
|
||||
if (this.account.icon !== allCategories[i].defaultAccountIconId) {
|
||||
for (let i = 0; i < this.allAccountCategories.length; i++) {
|
||||
if (this.allAccountCategories[i].id === oldCategory) {
|
||||
if (this.account.icon !== this.allAccountCategories[i].defaultAccountIconId) {
|
||||
return;
|
||||
} else {
|
||||
break;
|
||||
@@ -717,9 +733,9 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < allCategories.length; i++) {
|
||||
if (allCategories[i].id === newCategory) {
|
||||
this.account.icon = allCategories[i].defaultAccountIconId;
|
||||
for (let i = 0; i < this.allAccountCategories.length; i++) {
|
||||
if (this.allAccountCategories[i].id === newCategory) {
|
||||
this.account.icon = this.allAccountCategories[i].defaultAccountIconId;
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -730,7 +746,7 @@ export default {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.account.type === this.$constants.account.allAccountTypes.MultiSubAccounts) {
|
||||
if (this.account.type === this.allAccountTypes.MultiSubAccounts) {
|
||||
for (let i = 0; i < this.subAccounts.length; i++) {
|
||||
const isSubAccountEmpty = !!this.getInputEmptyProblemMessage(this.subAccounts[i], true);
|
||||
|
||||
@@ -749,7 +765,7 @@ export default {
|
||||
return 'Account type cannot be empty';
|
||||
} else if (!account.name) {
|
||||
return 'Account name cannot be empty';
|
||||
} else if (account.type === this.$constants.account.allAccountTypes.SingleAccount && !account.currency) {
|
||||
} else if (account.type === this.allAccountTypes.SingleAccount && !account.currency) {
|
||||
return 'Account currency cannot be empty';
|
||||
} else {
|
||||
return null;
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
</p>
|
||||
<p class="no-margin">
|
||||
<span class="net-assets" v-if="loading">0.00 USD</span>
|
||||
<span class="net-assets" v-else-if="!loading">{{ $locale.getDisplayCurrency(netAssets, defaultCurrency) }}</span>
|
||||
<span class="net-assets" v-else-if="!loading">{{ netAssets }}</span>
|
||||
<f7-link class="margin-left-half" @click="toggleShowAccountBalance()">
|
||||
<f7-icon :f7="showAccountBalance ? 'eye_slash_fill' : 'eye_fill'" size="18px"></f7-icon>
|
||||
</f7-link>
|
||||
@@ -29,10 +29,10 @@
|
||||
</small>
|
||||
<small class="account-overview-info" v-else-if="!loading">
|
||||
<span>{{ $t('Total assets') }}</span>
|
||||
<span>{{ $locale.getDisplayCurrency(totalAssets, defaultCurrency) }}</span>
|
||||
<span>{{ totalAssets }}</span>
|
||||
<span>|</span>
|
||||
<span>{{ $t('Total liabilities') }}</span>
|
||||
<span>{{ $locale.getDisplayCurrency(totalLiabilities, defaultCurrency) }}</span>
|
||||
<span>{{ totalLiabilities }}</span>
|
||||
</small>
|
||||
</p>
|
||||
</f7-card-header>
|
||||
@@ -68,21 +68,21 @@
|
||||
<f7-list-item group-title :sortable="false">
|
||||
<small>
|
||||
<span>{{ $t(accountCategory.name) }}</span>
|
||||
<span style="margin-left: 10px">{{ $locale.getDisplayCurrency(accountCategoryTotalBalance(accountCategory), defaultCurrency) }}</span>
|
||||
<span style="margin-left: 10px">{{ accountCategoryTotalBalance(accountCategory) }}</span>
|
||||
</small>
|
||||
</f7-list-item>
|
||||
<f7-list-item swipeout
|
||||
class="nested-list-item"
|
||||
:id="getAccountDomId(account)"
|
||||
:class="{ 'has-child-list-item': account.type === $constants.account.allAccountTypes.MultiSubAccounts && hasVisibleSubAccount(account), 'actual-first-child': account.id === firstShowingIds.accounts[accountCategory.id], 'actual-last-child': account.id === lastShowingIds.accounts[accountCategory.id] }"
|
||||
:after="$locale.getDisplayCurrency(accountBalance(account), account.currency)"
|
||||
:class="{ 'has-child-list-item': account.type === allAccountTypes.MultiSubAccounts && hasVisibleSubAccount(account), 'actual-first-child': account.id === firstShowingIds.accounts[accountCategory.id], 'actual-last-child': account.id === lastShowingIds.accounts[accountCategory.id] }"
|
||||
:after="accountBalance(account)"
|
||||
:link="!sortable ? '/transaction/list?accountId=' + account.id : null"
|
||||
:key="account.id"
|
||||
v-for="account in categorizedAccounts[accountCategory.id].accounts"
|
||||
v-show="showHidden || !account.hidden"
|
||||
@taphold="setSortable()"
|
||||
>
|
||||
<template #media v-if="account.type !== $constants.account.allAccountTypes.MultiSubAccounts || !hasVisibleSubAccount(account)">
|
||||
<template #media v-if="account.type !== allAccountTypes.MultiSubAccounts || !hasVisibleSubAccount(account)">
|
||||
<ItemIcon icon-type="account" :icon-id="account.icon" :color="account.color">
|
||||
<f7-badge color="gray" class="right-bottom-icon" v-if="account.hidden">
|
||||
<f7-icon f7="eye_slash_fill"></f7-icon>
|
||||
@@ -93,7 +93,7 @@
|
||||
<template #title>
|
||||
<div class="display-flex padding-top-half padding-bottom-half">
|
||||
<ItemIcon icon-type="account" :icon-id="account.icon" :color="account.color"
|
||||
v-if="account.type === $constants.account.allAccountTypes.MultiSubAccounts && hasVisibleSubAccount(account)">
|
||||
v-if="account.type === allAccountTypes.MultiSubAccounts && hasVisibleSubAccount(account)">
|
||||
<f7-badge color="gray" class="right-bottom-icon" v-if="account.hidden">
|
||||
<f7-icon f7="eye_slash_fill"></f7-icon>
|
||||
</f7-badge>
|
||||
@@ -103,12 +103,12 @@
|
||||
<div class="item-footer" v-if="account.comment">{{ account.comment }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<li v-if="account.type === $constants.account.allAccountTypes.MultiSubAccounts">
|
||||
<li v-if="account.type === allAccountTypes.MultiSubAccounts">
|
||||
<ul class="no-padding">
|
||||
<f7-list-item class="no-sortable nested-list-item-child"
|
||||
:class="{ 'actual-first-child': subAccount.id === firstShowingIds.subAccounts[account.id], 'actual-last-child': subAccount.id === lastShowingIds.subAccounts[account.id] }"
|
||||
:id="getAccountDomId(subAccount)"
|
||||
:title="subAccount.name" :footer="subAccount.comment" :after="$locale.getDisplayCurrency(accountBalance(subAccount), subAccount.currency)"
|
||||
:title="subAccount.name" :footer="subAccount.comment" :after="accountBalance(subAccount)"
|
||||
:link="!sortable ? '/transaction/list?accountId=' + subAccount.id : null"
|
||||
:key="subAccount.id"
|
||||
v-for="subAccount in account.subAccounts"
|
||||
@@ -165,6 +165,14 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useUserStore } from '@/stores/user.js';
|
||||
import { useAccountsStore } from '@/stores/account.js';
|
||||
import { useExchangeRatesStore } from '@/stores/exchangeRates.js';
|
||||
|
||||
import accountConstants from '@/consts/account.js';
|
||||
import { onSwipeoutDeleted } from '@/lib/ui.mobile.js';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'f7router'
|
||||
@@ -184,194 +192,46 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useUserStore, useAccountsStore, useExchangeRatesStore),
|
||||
defaultCurrency() {
|
||||
return this.$store.getters.currentUserDefaultCurrency;
|
||||
return this.userStore.currentUserDefaultCurrency;
|
||||
},
|
||||
allAccountTypes() {
|
||||
return accountConstants.allAccountTypes;
|
||||
},
|
||||
allAccountCategories() {
|
||||
return this.$constants.account.allCategories;
|
||||
return accountConstants.allCategories;
|
||||
},
|
||||
categorizedAccounts() {
|
||||
return this.$store.state.allCategorizedAccounts;
|
||||
return this.accountsStore.allCategorizedAccounts;
|
||||
},
|
||||
allAccountCount() {
|
||||
return this.$store.getters.allAvailableAccountsCount;
|
||||
return this.accountsStore.allAvailableAccountsCount;
|
||||
},
|
||||
firstShowingIds() {
|
||||
const ret = {
|
||||
accounts: {},
|
||||
subAccounts: {}
|
||||
};
|
||||
|
||||
for (let category in this.categorizedAccounts) {
|
||||
if (!Object.prototype.hasOwnProperty.call(this.categorizedAccounts, category)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!this.categorizedAccounts[category] || !this.categorizedAccounts[category].accounts) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const accounts = this.categorizedAccounts[category].accounts;
|
||||
|
||||
for (let i = 0; i < accounts.length; i++) {
|
||||
const account = accounts[i];
|
||||
|
||||
if (account.type === this.$constants.account.allAccountTypes.MultiSubAccounts && account.subAccounts) {
|
||||
for (let j = 0; j < account.subAccounts.length; j++) {
|
||||
const subAccount = account.subAccounts[j];
|
||||
|
||||
if (this.showHidden || !subAccount.hidden) {
|
||||
ret.subAccounts[account.id] = subAccount.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.showHidden || !account.hidden) {
|
||||
ret.accounts[category] = account.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return this.accountsStore.getFirstShowingIds(this.showHidden);
|
||||
},
|
||||
lastShowingIds() {
|
||||
const ret = {
|
||||
accounts: {},
|
||||
subAccounts: {}
|
||||
};
|
||||
|
||||
for (let category in this.categorizedAccounts) {
|
||||
if (!Object.prototype.hasOwnProperty.call(this.categorizedAccounts, category)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!this.categorizedAccounts[category] || !this.categorizedAccounts[category].accounts) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const accounts = this.categorizedAccounts[category].accounts;
|
||||
|
||||
for (let i = accounts.length - 1; i >= 0; i--) {
|
||||
const account = accounts[i];
|
||||
|
||||
if (account.type === this.$constants.account.allAccountTypes.MultiSubAccounts && account.subAccounts) {
|
||||
for (let j = account.subAccounts.length - 1; j >= 0; j--) {
|
||||
const subAccount = account.subAccounts[j];
|
||||
|
||||
if (this.showHidden || !subAccount.hidden) {
|
||||
ret.subAccounts[account.id] = subAccount.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.showHidden || !account.hidden) {
|
||||
ret.accounts[category] = account.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return this.accountsStore.getLastShowingIds(this.showHidden);
|
||||
},
|
||||
noAvailableAccount() {
|
||||
if (this.showHidden) {
|
||||
return this.$store.getters.allAvailableAccountsCount < 1;
|
||||
return this.accountsStore.allAvailableAccountsCount < 1;
|
||||
} else {
|
||||
return this.$store.getters.allVisibleAccountsCount < 1;
|
||||
return this.accountsStore.allVisibleAccountsCount < 1;
|
||||
}
|
||||
},
|
||||
netAssets() {
|
||||
if (!this.showAccountBalance) {
|
||||
return '***';
|
||||
}
|
||||
|
||||
const accountsBalance = this.$utilities.getAllFilteredAccountsBalance(this.categorizedAccounts, () => true);
|
||||
let netAssets = 0;
|
||||
let hasUnCalculatedAmount = false;
|
||||
|
||||
for (let i = 0; i < accountsBalance.length; i++) {
|
||||
if (accountsBalance[i].currency === this.defaultCurrency) {
|
||||
netAssets += accountsBalance[i].balance;
|
||||
} else {
|
||||
const balance = this.$store.getters.getExchangedAmount(accountsBalance[i].balance, accountsBalance[i].currency, this.defaultCurrency);
|
||||
|
||||
if (!this.$utilities.isNumber(balance)) {
|
||||
hasUnCalculatedAmount = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
netAssets += Math.floor(balance);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasUnCalculatedAmount) {
|
||||
return netAssets + '+';
|
||||
} else {
|
||||
return netAssets;
|
||||
}
|
||||
const netAssets = this.accountsStore.getNetAssets(this.showAccountBalance);
|
||||
return this.$locale.getDisplayCurrency(netAssets, this.defaultCurrency);
|
||||
},
|
||||
totalAssets() {
|
||||
if (!this.showAccountBalance) {
|
||||
return '***';
|
||||
}
|
||||
|
||||
const accountsBalance = this.$utilities.getAllFilteredAccountsBalance(this.categorizedAccounts, account => account.isAsset);
|
||||
let totalAssets = 0;
|
||||
let hasUnCalculatedAmount = false;
|
||||
|
||||
for (let i = 0; i < accountsBalance.length; i++) {
|
||||
if (accountsBalance[i].currency === this.defaultCurrency) {
|
||||
totalAssets += accountsBalance[i].balance;
|
||||
} else {
|
||||
const balance = this.$store.getters.getExchangedAmount(accountsBalance[i].balance, accountsBalance[i].currency, this.defaultCurrency);
|
||||
|
||||
if (!this.$utilities.isNumber(balance)) {
|
||||
hasUnCalculatedAmount = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
totalAssets += Math.floor(balance);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasUnCalculatedAmount) {
|
||||
return totalAssets + '+';
|
||||
} else {
|
||||
return totalAssets;
|
||||
}
|
||||
const totalAssets = this.accountsStore.getTotalAssets(this.showAccountBalance);
|
||||
return this.$locale.getDisplayCurrency(totalAssets, this.defaultCurrency);
|
||||
},
|
||||
totalLiabilities() {
|
||||
if (!this.showAccountBalance) {
|
||||
return '***';
|
||||
}
|
||||
|
||||
const accountsBalance = this.$utilities.getAllFilteredAccountsBalance(this.categorizedAccounts, account => account.isLiability);
|
||||
let totalLiabilities = 0;
|
||||
let hasUnCalculatedAmount = false;
|
||||
|
||||
for (let i = 0; i < accountsBalance.length; i++) {
|
||||
if (accountsBalance[i].currency === this.defaultCurrency) {
|
||||
totalLiabilities -= accountsBalance[i].balance;
|
||||
} else {
|
||||
const balance = this.$store.getters.getExchangedAmount(accountsBalance[i].balance, accountsBalance[i].currency, this.defaultCurrency);
|
||||
|
||||
if (!this.$utilities.isNumber(balance)) {
|
||||
hasUnCalculatedAmount = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
totalLiabilities -= Math.floor(balance);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasUnCalculatedAmount) {
|
||||
return totalLiabilities + '+';
|
||||
} else {
|
||||
return totalLiabilities;
|
||||
}
|
||||
const totalLiabilities = this.accountsStore.getTotalLiabilities(this.showAccountBalance);
|
||||
return this.$locale.getDisplayCurrency(totalLiabilities, this.defaultCurrency);
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@@ -379,7 +239,7 @@ export default {
|
||||
|
||||
self.loading = true;
|
||||
|
||||
self.$store.dispatch('loadAllAccounts', {
|
||||
self.accountsStore.loadAllAccounts({
|
||||
force: false
|
||||
}).then(() => {
|
||||
self.loading = false;
|
||||
@@ -394,7 +254,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
onPageAfterIn() {
|
||||
if (this.$store.state.accountListStateInvalid && !this.loading) {
|
||||
if (this.accountsStore.accountListStateInvalid && !this.loading) {
|
||||
this.reload(null);
|
||||
}
|
||||
|
||||
@@ -408,7 +268,7 @@ export default {
|
||||
|
||||
const self = this;
|
||||
|
||||
self.$store.dispatch('loadAllAccounts', {
|
||||
self.accountsStore.loadAllAccounts({
|
||||
force: true
|
||||
}).then(() => {
|
||||
if (done) {
|
||||
@@ -425,99 +285,22 @@ export default {
|
||||
});
|
||||
},
|
||||
hasAccount(accountCategory, visibleOnly) {
|
||||
if (!this.categorizedAccounts[accountCategory.id] ||
|
||||
!this.categorizedAccounts[accountCategory.id].accounts ||
|
||||
!this.categorizedAccounts[accountCategory.id].accounts.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let shownCount = 0;
|
||||
|
||||
for (let i = 0; i < this.categorizedAccounts[accountCategory.id].accounts.length; i++) {
|
||||
const account = this.categorizedAccounts[accountCategory.id].accounts[i];
|
||||
|
||||
if (!visibleOnly || !account.hidden) {
|
||||
shownCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return shownCount > 0;
|
||||
return this.accountsStore.hasAccount(accountCategory, visibleOnly);
|
||||
},
|
||||
hasVisibleSubAccount(account) {
|
||||
if (!account || account.type !== this.$constants.account.allAccountTypes.MultiSubAccounts || !account.subAccounts) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < account.subAccounts.length; i++) {
|
||||
if (this.showHidden || !account.subAccounts[i].hidden) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return this.accountsStore.hasVisibleSubAccount(this.showHidden, account);
|
||||
},
|
||||
toggleShowAccountBalance() {
|
||||
this.showAccountBalance = !this.showAccountBalance;
|
||||
this.$settings.setShowAccountBalance(this.showAccountBalance);
|
||||
},
|
||||
accountBalance(account) {
|
||||
if (account.type !== this.$constants.account.allAccountTypes.SingleAccount) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.showAccountBalance) {
|
||||
if (account.isAsset) {
|
||||
return account.balance;
|
||||
} else if (account.isLiability) {
|
||||
return -account.balance;
|
||||
} else {
|
||||
return account.balance;
|
||||
}
|
||||
} else {
|
||||
return '***';
|
||||
}
|
||||
const balance = this.accountsStore.getAccountBalance(this.showAccountBalance, account);
|
||||
return this.$locale.getDisplayCurrency(balance, account.currency);
|
||||
},
|
||||
accountCategoryTotalBalance(accountCategory) {
|
||||
if (!this.showAccountBalance) {
|
||||
return '***';
|
||||
}
|
||||
|
||||
const accountsBalance = this.$utilities.getAllFilteredAccountsBalance(this.categorizedAccounts, account => account.category === accountCategory.id);
|
||||
let totalBalance = 0;
|
||||
let hasUnCalculatedAmount = false;
|
||||
|
||||
for (let i = 0; i < accountsBalance.length; i++) {
|
||||
if (accountsBalance[i].currency === this.defaultCurrency) {
|
||||
if (accountsBalance[i].isAsset) {
|
||||
totalBalance += accountsBalance[i].balance;
|
||||
} else if (accountsBalance[i].isLiability) {
|
||||
totalBalance -= accountsBalance[i].balance;
|
||||
} else {
|
||||
totalBalance += accountsBalance[i].balance;
|
||||
}
|
||||
} else {
|
||||
const balance = this.$store.getters.getExchangedAmount(accountsBalance[i].balance, accountsBalance[i].currency, this.defaultCurrency);
|
||||
|
||||
if (!this.$utilities.isNumber(balance)) {
|
||||
hasUnCalculatedAmount = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (accountsBalance[i].isAsset) {
|
||||
totalBalance += Math.floor(balance);
|
||||
} else if (accountsBalance[i].isLiability) {
|
||||
totalBalance -= Math.floor(balance);
|
||||
} else {
|
||||
totalBalance += Math.floor(balance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasUnCalculatedAmount) {
|
||||
return totalBalance + '+';
|
||||
} else {
|
||||
return totalBalance;
|
||||
}
|
||||
const totalBalance = this.accountsStore.getAccountCategoryTotalBalance(this.showAccountBalance, accountCategory);
|
||||
return this.$locale.getDisplayCurrency(totalBalance, this.defaultCurrency);
|
||||
},
|
||||
setSortable() {
|
||||
if (this.sortable) {
|
||||
@@ -543,7 +326,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
self.$store.dispatch('changeAccountDisplayOrder', {
|
||||
self.accountsStore.changeAccountDisplayOrder({
|
||||
accountId: id,
|
||||
from: event.from - 1, // first item in the list is title, so the index need minus one
|
||||
to: event.to - 1
|
||||
@@ -565,7 +348,7 @@ export default {
|
||||
self.displayOrderSaving = true;
|
||||
self.$showLoading();
|
||||
|
||||
self.$store.dispatch('updateAccountDisplayOrders').then(() => {
|
||||
self.accountsStore.updateAccountDisplayOrders().then(() => {
|
||||
self.displayOrderSaving = false;
|
||||
self.$hideLoading();
|
||||
|
||||
@@ -589,7 +372,7 @@ export default {
|
||||
|
||||
self.$showLoading();
|
||||
|
||||
self.$store.dispatch('hideAccount', {
|
||||
self.accountsStore.hideAccount({
|
||||
account: account,
|
||||
hidden: hidden
|
||||
}).then(() => {
|
||||
@@ -620,10 +403,10 @@ export default {
|
||||
self.accountToDelete = null;
|
||||
self.$showLoading();
|
||||
|
||||
self.$store.dispatch('deleteAccount', {
|
||||
self.accountsStore.deleteAccount({
|
||||
account: account,
|
||||
beforeResolve: (done) => {
|
||||
self.$ui.onSwipeoutDeleted(self.getAccountDomId(account), done);
|
||||
onSwipeoutDeleted(self.getAccountDomId(account), done);
|
||||
}
|
||||
}).then(() => {
|
||||
self.$hideLoading();
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'f7router'
|
||||
@@ -28,12 +31,15 @@ export default {
|
||||
loadingError: null
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useTransactionCategoriesStore)
|
||||
},
|
||||
created() {
|
||||
const self = this;
|
||||
|
||||
self.loading = true;
|
||||
|
||||
self.$store.dispatch('loadAllCategories', {
|
||||
self.transactionCategoriesStore.loadAllCategories({
|
||||
force: false
|
||||
}).then(() => {
|
||||
self.loading = false;
|
||||
@@ -53,7 +59,7 @@ export default {
|
||||
reload(done) {
|
||||
const self = this;
|
||||
|
||||
self.$store.dispatch('loadAllCategories', {
|
||||
self.transactionCategoriesStore.loadAllCategories({
|
||||
force: true
|
||||
}).then(() => {
|
||||
if (done) {
|
||||
|
||||
@@ -128,6 +128,13 @@
|
||||
</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: [
|
||||
'f7route',
|
||||
@@ -145,8 +152,8 @@ export default {
|
||||
type: parseInt(query.type),
|
||||
name: '',
|
||||
parentId: query.parentId,
|
||||
icon: self.$constants.icons.defaultCategoryIconId,
|
||||
color: self.$constants.colors.defaultCategoryColor,
|
||||
icon: iconConstants.defaultCategoryIconId,
|
||||
color: colorConstants.defaultCategoryColor,
|
||||
comment: '',
|
||||
visible: true,
|
||||
showIconSelectionSheet: false,
|
||||
@@ -156,6 +163,7 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useTransactionCategoriesStore),
|
||||
title() {
|
||||
if (!this.editCategoryId) {
|
||||
if (this.category.parentId === '0') {
|
||||
@@ -175,10 +183,10 @@ export default {
|
||||
}
|
||||
},
|
||||
allCategoryIcons() {
|
||||
return this.$constants.icons.allCategoryIcons;
|
||||
return iconConstants.allCategoryIcons;
|
||||
},
|
||||
allCategoryColors() {
|
||||
return this.$constants.colors.allCategoryColors;
|
||||
return colorConstants.allCategoryColors;
|
||||
},
|
||||
inputIsEmpty() {
|
||||
return !!this.inputEmptyProblemMessage;
|
||||
@@ -205,7 +213,7 @@ export default {
|
||||
self.loading = true;
|
||||
|
||||
self.editCategoryId = query.id;
|
||||
self.$store.dispatch('getCategory', {
|
||||
self.transactionCategoriesStore.getCategory({
|
||||
categoryId: self.editCategoryId
|
||||
}).then(category => {
|
||||
self.category.id = category.id;
|
||||
@@ -229,9 +237,9 @@ export default {
|
||||
} else if (query.parentId) {
|
||||
const categoryType = parseInt(query.type);
|
||||
|
||||
if (categoryType !== this.$constants.category.allCategoryTypes.Income &&
|
||||
categoryType !== this.$constants.category.allCategoryTypes.Expense &&
|
||||
categoryType !== this.$constants.category.allCategoryTypes.Transfer) {
|
||||
if (categoryType !== categoryConstants.allCategoryTypes.Income &&
|
||||
categoryType !== categoryConstants.allCategoryTypes.Expense &&
|
||||
categoryType !== categoryConstants.allCategoryTypes.Transfer) {
|
||||
self.$toast('Parameter Invalid');
|
||||
self.loadingError = 'Parameter Invalid';
|
||||
return;
|
||||
@@ -272,7 +280,7 @@ export default {
|
||||
submitCategory.hidden = !self.category.visible;
|
||||
}
|
||||
|
||||
self.$store.dispatch('saveCategory', {
|
||||
self.transactionCategoriesStore.saveCategory({
|
||||
category: submitCategory
|
||||
}).then(() => {
|
||||
self.submitting = false;
|
||||
|
||||
@@ -87,6 +87,12 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
|
||||
|
||||
import categoryConstants from '@/consts/category.js';
|
||||
import { onSwipeoutDeleted } from '@/lib/ui.mobile.js';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'f7route',
|
||||
@@ -109,19 +115,20 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useTransactionCategoriesStore),
|
||||
categories() {
|
||||
if (!this.categoryId || this.categoryId === '' || this.categoryId === '0') {
|
||||
if (!this.$store.state.allTransactionCategories || !this.$store.state.allTransactionCategories[this.categoryType]) {
|
||||
if (!this.transactionCategoriesStore.allTransactionCategories || !this.transactionCategoriesStore.allTransactionCategories[this.categoryType]) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return this.$store.state.allTransactionCategories[this.categoryType];
|
||||
return this.transactionCategoriesStore.allTransactionCategories[this.categoryType];
|
||||
} else if (this.categoryId && this.categoryId !== '' && this.categoryId !== '0') {
|
||||
if (!this.$store.state.allTransactionCategoriesMap || !this.$store.state.allTransactionCategoriesMap[this.categoryId]) {
|
||||
if (!this.transactionCategoriesStore.allTransactionCategoriesMap || !this.transactionCategoriesStore.allTransactionCategoriesMap[this.categoryId]) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return this.$store.state.allTransactionCategoriesMap[this.categoryId].subCategories;
|
||||
return this.transactionCategoriesStore.allTransactionCategoriesMap[this.categoryId].subCategories;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
@@ -130,13 +137,13 @@ export default {
|
||||
let title = '';
|
||||
|
||||
switch (this.categoryType) {
|
||||
case this.$constants.category.allCategoryTypes.Income:
|
||||
case categoryConstants.allCategoryTypes.Income:
|
||||
title = 'Income';
|
||||
break;
|
||||
case this.$constants.category.allCategoryTypes.Expense:
|
||||
case categoryConstants.allCategoryTypes.Expense:
|
||||
title = 'Expense';
|
||||
break;
|
||||
case this.$constants.category.allCategoryTypes.Transfer:
|
||||
case categoryConstants.allCategoryTypes.Transfer:
|
||||
title = 'Transfer';
|
||||
break;
|
||||
default:
|
||||
@@ -189,9 +196,9 @@ export default {
|
||||
|
||||
self.categoryType = parseInt(query.type);
|
||||
|
||||
if (self.categoryType !== this.$constants.category.allCategoryTypes.Income &&
|
||||
self.categoryType !== this.$constants.category.allCategoryTypes.Expense &&
|
||||
self.categoryType !== this.$constants.category.allCategoryTypes.Transfer) {
|
||||
if (self.categoryType !== categoryConstants.allCategoryTypes.Income &&
|
||||
self.categoryType !== categoryConstants.allCategoryTypes.Expense &&
|
||||
self.categoryType !== categoryConstants.allCategoryTypes.Transfer) {
|
||||
self.$toast('Parameter Invalid');
|
||||
self.loadingError = 'Parameter Invalid';
|
||||
return;
|
||||
@@ -207,7 +214,7 @@ export default {
|
||||
|
||||
self.loading = true;
|
||||
|
||||
self.$store.dispatch('loadAllCategories', {
|
||||
self.transactionCategoriesStore.loadAllCategories({
|
||||
force: false
|
||||
}).then(() => {
|
||||
self.loading = false;
|
||||
@@ -222,7 +229,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
onPageAfterIn() {
|
||||
if (this.$store.state.transactionCategoryListStateInvalid && !this.loading) {
|
||||
if (this.transactionCategoriesStore.transactionCategoryListStateInvalid && !this.loading) {
|
||||
this.reload(null);
|
||||
}
|
||||
|
||||
@@ -236,7 +243,7 @@ export default {
|
||||
|
||||
const self = this;
|
||||
|
||||
self.$store.dispatch('loadAllCategories', {
|
||||
self.transactionCategoriesStore.loadAllCategories({
|
||||
force: true
|
||||
}).then(() => {
|
||||
if (done) {
|
||||
@@ -276,7 +283,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
self.$store.dispatch('changeCategoryDisplayOrder', {
|
||||
self.transactionCategoriesStore.changeCategoryDisplayOrder({
|
||||
categoryId: id,
|
||||
from: event.from,
|
||||
to: event.to
|
||||
@@ -298,7 +305,7 @@ export default {
|
||||
self.displayOrderSaving = true;
|
||||
self.$showLoading();
|
||||
|
||||
self.$store.dispatch('updateCategoryDisplayOrders', {
|
||||
self.transactionCategoriesStore.updateCategoryDisplayOrders({
|
||||
type: self.categoryType,
|
||||
parentId: self.categoryId,
|
||||
}).then(() => {
|
||||
@@ -325,7 +332,7 @@ export default {
|
||||
|
||||
self.$showLoading();
|
||||
|
||||
self.$store.dispatch('hideCategory', {
|
||||
self.transactionCategoriesStore.hideCategory({
|
||||
category: category,
|
||||
hidden: hidden
|
||||
}).then(() => {
|
||||
@@ -356,10 +363,10 @@ export default {
|
||||
self.categoryToDelete = null;
|
||||
self.$showLoading();
|
||||
|
||||
self.$store.dispatch('deleteCategory', {
|
||||
self.transactionCategoriesStore.deleteCategory({
|
||||
category: category,
|
||||
beforeResolve: (done) => {
|
||||
self.$ui.onSwipeoutDeleted(self.getCategoryDomId(category), done);
|
||||
onSwipeoutDeleted(self.getCategoryDomId(category), done);
|
||||
}
|
||||
}).then(() => {
|
||||
self.$hideLoading();
|
||||
|
||||
@@ -55,6 +55,12 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
|
||||
|
||||
import categoryConstants from '@/consts/category.js';
|
||||
import { copyArrayTo } from '@/lib/common.js';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'f7route',
|
||||
@@ -74,6 +80,7 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useTransactionCategoriesStore),
|
||||
allLanguages() {
|
||||
return this.$locale.getAllLanguageInfos();
|
||||
}
|
||||
@@ -85,9 +92,9 @@ export default {
|
||||
self.categoryType = parseInt(query.type);
|
||||
|
||||
if (self.categoryType !== 0 &&
|
||||
self.categoryType !== this.$constants.category.allCategoryTypes.Income &&
|
||||
self.categoryType !== this.$constants.category.allCategoryTypes.Expense &&
|
||||
self.categoryType !== this.$constants.category.allCategoryTypes.Transfer) {
|
||||
self.categoryType !== categoryConstants.allCategoryTypes.Income &&
|
||||
self.categoryType !== categoryConstants.allCategoryTypes.Expense &&
|
||||
self.categoryType !== categoryConstants.allCategoryTypes.Transfer) {
|
||||
self.$toast('Parameter Invalid');
|
||||
self.loadingError = 'Parameter Invalid';
|
||||
return;
|
||||
@@ -97,13 +104,13 @@ export default {
|
||||
for (let i = 1; i <= 3; i++) {
|
||||
self.allCategories.push({
|
||||
type: i,
|
||||
categories: self.$utilities.copyArrayTo(self.getDefaultCategories(i), [])
|
||||
categories: copyArrayTo(self.getDefaultCategories(i), [])
|
||||
});
|
||||
}
|
||||
} else {
|
||||
self.allCategories.push({
|
||||
type: self.categoryType,
|
||||
categories: self.$utilities.copyArrayTo(self.getDefaultCategories(self.categoryType), [])
|
||||
categories: copyArrayTo(self.getDefaultCategories(self.categoryType), [])
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -113,12 +120,12 @@ export default {
|
||||
},
|
||||
getDefaultCategories(categoryType) {
|
||||
switch (categoryType) {
|
||||
case this.$constants.category.allCategoryTypes.Income:
|
||||
return this.$constants.category.defaultIncomeCategories;
|
||||
case this.$constants.category.allCategoryTypes.Expense:
|
||||
return this.$constants.category.defaultExpenseCategories;
|
||||
case this.$constants.category.allCategoryTypes.Transfer:
|
||||
return this.$constants.category.defaultTransferCategories;
|
||||
case categoryConstants.allCategoryTypes.Income:
|
||||
return categoryConstants.defaultIncomeCategories;
|
||||
case categoryConstants.allCategoryTypes.Expense:
|
||||
return categoryConstants.defaultExpenseCategories;
|
||||
case categoryConstants.allCategoryTypes.Transfer:
|
||||
return categoryConstants.defaultTransferCategories;
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
@@ -159,7 +166,7 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
self.$store.dispatch('addCategories', {
|
||||
self.transactionCategoriesStore.addCategories({
|
||||
categories: categories
|
||||
}).then(() => {
|
||||
self.submitting = false;
|
||||
@@ -178,11 +185,11 @@ export default {
|
||||
},
|
||||
getCategoryTypeName(categoryType) {
|
||||
switch (categoryType) {
|
||||
case this.$constants.category.allCategoryTypes.Income:
|
||||
case categoryConstants.allCategoryTypes.Income:
|
||||
return this.$t('Income Categories');
|
||||
case this.$constants.category.allCategoryTypes.Expense:
|
||||
case categoryConstants.allCategoryTypes.Expense:
|
||||
return this.$t('Expense Categories');
|
||||
case this.$constants.category.allCategoryTypes.Transfer:
|
||||
case categoryConstants.allCategoryTypes.Transfer:
|
||||
return this.$t('Transfer Categories');
|
||||
default:
|
||||
return this.$t('Transaction Categories');
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
<f7-accordion-content :style="{ height: collapseStates[accountCategory.category].opened ? 'auto' : '' }">
|
||||
<f7-list strong inset dividers accordion-list class="combination-list-content">
|
||||
<f7-list-item checkbox
|
||||
:class="{ 'has-child-list-item': account.type === $constants.account.allAccountTypes.MultiSubAccounts && accountCategory.visibleSubAccounts[account.id] }"
|
||||
:class="{ 'has-child-list-item': account.type === allAccountTypes.MultiSubAccounts && accountCategory.visibleSubAccounts[account.id] }"
|
||||
:title="account.name"
|
||||
:value="account.id"
|
||||
:checked="isAccountOrSubAccountsAllChecked(account, filterAccountIds)"
|
||||
@@ -80,7 +80,7 @@
|
||||
|
||||
<template #root>
|
||||
<ul class="padding-left"
|
||||
v-if="account.type === $constants.account.allAccountTypes.MultiSubAccounts && accountCategory.visibleSubAccounts[account.id]">
|
||||
v-if="account.type === allAccountTypes.MultiSubAccounts && accountCategory.visibleSubAccounts[account.id]">
|
||||
<f7-list-item checkbox
|
||||
:title="subAccount.name"
|
||||
:value="subAccount.id"
|
||||
@@ -114,6 +114,14 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useAccountsStore } from '@/stores/account.js';
|
||||
import { useStatisticsStore } from '@/stores/statistics.js';
|
||||
|
||||
import accountConstants from '@/consts/account.js';
|
||||
import { copyObjectTo } from '@/lib/common.js';
|
||||
import { getVisibleCategorizedAccounts } from '@/lib/account.js';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'f7route',
|
||||
@@ -132,6 +140,7 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useAccountsStore, useStatisticsStore),
|
||||
title() {
|
||||
if (this.modifyDefault) {
|
||||
return 'Default Account Filter';
|
||||
@@ -146,11 +155,14 @@ export default {
|
||||
return 'Apply';
|
||||
}
|
||||
},
|
||||
allAccountTypes() {
|
||||
return accountConstants.allAccountTypes;
|
||||
},
|
||||
allVisibleCategorizedAccounts() {
|
||||
return this.$utilities.getVisibleCategorizedAccounts(this.$store.state.allCategorizedAccounts);
|
||||
return getVisibleCategorizedAccounts(this.accountsStore.allCategorizedAccounts);
|
||||
},
|
||||
hasAnyAvailableAccount() {
|
||||
return this.$store.getters.allVisibleAccountsCount > 0;
|
||||
return this.accountsStore.allVisibleAccountsCount > 0;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@@ -159,26 +171,26 @@ export default {
|
||||
|
||||
self.modifyDefault = !!query.modifyDefault;
|
||||
|
||||
self.$store.dispatch('loadAllAccounts', {
|
||||
self.accountsStore.loadAllAccounts({
|
||||
force: false
|
||||
}).then(() => {
|
||||
self.loading = false;
|
||||
|
||||
const allAccountIds = {};
|
||||
|
||||
for (let accountId in self.$store.state.allAccountsMap) {
|
||||
if (!Object.prototype.hasOwnProperty.call(self.$store.state.allAccountsMap, accountId)) {
|
||||
for (let accountId in self.accountsStore.allAccountsMap) {
|
||||
if (!Object.prototype.hasOwnProperty.call(self.accountsStore.allAccountsMap, accountId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const account = self.$store.state.allAccountsMap[accountId];
|
||||
const account = self.accountsStore.allAccountsMap[accountId];
|
||||
allAccountIds[account.id] = false;
|
||||
}
|
||||
|
||||
if (self.modifyDefault) {
|
||||
self.filterAccountIds = self.$utilities.copyObjectTo(self.$settings.getStatisticsDefaultAccountFilter(), allAccountIds);
|
||||
self.filterAccountIds = copyObjectTo(self.$settings.getStatisticsDefaultAccountFilter(), allAccountIds);
|
||||
} else {
|
||||
self.filterAccountIds = self.$utilities.copyObjectTo(self.$store.state.transactionStatisticsFilter.filterAccountIds, allAccountIds);
|
||||
self.filterAccountIds = copyObjectTo(self.statisticsStore.transactionStatisticsFilter.filterAccountIds, allAccountIds);
|
||||
}
|
||||
}).catch(error => {
|
||||
if (error.processed) {
|
||||
@@ -212,7 +224,7 @@ export default {
|
||||
if (self.modifyDefault) {
|
||||
self.$settings.setStatisticsDefaultAccountFilter(filteredAccountIds);
|
||||
} else {
|
||||
self.$store.dispatch('updateTransactionStatisticsFilter', {
|
||||
self.statisticsStore.updateTransactionStatisticsFilter({
|
||||
filterAccountIds: filteredAccountIds
|
||||
});
|
||||
}
|
||||
@@ -221,15 +233,15 @@ export default {
|
||||
},
|
||||
selectAccountOrSubAccounts(e) {
|
||||
const accountId = e.target.value;
|
||||
const account = this.$store.state.allAccountsMap[accountId];
|
||||
const account = this.accountsStore.allAccountsMap[accountId];
|
||||
|
||||
if (!account) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (account.type === this.$constants.account.allAccountTypes.SingleAccount) {
|
||||
if (account.type === this.allAccountTypes.SingleAccount) {
|
||||
this.filterAccountIds[account.id] = !e.target.checked;
|
||||
} else if (account.type === this.$constants.account.allAccountTypes.MultiSubAccounts) {
|
||||
} else if (account.type === this.allAccountTypes.MultiSubAccounts) {
|
||||
if (!account.subAccounts || !account.subAccounts.length) {
|
||||
return;
|
||||
}
|
||||
@@ -242,7 +254,7 @@ export default {
|
||||
},
|
||||
selectAccount(e) {
|
||||
const accountId = e.target.value;
|
||||
const account = this.$store.state.allAccountsMap[accountId];
|
||||
const account = this.accountsStore.allAccountsMap[accountId];
|
||||
|
||||
if (!account) {
|
||||
return;
|
||||
@@ -256,9 +268,9 @@ export default {
|
||||
continue;
|
||||
}
|
||||
|
||||
const account = this.$store.state.allAccountsMap[accountId];
|
||||
const account = this.accountsStore.allAccountsMap[accountId];
|
||||
|
||||
if (account && account.type === this.$constants.account.allAccountTypes.SingleAccount) {
|
||||
if (account && account.type === this.allAccountTypes.SingleAccount) {
|
||||
this.filterAccountIds[account.id] = false;
|
||||
}
|
||||
}
|
||||
@@ -269,9 +281,9 @@ export default {
|
||||
continue;
|
||||
}
|
||||
|
||||
const account = this.$store.state.allAccountsMap[accountId];
|
||||
const account = this.accountsStore.allAccountsMap[accountId];
|
||||
|
||||
if (account && account.type === this.$constants.account.allAccountTypes.SingleAccount) {
|
||||
if (account && account.type === this.allAccountTypes.SingleAccount) {
|
||||
this.filterAccountIds[account.id] = true;
|
||||
}
|
||||
}
|
||||
@@ -282,9 +294,9 @@ export default {
|
||||
continue;
|
||||
}
|
||||
|
||||
const account = this.$store.state.allAccountsMap[accountId];
|
||||
const account = this.accountsStore.allAccountsMap[accountId];
|
||||
|
||||
if (account && account.type === this.$constants.account.allAccountTypes.SingleAccount) {
|
||||
if (account && account.type === this.allAccountTypes.SingleAccount) {
|
||||
this.filterAccountIds[account.id] = !this.filterAccountIds[account.id];
|
||||
}
|
||||
}
|
||||
@@ -325,12 +337,12 @@ export default {
|
||||
getCollapseStates() {
|
||||
const collapseStates = {};
|
||||
|
||||
for (let categoryType in this.$constants.account.allCategories) {
|
||||
if (!Object.prototype.hasOwnProperty.call(this.$constants.account.allCategories, categoryType)) {
|
||||
for (let categoryType in accountConstants.allCategories) {
|
||||
if (!Object.prototype.hasOwnProperty.call(accountConstants.allCategories, categoryType)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const accountCategory = this.$constants.account.allCategories[categoryType];
|
||||
const accountCategory = accountConstants.allCategories[categoryType];
|
||||
|
||||
collapseStates[accountCategory.id] = {
|
||||
opened: true
|
||||
|
||||
@@ -123,6 +123,14 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
|
||||
import { useStatisticsStore } from '@/stores/statistics.js';
|
||||
|
||||
import categoryConstants from '@/consts/category.js';
|
||||
import { copyObjectTo } from '@/lib/common.js';
|
||||
import { allVisibleTransactionCategories } from '@/lib/category.js';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'f7route',
|
||||
@@ -141,6 +149,8 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useTransactionCategoriesStore, useStatisticsStore),
|
||||
|
||||
title() {
|
||||
if (this.modifyDefault) {
|
||||
return 'Default Transaction Category Filter';
|
||||
@@ -156,7 +166,7 @@ export default {
|
||||
}
|
||||
},
|
||||
allVisibleTransactionCategories() {
|
||||
return this.$utilities.allVisibleTransactionCategories(this.$store.state.allTransactionCategories);
|
||||
return allVisibleTransactionCategories(this.transactionCategoriesStore.allTransactionCategories);
|
||||
},
|
||||
hasAnyAvailableCategory() {
|
||||
for (let type in this.allVisibleTransactionCategories) {
|
||||
@@ -194,26 +204,26 @@ export default {
|
||||
|
||||
self.modifyDefault = !!query.modifyDefault;
|
||||
|
||||
self.$store.dispatch('loadAllCategories', {
|
||||
self.transactionCategoriesStore.loadAllCategories({
|
||||
force: false
|
||||
}).then(() => {
|
||||
self.loading = false;
|
||||
|
||||
const allCategoryIds = {};
|
||||
|
||||
for (let categoryId in self.$store.state.allTransactionCategoriesMap) {
|
||||
if (!Object.prototype.hasOwnProperty.call(self.$store.state.allTransactionCategoriesMap, categoryId)) {
|
||||
for (let categoryId in self.transactionCategoriesStore.allTransactionCategoriesMap) {
|
||||
if (!Object.prototype.hasOwnProperty.call(self.transactionCategoriesStore.allTransactionCategoriesMap, categoryId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const category = self.$store.state.allTransactionCategoriesMap[categoryId];
|
||||
const category = self.transactionCategoriesStore.allTransactionCategoriesMap[categoryId];
|
||||
allCategoryIds[category.id] = false;
|
||||
}
|
||||
|
||||
if (self.modifyDefault) {
|
||||
self.filterCategoryIds = self.$utilities.copyObjectTo(self.$settings.getStatisticsDefaultTransactionCategoryFilter(), allCategoryIds);
|
||||
self.filterCategoryIds = copyObjectTo(self.$settings.getStatisticsDefaultTransactionCategoryFilter(), allCategoryIds);
|
||||
} else {
|
||||
self.filterCategoryIds = self.$utilities.copyObjectTo(self.$store.state.transactionStatisticsFilter.filterCategoryIds, allCategoryIds);
|
||||
self.filterCategoryIds = copyObjectTo(self.statisticsStore.transactionStatisticsFilter.filterCategoryIds, allCategoryIds);
|
||||
}
|
||||
}).catch(error => {
|
||||
if (error.processed) {
|
||||
@@ -247,7 +257,7 @@ export default {
|
||||
if (self.modifyDefault) {
|
||||
self.$settings.setStatisticsDefaultTransactionCategoryFilter(filteredCategoryIds);
|
||||
} else {
|
||||
self.$store.dispatch('updateTransactionStatisticsFilter', {
|
||||
self.statisticsStore.updateTransactionStatisticsFilter({
|
||||
filterCategoryIds: filteredCategoryIds
|
||||
});
|
||||
}
|
||||
@@ -256,7 +266,7 @@ export default {
|
||||
},
|
||||
selectCategory(e) {
|
||||
const categoryId = e.target.value;
|
||||
const category = this.$store.state.allTransactionCategoriesMap[categoryId];
|
||||
const category = this.transactionCategoriesStore.allTransactionCategoriesMap[categoryId];
|
||||
|
||||
if (!category) {
|
||||
return;
|
||||
@@ -266,7 +276,7 @@ export default {
|
||||
},
|
||||
selectSubCategories(e) {
|
||||
const categoryId = e.target.value;
|
||||
const category = this.$store.state.allTransactionCategoriesMap[categoryId];
|
||||
const category = this.transactionCategoriesStore.allTransactionCategoriesMap[categoryId];
|
||||
|
||||
if (!category || !category.subCategories || !category.subCategories.length) {
|
||||
return;
|
||||
@@ -283,7 +293,7 @@ export default {
|
||||
continue;
|
||||
}
|
||||
|
||||
const category = this.$store.state.allTransactionCategoriesMap[categoryId];
|
||||
const category = this.transactionCategoriesStore.allTransactionCategoriesMap[categoryId];
|
||||
|
||||
if (category) {
|
||||
this.filterCategoryIds[category.id] = false;
|
||||
@@ -296,7 +306,7 @@ export default {
|
||||
continue;
|
||||
}
|
||||
|
||||
const category = this.$store.state.allTransactionCategoriesMap[categoryId];
|
||||
const category = this.transactionCategoriesStore.allTransactionCategoriesMap[categoryId];
|
||||
|
||||
if (category) {
|
||||
this.filterCategoryIds[category.id] = true;
|
||||
@@ -309,7 +319,7 @@ export default {
|
||||
continue;
|
||||
}
|
||||
|
||||
const category = this.$store.state.allTransactionCategoriesMap[categoryId];
|
||||
const category = this.transactionCategoriesStore.allTransactionCategoriesMap[categoryId];
|
||||
|
||||
if (category) {
|
||||
this.filterCategoryIds[category.id] = !this.filterCategoryIds[category.id];
|
||||
@@ -318,11 +328,11 @@ export default {
|
||||
},
|
||||
getCategoryTypeName(categoryType) {
|
||||
switch (categoryType) {
|
||||
case this.$constants.category.allCategoryTypes.Income.toString():
|
||||
case categoryConstants.allCategoryTypes.Income.toString():
|
||||
return this.$t('Income Categories');
|
||||
case this.$constants.category.allCategoryTypes.Expense.toString():
|
||||
case categoryConstants.allCategoryTypes.Expense.toString():
|
||||
return this.$t('Expense Categories');
|
||||
case this.$constants.category.allCategoryTypes.Transfer.toString():
|
||||
case categoryConstants.allCategoryTypes.Transfer.toString():
|
||||
return this.$t('Transfer Categories');
|
||||
default:
|
||||
return this.$t('Transaction Categories');
|
||||
@@ -356,12 +366,12 @@ export default {
|
||||
getCollapseStates() {
|
||||
const collapseStates = {};
|
||||
|
||||
for (let categoryTypeField in this.$constants.category.allCategoryTypes) {
|
||||
if (!Object.prototype.hasOwnProperty.call(this.$constants.category.allCategoryTypes, categoryTypeField)) {
|
||||
for (let categoryTypeField in categoryConstants.allCategoryTypes) {
|
||||
if (!Object.prototype.hasOwnProperty.call(categoryConstants.allCategoryTypes, categoryTypeField)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const categoryType = this.$constants.category.allCategoryTypes[categoryTypeField];
|
||||
const categoryType = categoryConstants.allCategoryTypes[categoryTypeField];
|
||||
|
||||
collapseStates[categoryType] = {
|
||||
opened: true
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
:title="$t('Default Chart Type')"
|
||||
smart-select :smart-select-params="{ openIn: 'popup', popupPush: true, closeOnSelect: true, scrollToSelectedItem: true, searchbar: true, searchbarPlaceholder: $t('Chart Type'), searchbarDisableText: $t('Cancel'), appendSearchbarNotFound: $t('No results'), popupCloseLinkText: $t('Done') }">
|
||||
<select v-model="defaultChartType">
|
||||
<option :value="$constants.statistics.allChartTypes.Pie">{{ $t('Pie Chart') }}</option>
|
||||
<option :value="$constants.statistics.allChartTypes.Bar">{{ $t('Bar Chart') }}</option>
|
||||
<option :value="allChartTypes.Pie">{{ $t('Pie Chart') }}</option>
|
||||
<option :value="allChartTypes.Bar">{{ $t('Bar Chart') }}</option>
|
||||
</select>
|
||||
</f7-list-item>
|
||||
|
||||
@@ -50,25 +50,31 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import datetimeConstants from '@/consts/datetime.js';
|
||||
import statisticsConstants from '@/consts/statistics.js';
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
allChartTypes() {
|
||||
return statisticsConstants.allChartTypes;
|
||||
},
|
||||
allChartDataTypes() {
|
||||
return this.$constants.statistics.allChartDataTypes;
|
||||
return statisticsConstants.allChartDataTypes;
|
||||
},
|
||||
allSortingTypes() {
|
||||
return this.$constants.statistics.allSortingTypes;
|
||||
return statisticsConstants.allSortingTypes;
|
||||
},
|
||||
allDateRanges() {
|
||||
const allDateRanges = [];
|
||||
|
||||
for (let dateRangeField in this.$constants.datetime.allDateRanges) {
|
||||
if (!Object.prototype.hasOwnProperty.call(this.$constants.datetime.allDateRanges, dateRangeField)) {
|
||||
for (let dateRangeField in datetimeConstants.allDateRanges) {
|
||||
if (!Object.prototype.hasOwnProperty.call(datetimeConstants.allDateRanges, dateRangeField)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const dateRangeType = this.$constants.datetime.allDateRanges[dateRangeField];
|
||||
const dateRangeType = datetimeConstants.allDateRanges[dateRangeField];
|
||||
|
||||
if (dateRangeType.type !== this.$constants.datetime.allDateRanges.Custom.type) {
|
||||
if (dateRangeType.type !== datetimeConstants.allDateRanges.Custom.type) {
|
||||
allDateRanges.push(dateRangeType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
</f7-list>
|
||||
</f7-popover>
|
||||
|
||||
<f7-card v-if="query.chartType === $constants.statistics.allChartTypes.Pie">
|
||||
<f7-card v-if="query.chartType === allChartTypes.Pie">
|
||||
<f7-card-header class="no-border display-block">
|
||||
<div class="statistics-chart-header full-line text-align-right">
|
||||
<span style="margin-right: 4px;">{{ $t('Sort By') }}</span>
|
||||
@@ -76,7 +76,7 @@
|
||||
</f7-card-content>
|
||||
</f7-card>
|
||||
|
||||
<f7-card v-else-if="query.chartType === $constants.statistics.allChartTypes.Bar">
|
||||
<f7-card v-else-if="query.chartType === allChartTypes.Bar">
|
||||
<f7-card-header class="no-border display-block">
|
||||
<div class="statistics-chart-header display-flex full-line justify-content-space-between">
|
||||
<div>
|
||||
@@ -150,7 +150,7 @@
|
||||
<template #title>
|
||||
<div class="statistics-list-item-text">
|
||||
<span>{{ item.name }}</span>
|
||||
<small class="statistics-percent" v-if="item.percent >= 0">{{ $utilities.formatPercent(item.percent, 2, '<0.01') }}</small>
|
||||
<small class="statistics-percent" v-if="item.percent >= 0">{{ getDisplayPercent(item.percent, 2, '<0.01') }}</small>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -195,11 +195,11 @@
|
||||
<f7-link :class="{ 'disabled': query.dateType === allDateRanges.All.type || query.chartDataType === allChartDataTypes.AccountTotalAssets.type || query.chartDataType === allChartDataTypes.AccountTotalLiabilities.type }" @click="shiftDateRange(query.startTime, query.endTime, 1)">
|
||||
<f7-icon f7="arrow_right_square"></f7-icon>
|
||||
</f7-link>
|
||||
<f7-link class="tabbar-text-with-ellipsis" @click="setChartType($constants.statistics.allChartTypes.Pie)">
|
||||
<span :class="{ 'tabbar-item-changed': query.chartType === $constants.statistics.allChartTypes.Pie }">{{ $t('Pie Chart') }}</span>
|
||||
<f7-link class="tabbar-text-with-ellipsis" @click="setChartType(allChartTypes.Pie)">
|
||||
<span :class="{ 'tabbar-item-changed': query.chartType === allChartTypes.Pie }">{{ $t('Pie Chart') }}</span>
|
||||
</f7-link>
|
||||
<f7-link class="tabbar-text-with-ellipsis" @click="setChartType($constants.statistics.allChartTypes.Bar)">
|
||||
<span :class="{ 'tabbar-item-changed': query.chartType === $constants.statistics.allChartTypes.Bar }">{{ $t('Bar Chart') }}</span>
|
||||
<f7-link class="tabbar-text-with-ellipsis" @click="setChartType(allChartTypes.Bar)">
|
||||
<span :class="{ 'tabbar-item-changed': query.chartType === allChartTypes.Bar }">{{ $t('Bar Chart') }}</span>
|
||||
</f7-link>
|
||||
</f7-toolbar>
|
||||
|
||||
@@ -217,10 +217,10 @@
|
||||
</template>
|
||||
<template #footer>
|
||||
<div v-if="dateRange.type === allDateRanges.Custom.type && query.dateType === allDateRanges.Custom.type && query.startTime && query.endTime">
|
||||
<span>{{ $utilities.formatUnixTime(query.startTime, $locale.getLongDateTimeFormat()) }}</span>
|
||||
<span>{{ queryStartTime }}</span>
|
||||
<span> - </span>
|
||||
<br/>
|
||||
<span>{{ $utilities.formatUnixTime(query.endTime, $locale.getLongDateTimeFormat()) }}</span>
|
||||
<span>{{ queryEndTime }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</f7-list-item>
|
||||
@@ -250,6 +250,24 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useUserStore } from '@/stores/user.js';
|
||||
import { useAccountsStore } from '@/stores/account.js';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
|
||||
import { useStatisticsStore } from '@/stores/statistics.js';
|
||||
|
||||
import datetimeConstants from '@/consts/datetime.js';
|
||||
import statisticsConstants from '@/consts/statistics.js';
|
||||
import { getNameByKeyValue, limitText, formatPercent } from '@/lib/common.js'
|
||||
import {
|
||||
parseDateFromUnixTime,
|
||||
getYear,
|
||||
getShiftedDateRange,
|
||||
getDateRangeByDateType,
|
||||
isDateRangeMatchFullYears,
|
||||
isDateRangeMatchFullMonths
|
||||
} from '@/lib/datetime.js';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'f7router'
|
||||
@@ -269,31 +287,41 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useUserStore, useAccountsStore, useTransactionCategoriesStore, useStatisticsStore),
|
||||
defaultCurrency() {
|
||||
return this.$store.getters.currentUserDefaultCurrency;
|
||||
return this.userStore.currentUserDefaultCurrency;
|
||||
},
|
||||
firstDayOfWeek() {
|
||||
return this.$store.getters.currentUserFirstDayOfWeek;
|
||||
return this.userStore.currentUserFirstDayOfWeek;
|
||||
},
|
||||
query() {
|
||||
return this.$store.state.transactionStatisticsFilter;
|
||||
return this.statisticsStore.transactionStatisticsFilter;
|
||||
},
|
||||
queryChartDataTypeName() {
|
||||
const queryChartDataTypeName = this.$utilities.getNameByKeyValue(this.allChartDataTypes, this.query.chartDataType, 'type', 'name', 'Statistics');
|
||||
const queryChartDataTypeName = getNameByKeyValue(this.allChartDataTypes, this.query.chartDataType, 'type', 'name', 'Statistics');
|
||||
return this.$t(queryChartDataTypeName);
|
||||
},
|
||||
querySortingTypeName() {
|
||||
const querySortingTypeName = this.$utilities.getNameByKeyValue(this.allSortingTypes, this.query.sortingType, 'type', 'name', 'System Default');
|
||||
const querySortingTypeName = getNameByKeyValue(this.allSortingTypes, this.query.sortingType, 'type', 'name', 'System Default');
|
||||
return this.$t(querySortingTypeName);
|
||||
},
|
||||
queryStartTime() {
|
||||
return this.$locale.formatUnixTimeToLongDateTime(this.userStore, this.query.startTime);
|
||||
},
|
||||
queryEndTime() {
|
||||
return this.$locale.formatUnixTimeToLongDateTime(this.userStore, this.query.endTime);
|
||||
},
|
||||
allChartTypes() {
|
||||
return statisticsConstants.allChartTypes;
|
||||
},
|
||||
allChartDataTypes() {
|
||||
return this.$constants.statistics.allChartDataTypes;
|
||||
return statisticsConstants.allChartDataTypes;
|
||||
},
|
||||
allSortingTypes() {
|
||||
return this.$constants.statistics.allSortingTypes;
|
||||
return statisticsConstants.allSortingTypes;
|
||||
},
|
||||
allDateRanges() {
|
||||
return this.$constants.datetime.allDateRanges;
|
||||
return datetimeConstants.allDateRanges;
|
||||
},
|
||||
totalAmountName() {
|
||||
if (this.query.chartDataType === this.allChartDataTypes.IncomeByAccount.type
|
||||
@@ -325,10 +353,10 @@ export default {
|
||||
self.query.chartDataType === self.allChartDataTypes.IncomeByAccount.type ||
|
||||
self.query.chartDataType === self.allChartDataTypes.IncomeByPrimaryCategory.type ||
|
||||
self.query.chartDataType === self.allChartDataTypes.IncomeBySecondaryCategory.type) {
|
||||
combinedData = this.$store.getters.statisticsItemsByTransactionStatisticsData;
|
||||
combinedData = this.statisticsStore.statisticsItemsByTransactionStatisticsData;
|
||||
} else if (self.query.chartDataType === self.allChartDataTypes.AccountTotalAssets.type ||
|
||||
self.query.chartDataType === self.allChartDataTypes.AccountTotalLiabilities.type) {
|
||||
combinedData = this.$store.getters.statisticsItemsByAccountsData;
|
||||
combinedData = this.statisticsStore.statisticsItemsByAccountsData;
|
||||
}
|
||||
|
||||
const allStatisticsItems = [];
|
||||
@@ -405,31 +433,31 @@ export default {
|
||||
|
||||
let defaultChartType = self.$settings.getStatisticsDefaultChartType();
|
||||
|
||||
if (defaultChartType !== self.$constants.statistics.allChartTypes.Pie && defaultChartType !== self.$constants.statistics.allChartTypes.Bar) {
|
||||
defaultChartType = self.$constants.statistics.defaultChartType;
|
||||
if (defaultChartType !== self.allChartTypes.Pie && defaultChartType !== self.allChartTypes.Bar) {
|
||||
defaultChartType = statisticsConstants.defaultChartType;
|
||||
}
|
||||
|
||||
let defaultChartDataType = self.$settings.getStatisticsDefaultChartDataType();
|
||||
|
||||
if (defaultChartDataType < self.allChartDataTypes.ExpenseByAccount.type || defaultChartDataType > self.allChartDataTypes.AccountTotalLiabilities.type) {
|
||||
defaultChartDataType = self.$constants.statistics.defaultChartDataType;
|
||||
defaultChartDataType = statisticsConstants.defaultChartDataType;
|
||||
}
|
||||
|
||||
let defaultDateRange = self.$settings.getStatisticsDefaultDateRange();
|
||||
|
||||
if (defaultDateRange < self.allDateRanges.All.type || defaultDateRange >= self.allDateRanges.Custom.type) {
|
||||
defaultDateRange = self.$constants.statistics.defaultDataRangeType;
|
||||
defaultDateRange = statisticsConstants.defaultDataRangeType;
|
||||
}
|
||||
|
||||
let defaultSortType = self.$settings.getStatisticsSortingType();
|
||||
|
||||
if (defaultSortType < self.allSortingTypes.Amount.type || defaultSortType > self.allSortingTypes.Name.type) {
|
||||
defaultSortType = self.$constants.statistics.defaultSortingType;
|
||||
defaultSortType = statisticsConstants.defaultSortingType;
|
||||
}
|
||||
|
||||
const dateRange = self.$utilities.getDateRangeByDateType(defaultDateRange, self.firstDayOfWeek);
|
||||
const dateRange = getDateRangeByDateType(defaultDateRange, self.firstDayOfWeek);
|
||||
|
||||
self.$store.dispatch('initTransactionStatisticsFilter', {
|
||||
self.statisticsStore.initTransactionStatisticsFilter({
|
||||
dateType: dateRange ? dateRange.dateType : undefined,
|
||||
startTime: dateRange ? dateRange.minTime : undefined,
|
||||
endTime: dateRange ? dateRange.maxTime : undefined,
|
||||
@@ -441,10 +469,10 @@ export default {
|
||||
});
|
||||
|
||||
Promise.all([
|
||||
self.$store.dispatch('loadAllAccounts', { force: false }),
|
||||
self.$store.dispatch('loadAllCategories', { force: false })
|
||||
self.accountsStore.loadAllAccounts({ force: false }),
|
||||
self.transactionCategoriesStore.loadAllCategories({ force: false })
|
||||
]).then(() => {
|
||||
return self.$store.dispatch('loadTransactionStatistics', {
|
||||
return self.statisticsStore.loadTransactionStatistics({
|
||||
defaultCurrency: self.defaultCurrency
|
||||
});
|
||||
}).then(() => {
|
||||
@@ -460,7 +488,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
onPageAfterIn() {
|
||||
if (this.$store.state.transactionStatisticsStateInvalid && !this.loading) {
|
||||
if (this.statisticsStore.transactionStatisticsStateInvalid && !this.loading) {
|
||||
this.reload(null);
|
||||
}
|
||||
|
||||
@@ -476,12 +504,12 @@ export default {
|
||||
self.query.chartDataType === self.allChartDataTypes.IncomeByAccount.type ||
|
||||
self.query.chartDataType === self.allChartDataTypes.IncomeByPrimaryCategory.type ||
|
||||
self.query.chartDataType === self.allChartDataTypes.IncomeBySecondaryCategory.type) {
|
||||
dispatchPromise = self.$store.dispatch('loadTransactionStatistics', {
|
||||
dispatchPromise = self.statisticsStore.loadTransactionStatistics({
|
||||
defaultCurrency: self.defaultCurrency
|
||||
});
|
||||
} else if (self.query.chartDataType === self.allChartDataTypes.AccountTotalAssets.type ||
|
||||
self.query.chartDataType === self.allChartDataTypes.AccountTotalLiabilities.type) {
|
||||
dispatchPromise = self.$store.dispatch('loadAllAccounts', {
|
||||
dispatchPromise = self.accountsStore.loadAllAccounts({
|
||||
force: true
|
||||
});
|
||||
}
|
||||
@@ -503,12 +531,12 @@ export default {
|
||||
}
|
||||
},
|
||||
setChartType(chartType) {
|
||||
this.$store.dispatch('updateTransactionStatisticsFilter', {
|
||||
this.statisticsStore.updateTransactionStatisticsFilter({
|
||||
chartType: chartType
|
||||
});
|
||||
},
|
||||
setChartDataType(chartDataType) {
|
||||
this.$store.dispatch('updateTransactionStatisticsFilter', {
|
||||
this.statisticsStore.updateTransactionStatisticsFilter({
|
||||
chartDataType: chartDataType
|
||||
});
|
||||
this.showChartDataTypePopover = false;
|
||||
@@ -519,7 +547,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$store.dispatch('updateTransactionStatisticsFilter', {
|
||||
this.statisticsStore.updateTransactionStatisticsFilter({
|
||||
sortingType: sortingType
|
||||
});
|
||||
|
||||
@@ -535,13 +563,13 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
const dateRange = this.$utilities.getDateRangeByDateType(dateType, this.firstDayOfWeek);
|
||||
const dateRange = getDateRangeByDateType(dateType, this.firstDayOfWeek);
|
||||
|
||||
if (!dateRange) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$store.dispatch('updateTransactionStatisticsFilter', {
|
||||
this.statisticsStore.updateTransactionStatisticsFilter({
|
||||
dateType: dateRange.dateType,
|
||||
startTime: dateRange.minTime,
|
||||
endTime: dateRange.maxTime
|
||||
@@ -555,7 +583,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$store.dispatch('updateTransactionStatisticsFilter', {
|
||||
this.statisticsStore.updateTransactionStatisticsFilter({
|
||||
dateType: this.allDateRanges.Custom.type,
|
||||
startTime: startTime,
|
||||
endTime: endTime
|
||||
@@ -570,7 +598,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
const newDateRange = this.$utilities.getShiftedDateRange(startTime, endTime, scale);
|
||||
const newDateRange = getShiftedDateRange(startTime, endTime, scale);
|
||||
let newDateType = this.allDateRanges.Custom.type;
|
||||
|
||||
for (let dateRangeField in this.allDateRanges) {
|
||||
@@ -579,7 +607,7 @@ export default {
|
||||
}
|
||||
|
||||
const dateRangeType = this.allDateRanges[dateRangeField];
|
||||
const dateRange = this.$utilities.getDateRangeByDateType(dateRangeType.type, this.firstDayOfWeek);
|
||||
const dateRange = getDateRangeByDateType(dateRangeType.type, this.firstDayOfWeek);
|
||||
|
||||
if (dateRange && dateRange.minTime === newDateRange.minTime && dateRange.maxTime === newDateRange.maxTime) {
|
||||
newDateType = dateRangeType.type;
|
||||
@@ -587,7 +615,7 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
this.$store.dispatch('updateTransactionStatisticsFilter', {
|
||||
this.statisticsStore.updateTransactionStatisticsFilter({
|
||||
dateType: newDateType,
|
||||
startTime: newDateRange.minTime,
|
||||
endTime: newDateRange.maxTime
|
||||
@@ -617,30 +645,30 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.$utilities.isDateRangeMatchFullYears(query.startTime, query.endTime)) {
|
||||
const displayStartTime = this.$utilities.formatUnixTime(query.startTime, this.$locale.getShortYearFormat());
|
||||
const displayEndTime = this.$utilities.formatUnixTime(query.endTime, this.$locale.getShortYearFormat());
|
||||
if (isDateRangeMatchFullYears(query.startTime, query.endTime)) {
|
||||
const displayStartTime = this.$locale.formatUnixTimeToShortYear(this.userStore, query.startTime);
|
||||
const displayEndTime = this.$locale.formatUnixTimeToShortYear(this.userStore, query.endTime);
|
||||
|
||||
return displayStartTime !== displayEndTime ? `${displayStartTime} ~ ${displayEndTime}` : displayStartTime;
|
||||
}
|
||||
|
||||
if (this.$utilities.isDateRangeMatchFullMonths(query.startTime, query.endTime)) {
|
||||
const displayStartTime = this.$utilities.formatUnixTime(query.startTime, this.$locale.getShortYearMonthFormat());
|
||||
const displayEndTime = this.$utilities.formatUnixTime(query.endTime, this.$locale.getShortYearMonthFormat());
|
||||
if (isDateRangeMatchFullMonths(query.startTime, query.endTime)) {
|
||||
const displayStartTime = this.$locale.formatUnixTimeToShortYearMonth(this.userStore, query.startTime);
|
||||
const displayEndTime = this.$locale.formatUnixTimeToShortYearMonth(this.userStore, query.endTime);
|
||||
|
||||
return displayStartTime !== displayEndTime ? `${displayStartTime} ~ ${displayEndTime}` : displayStartTime;
|
||||
}
|
||||
|
||||
const startTimeYear = this.$utilities.getYear(this.$utilities.parseDateFromUnixTime(query.startTime));
|
||||
const endTimeYear = this.$utilities.getYear(this.$utilities.parseDateFromUnixTime(query.endTime));
|
||||
const startTimeYear = getYear(parseDateFromUnixTime(query.startTime));
|
||||
const endTimeYear = getYear(parseDateFromUnixTime(query.endTime));
|
||||
|
||||
const displayStartTime = this.$utilities.formatUnixTime(query.startTime, this.$locale.getShortDateFormat());
|
||||
const displayEndTime = this.$utilities.formatUnixTime(query.endTime, this.$locale.getShortDateFormat());
|
||||
const displayStartTime = this.$locale.formatUnixTimeToShortDate(this.userStore, query.startTime);
|
||||
const displayEndTime = this.$locale.formatUnixTimeToShortDate(this.userStore, query.endTime);
|
||||
|
||||
if (displayStartTime === displayEndTime) {
|
||||
return displayStartTime;
|
||||
} else if (startTimeYear === endTimeYear) {
|
||||
const displayShortEndTime = this.$utilities.formatUnixTime(query.endTime, this.$locale.getShortMonthDayFormat());
|
||||
const displayShortEndTime = this.$locale.formatUnixTimeToShortMonthDay(this.userStore, query.endTime);
|
||||
return `${displayStartTime} ~ ${displayShortEndTime}`;
|
||||
}
|
||||
|
||||
@@ -690,11 +718,14 @@ export default {
|
||||
}
|
||||
|
||||
if (textLimit) {
|
||||
this.$utilities.limitText(amount, textLimit);
|
||||
return limitText(amount, textLimit);
|
||||
}
|
||||
|
||||
return amount;
|
||||
},
|
||||
getDisplayPercent(value, precision, lowPrecisionValue) {
|
||||
return formatPercent(value, precision, lowPrecisionValue);
|
||||
},
|
||||
getItemLinkUrl(item) {
|
||||
const querys = [];
|
||||
|
||||
|
||||
@@ -144,6 +144,11 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useTransactionTagsStore } from '@/stores/transactionTag.js';
|
||||
|
||||
import { onSwipeoutDeleted } from '@/lib/ui.mobile.js';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'f7router'
|
||||
@@ -168,8 +173,10 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useTransactionTagsStore),
|
||||
|
||||
tags() {
|
||||
return this.$store.state.allTransactionTags;
|
||||
return this.transactionTagsStore.allTransactionTags;
|
||||
},
|
||||
firstShowingId() {
|
||||
for (let i = 0; i < this.tags.length; i++) {
|
||||
@@ -207,7 +214,7 @@ export default {
|
||||
|
||||
self.loading = true;
|
||||
|
||||
self.$store.dispatch('loadAllTags', {
|
||||
self.transactionTagsStore.loadAllTags({
|
||||
force: false
|
||||
}).then(() => {
|
||||
self.loading = false;
|
||||
@@ -232,7 +239,7 @@ export default {
|
||||
|
||||
const self = this;
|
||||
|
||||
self.$store.dispatch('loadAllTags', {
|
||||
self.transactionTagsStore.loadAllTags({
|
||||
force: true
|
||||
}).then(() => {
|
||||
if (done) {
|
||||
@@ -272,7 +279,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
self.$store.dispatch('changeTagDisplayOrder', {
|
||||
self.transactionTagsStore.changeTagDisplayOrder({
|
||||
tagId: id,
|
||||
from: event.from,
|
||||
to: event.to
|
||||
@@ -294,7 +301,7 @@ export default {
|
||||
self.displayOrderSaving = true;
|
||||
self.$showLoading();
|
||||
|
||||
self.$store.dispatch('updateTagDisplayOrders').then(() => {
|
||||
self.transactionTagsStore.updateTagDisplayOrders().then(() => {
|
||||
self.displayOrderSaving = false;
|
||||
self.$hideLoading();
|
||||
|
||||
@@ -324,7 +331,7 @@ export default {
|
||||
|
||||
self.$showLoading();
|
||||
|
||||
self.$store.dispatch('saveTag', {
|
||||
self.transactionTagsStore.saveTag({
|
||||
tag: tag
|
||||
}).then(() => {
|
||||
self.$hideLoading();
|
||||
@@ -363,7 +370,7 @@ export default {
|
||||
|
||||
self.$showLoading();
|
||||
|
||||
self.$store.dispatch('hideTag', {
|
||||
self.transactionTagsStore.hideTag({
|
||||
tag: tag,
|
||||
hidden: hidden
|
||||
}).then(() => {
|
||||
@@ -394,10 +401,10 @@ export default {
|
||||
self.tagToDelete = null;
|
||||
self.$showLoading();
|
||||
|
||||
self.$store.dispatch('deleteTag', {
|
||||
self.transactionTagsStore.deleteTag({
|
||||
tag: tag,
|
||||
beforeResolve: (done) => {
|
||||
self.$ui.onSwipeoutDeleted(self.getTagDomId(tag), done);
|
||||
onSwipeoutDeleted(self.getTagDomId(tag), done);
|
||||
}
|
||||
}).then(() => {
|
||||
self.$hideLoading();
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
|
||||
<f7-subnavbar>
|
||||
<f7-segmented strong :class="{ 'readonly': mode !== 'add' }">
|
||||
<f7-button :text="$t('Expense')" :active="transaction.type === $constants.transaction.allTransactionTypes.Expense" @click="transaction.type = $constants.transaction.allTransactionTypes.Expense"></f7-button>
|
||||
<f7-button :text="$t('Income')" :active="transaction.type === $constants.transaction.allTransactionTypes.Income" @click="transaction.type = $constants.transaction.allTransactionTypes.Income"></f7-button>
|
||||
<f7-button :text="$t('Transfer')" :active="transaction.type === $constants.transaction.allTransactionTypes.Transfer" @click="transaction.type = $constants.transaction.allTransactionTypes.Transfer"></f7-button>
|
||||
<f7-button :text="$t('Expense')" :active="transaction.type === allTransactionTypes.Expense" @click="transaction.type = allTransactionTypes.Expense"></f7-button>
|
||||
<f7-button :text="$t('Income')" :active="transaction.type === allTransactionTypes.Income" @click="transaction.type = allTransactionTypes.Income"></f7-button>
|
||||
<f7-button :text="$t('Transfer')" :active="transaction.type === allTransactionTypes.Transfer" @click="transaction.type = allTransactionTypes.Transfer"></f7-button>
|
||||
</f7-segmented>
|
||||
</f7-subnavbar>
|
||||
</f7-navbar>
|
||||
@@ -43,14 +43,14 @@
|
||||
<f7-list-item
|
||||
class="transaction-edit-amount"
|
||||
link="#" no-chevron
|
||||
:class="{ 'readonly': mode === 'view', 'color-teal': transaction.type === $constants.transaction.allTransactionTypes.Expense, 'color-red': transaction.type === $constants.transaction.allTransactionTypes.Income }"
|
||||
:class="{ 'readonly': mode === 'view', 'color-teal': transaction.type === allTransactionTypes.Expense, 'color-red': transaction.type === allTransactionTypes.Income }"
|
||||
:style="{ fontSize: sourceAmountFontSize + 'px' }"
|
||||
:header="$t(sourceAmountName)"
|
||||
:title="getDisplayAmount(transaction.sourceAmount, transaction.hideAmount)"
|
||||
@click="showSourceAmountSheet = true"
|
||||
>
|
||||
<number-pad-sheet :min-value="$constants.transaction.minAmount"
|
||||
:max-value="$constants.transaction.maxAmount"
|
||||
<number-pad-sheet :min-value="allowedMinAmount"
|
||||
:max-value="allowedMaxAmount"
|
||||
v-model:show="showSourceAmountSheet"
|
||||
v-model="transaction.sourceAmount"
|
||||
></number-pad-sheet>
|
||||
@@ -64,10 +64,10 @@
|
||||
:header="$t('Transfer In Amount')"
|
||||
:title="getDisplayAmount(transaction.destinationAmount, transaction.hideAmount)"
|
||||
@click="showDestinationAmountSheet = true"
|
||||
v-if="transaction.type === $constants.transaction.allTransactionTypes.Transfer"
|
||||
v-if="transaction.type === allTransactionTypes.Transfer"
|
||||
>
|
||||
<number-pad-sheet :min-value="$constants.transaction.minAmount"
|
||||
:max-value="$constants.transaction.maxAmount"
|
||||
<number-pad-sheet :min-value="allowedMinAmount"
|
||||
:max-value="allowedMaxAmount"
|
||||
v-model:show="showDestinationAmountSheet"
|
||||
v-model="transaction.destinationAmount"
|
||||
></number-pad-sheet>
|
||||
@@ -80,13 +80,13 @@
|
||||
:class="{ 'disabled': !hasAvailableExpenseCategories, 'readonly': mode === 'view' }"
|
||||
:header="$t('Category')"
|
||||
@click="showCategorySheet = true"
|
||||
v-if="transaction.type === $constants.transaction.allTransactionTypes.Expense"
|
||||
v-if="transaction.type === allTransactionTypes.Expense"
|
||||
>
|
||||
<template #title>
|
||||
<div class="list-item-custom-title" v-if="hasAvailableExpenseCategories">
|
||||
<span>{{ getPrimaryCategoryName(transaction.expenseCategory, allCategories[$constants.category.allCategoryTypes.Expense]) }}</span>
|
||||
<span>{{ getPrimaryCategoryName(transaction.expenseCategory, allCategories[allCategoryTypes.Expense]) }}</span>
|
||||
<f7-icon class="category-separate-icon" f7="chevron_right"></f7-icon>
|
||||
<span>{{ getSecondaryCategoryName(transaction.expenseCategory, allCategories[$constants.category.allCategoryTypes.Expense]) }}</span>
|
||||
<span>{{ getSecondaryCategoryName(transaction.expenseCategory, allCategories[allCategoryTypes.Expense]) }}</span>
|
||||
</div>
|
||||
<div class="list-item-custom-title" v-else-if="!hasAvailableExpenseCategories">
|
||||
<span>{{ $t('None') }}</span>
|
||||
@@ -97,7 +97,7 @@
|
||||
primary-sub-items-field="subCategories"
|
||||
secondary-key-field="id" secondary-value-field="id" secondary-title-field="name"
|
||||
secondary-icon-field="icon" secondary-icon-type="category" secondary-color-field="color"
|
||||
:items="allCategories[$constants.category.allCategoryTypes.Expense]"
|
||||
:items="allCategories[allCategoryTypes.Expense]"
|
||||
v-model:show="showCategorySheet"
|
||||
v-model="transaction.expenseCategory">
|
||||
</tree-view-selection-sheet>
|
||||
@@ -110,13 +110,13 @@
|
||||
:class="{ 'disabled': !hasAvailableIncomeCategories, 'readonly': mode === 'view' }"
|
||||
:header="$t('Category')"
|
||||
@click="showCategorySheet = true"
|
||||
v-if="transaction.type === $constants.transaction.allTransactionTypes.Income"
|
||||
v-if="transaction.type === allTransactionTypes.Income"
|
||||
>
|
||||
<template #title>
|
||||
<div class="list-item-custom-title" v-if="hasAvailableIncomeCategories">
|
||||
<span>{{ getPrimaryCategoryName(transaction.incomeCategory, allCategories[$constants.category.allCategoryTypes.Income]) }}</span>
|
||||
<span>{{ getPrimaryCategoryName(transaction.incomeCategory, allCategories[allCategoryTypes.Income]) }}</span>
|
||||
<f7-icon class="category-separate-icon" f7="chevron_right"></f7-icon>
|
||||
<span>{{ getSecondaryCategoryName(transaction.incomeCategory, allCategories[$constants.category.allCategoryTypes.Income]) }}</span>
|
||||
<span>{{ getSecondaryCategoryName(transaction.incomeCategory, allCategories[allCategoryTypes.Income]) }}</span>
|
||||
</div>
|
||||
<div class="list-item-custom-title" v-else-if="!hasAvailableIncomeCategories">
|
||||
<span>{{ $t('None') }}</span>
|
||||
@@ -127,7 +127,7 @@
|
||||
primary-sub-items-field="subCategories"
|
||||
secondary-key-field="id" secondary-value-field="id" secondary-title-field="name"
|
||||
secondary-icon-field="icon" secondary-icon-type="category" secondary-color-field="color"
|
||||
:items="allCategories[$constants.category.allCategoryTypes.Income]"
|
||||
:items="allCategories[allCategoryTypes.Income]"
|
||||
v-model:show="showCategorySheet"
|
||||
v-model="transaction.incomeCategory">
|
||||
</tree-view-selection-sheet>
|
||||
@@ -140,13 +140,13 @@
|
||||
:class="{ 'disabled': !hasAvailableTransferCategories, 'readonly': mode === 'view' }"
|
||||
:header="$t('Category')"
|
||||
@click="showCategorySheet = true"
|
||||
v-if="transaction.type === $constants.transaction.allTransactionTypes.Transfer"
|
||||
v-if="transaction.type === allTransactionTypes.Transfer"
|
||||
>
|
||||
<template #title>
|
||||
<div class="list-item-custom-title" v-if="hasAvailableTransferCategories">
|
||||
<span>{{ getPrimaryCategoryName(transaction.transferCategory, allCategories[$constants.category.allCategoryTypes.Transfer]) }}</span>
|
||||
<span>{{ getPrimaryCategoryName(transaction.transferCategory, allCategories[allCategoryTypes.Transfer]) }}</span>
|
||||
<f7-icon class="category-separate-icon" f7="chevron_right"></f7-icon>
|
||||
<span>{{ getSecondaryCategoryName(transaction.transferCategory, allCategories[$constants.category.allCategoryTypes.Transfer]) }}</span>
|
||||
<span>{{ getSecondaryCategoryName(transaction.transferCategory, allCategories[allCategoryTypes.Transfer]) }}</span>
|
||||
</div>
|
||||
<div class="list-item-custom-title" v-else-if="!hasAvailableTransferCategories">
|
||||
<span>{{ $t('None') }}</span>
|
||||
@@ -157,7 +157,7 @@
|
||||
primary-sub-items-field="subCategories"
|
||||
secondary-key-field="id" secondary-value-field="id" secondary-title-field="name"
|
||||
secondary-icon-field="icon" secondary-icon-type="category" secondary-color-field="color"
|
||||
:items="allCategories[$constants.category.allCategoryTypes.Transfer]"
|
||||
:items="allCategories[allCategoryTypes.Transfer]"
|
||||
v-model:show="showCategorySheet"
|
||||
v-model="transaction.transferCategory">
|
||||
</tree-view-selection-sheet>
|
||||
@@ -167,8 +167,8 @@
|
||||
class="list-item-with-header-and-title"
|
||||
link="#" no-chevron
|
||||
:class="{ 'disabled': !allVisibleAccounts.length, 'readonly': mode === 'view' }"
|
||||
:header="$t(sourceAccountName)"
|
||||
:title="transaction.sourceAccountId ? $utilities.getNameByKeyValue(allAccounts, transaction.sourceAccountId, 'id', 'name') : $t('None')"
|
||||
:header="$t(sourceAccountTitle)"
|
||||
:title="sourceAccountName"
|
||||
@click="showSourceAccountSheet = true"
|
||||
>
|
||||
<two-column-list-item-selection-sheet primary-key-field="id" primary-value-field="category"
|
||||
@@ -190,8 +190,8 @@
|
||||
link="#" no-chevron
|
||||
:class="{ 'disabled': !allVisibleAccounts.length, 'readonly': mode === 'view' }"
|
||||
:header="$t('Destination Account')"
|
||||
:title="transaction.destinationAccountId ? $utilities.getNameByKeyValue(allAccounts, transaction.destinationAccountId, 'id', 'name') : $t('None')"
|
||||
v-if="transaction.type === $constants.transaction.allTransactionTypes.Transfer"
|
||||
:title="destinationAccountName"
|
||||
v-if="transaction.type === allTransactionTypes.Transfer"
|
||||
@click="showDestinationAccountSheet = true"
|
||||
>
|
||||
<two-column-list-item-selection-sheet primary-key-field="id" primary-value-field="category"
|
||||
@@ -213,7 +213,7 @@
|
||||
link="#" no-chevron
|
||||
:class="{ 'readonly': mode === 'view' }"
|
||||
:header="$t('Transaction Time')"
|
||||
:title="$utilities.formatUnixTime($utilities.getActualUnixTimeForStore(transaction.time, $utilities.getTimezoneOffsetMinutes(), $utilities.getBrowserTimezoneOffsetMinutes()), $locale.getLongDateTimeFormat())"
|
||||
:title="transactionDisplayTime"
|
||||
@click="showTransactionDateTimeSheet = true"
|
||||
>
|
||||
<date-time-selection-sheet v-model:show="showTransactionDateTimeSheet"
|
||||
@@ -234,8 +234,8 @@
|
||||
</select>
|
||||
<template #title>
|
||||
<f7-block class="list-item-custom-title no-padding no-margin">
|
||||
<span>{{ `(UTC${$utilities.getUtcOffsetByUtcOffsetMinutes(transaction.utcOffset)})` }}</span>
|
||||
<span class="transaction-edit-timezone-name" v-if="transaction.timeZone || transaction.timeZone === ''">{{ $utilities.getNameByKeyValue(allTimezones, transaction.timeZone, 'name', 'displayName') }}</span>
|
||||
<span>{{ `(${transactionDisplayTimezone})` }}</span>
|
||||
<span class="transaction-edit-timezone-name" v-if="transaction.timeZone || transaction.timeZone === ''">{{ transactionDisplayTimezoneName }}</span>
|
||||
</f7-block>
|
||||
</template>
|
||||
</f7-list-item>
|
||||
@@ -273,7 +273,7 @@
|
||||
<template #footer>
|
||||
<f7-block class="margin-top-half no-padding no-margin" v-if="transaction.tagIds && transaction.tagIds.length">
|
||||
<f7-chip media-bg-color="black" class="transaction-edit-tag"
|
||||
:text="$utilities.getNameByKeyValue(allTags, tagId, 'id', 'name')"
|
||||
:text="getTagName(tagId)"
|
||||
:key="tagId"
|
||||
v-for="tagId in transaction.tagIds">
|
||||
<template #media>
|
||||
@@ -332,6 +332,43 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useUserStore } from '@/stores/user.js';
|
||||
import { useAccountsStore } from '@/stores/account.js';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
|
||||
import { useTransactionTagsStore } from '@/stores/transactionTag.js';
|
||||
import { useTransactionsStore } from '@/stores/transaction.js';
|
||||
import { useExchangeRatesStore } from '@/stores/exchangeRates.js';
|
||||
|
||||
import categoryConstants from '@/consts/category.js';
|
||||
import transactionConstants from '@/consts/transaction.js';
|
||||
import logger from '@/lib/logger.js';
|
||||
import {
|
||||
isNumber,
|
||||
copyObjectTo,
|
||||
getNameByKeyValue
|
||||
} from '@/lib/common.js';
|
||||
import {
|
||||
getCurrentUnixTime,
|
||||
getTimezoneOffsetMinutes,
|
||||
getBrowserTimezoneOffsetMinutes,
|
||||
getUtcOffsetByUtcOffsetMinutes,
|
||||
getDummyUnixTimeForLocalUsage,
|
||||
getActualUnixTimeForStore
|
||||
} from '@/lib/datetime.js';
|
||||
import {
|
||||
stringCurrencyToNumeric
|
||||
} from '@/lib/currency.js';
|
||||
import {
|
||||
getCategorizedAccounts,
|
||||
getAllFilteredAccountsBalance
|
||||
} from '@/lib/account.js';
|
||||
import {
|
||||
categoryTypeToTransactionType,
|
||||
getTransactionPrimaryCategoryName,
|
||||
getTransactionSecondaryCategoryName
|
||||
} from '@/lib/category.js';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'f7route',
|
||||
@@ -340,15 +377,15 @@ export default {
|
||||
data() {
|
||||
const self = this;
|
||||
const query = self.f7route.query;
|
||||
const now = self.$utilities.getCurrentUnixTime();
|
||||
const now = getCurrentUnixTime();
|
||||
const currentTimezone = self.$locale.getTimezone();
|
||||
|
||||
let defaultType = self.$constants.transaction.allTransactionTypes.Expense;
|
||||
let defaultType = transactionConstants.allTransactionTypes.Expense;
|
||||
|
||||
if (query.type === self.$constants.transaction.allTransactionTypes.Income.toString()) {
|
||||
defaultType = self.$constants.transaction.allTransactionTypes.Income;
|
||||
} else if (query.type === self.$constants.transaction.allTransactionTypes.Transfer.toString()) {
|
||||
defaultType = self.$constants.transaction.allTransactionTypes.Transfer;
|
||||
if (query.type === transactionConstants.allTransactionTypes.Income.toString()) {
|
||||
defaultType = transactionConstants.allTransactionTypes.Income;
|
||||
} else if (query.type === transactionConstants.allTransactionTypes.Transfer.toString()) {
|
||||
defaultType = transactionConstants.allTransactionTypes.Transfer;
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -358,7 +395,7 @@ export default {
|
||||
type: defaultType,
|
||||
time: now,
|
||||
timeZone: currentTimezone,
|
||||
utcOffset: self.$utilities.getTimezoneOffsetMinutes(currentTimezone),
|
||||
utcOffset: getTimezoneOffsetMinutes(currentTimezone),
|
||||
expenseCategory: '',
|
||||
incomeCategory: '',
|
||||
transferCategory: '',
|
||||
@@ -390,6 +427,7 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useUserStore, useAccountsStore, useTransactionCategoriesStore, useTransactionTagsStore, useTransactionsStore, useExchangeRatesStore),
|
||||
title() {
|
||||
if (this.mode === 'add') {
|
||||
return 'Add Transaction';
|
||||
@@ -407,48 +445,51 @@ export default {
|
||||
}
|
||||
},
|
||||
sourceAmountName() {
|
||||
if (this.transaction.type === this.$constants.transaction.allTransactionTypes.Expense) {
|
||||
if (this.transaction.type === this.allTransactionTypes.Expense) {
|
||||
return 'Expense Amount';
|
||||
} else if (this.transaction.type === this.$constants.transaction.allTransactionTypes.Income) {
|
||||
} else if (this.transaction.type === this.allTransactionTypes.Income) {
|
||||
return 'Income Amount';
|
||||
} else if (this.transaction.type === this.$constants.transaction.allTransactionTypes.Transfer) {
|
||||
} else if (this.transaction.type === this.allTransactionTypes.Transfer) {
|
||||
return 'Transfer Out Amount';
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
sourceAccountName() {
|
||||
if (this.transaction.type === this.$constants.transaction.allTransactionTypes.Expense || this.transaction.type === this.$constants.transaction.allTransactionTypes.Income) {
|
||||
sourceAccountTitle() {
|
||||
if (this.transaction.type === this.allTransactionTypes.Expense || this.transaction.type === this.allTransactionTypes.Income) {
|
||||
return 'Account';
|
||||
} else if (this.transaction.type === this.$constants.transaction.allTransactionTypes.Transfer) {
|
||||
} else if (this.transaction.type === this.allTransactionTypes.Transfer) {
|
||||
return 'Source Account';
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
defaultCurrency() {
|
||||
return this.$store.getters.currentUserDefaultCurrency;
|
||||
return this.userStore.currentUserDefaultCurrency;
|
||||
},
|
||||
defaultAccountId() {
|
||||
return this.$store.getters.currentUserDefaultAccountId;
|
||||
return this.userStore.currentUserDefaultAccountId;
|
||||
},
|
||||
defaultFirstDayOfWeek() {
|
||||
return this.$store.getters.currentUserFirstDayOfWeek;
|
||||
allTransactionTypes() {
|
||||
return transactionConstants.allTransactionTypes;
|
||||
},
|
||||
allCategoryTypes() {
|
||||
return categoryConstants.allCategoryTypes;
|
||||
},
|
||||
allTimezones() {
|
||||
return this.$locale.getAllTimezones(true);
|
||||
},
|
||||
allAccounts() {
|
||||
return this.$store.getters.allPlainAccounts;
|
||||
return this.accountsStore.allPlainAccounts;
|
||||
},
|
||||
allVisibleAccounts() {
|
||||
return this.$store.getters.allVisiblePlainAccounts;
|
||||
return this.accountsStore.allVisiblePlainAccounts;
|
||||
},
|
||||
allAccountsMap() {
|
||||
return this.$store.state.allAccountsMap;
|
||||
return this.accountsStore.allAccountsMap;
|
||||
},
|
||||
categorizedAccounts() {
|
||||
const categorizedAccounts = this.$utilities.copyObjectTo(this.$utilities.getCategorizedAccounts(this.allVisibleAccounts), {});
|
||||
const categorizedAccounts = copyObjectTo(getCategorizedAccounts(this.allVisibleAccounts), {});
|
||||
|
||||
for (let category in categorizedAccounts) {
|
||||
if (!Object.prototype.hasOwnProperty.call(categorizedAccounts, category)) {
|
||||
@@ -472,7 +513,7 @@ export default {
|
||||
}
|
||||
|
||||
if (this.showAccountBalance) {
|
||||
const accountsBalance = this.$utilities.getAllFilteredAccountsBalance(categorizedAccounts, account => account.category === accountCategory.category);
|
||||
const accountsBalance = getAllFilteredAccountsBalance(categorizedAccounts, account => account.category === accountCategory.category);
|
||||
let totalBalance = 0;
|
||||
let hasUnCalculatedAmount = false;
|
||||
|
||||
@@ -484,9 +525,9 @@ export default {
|
||||
totalBalance -= accountsBalance[i].balance;
|
||||
}
|
||||
} else {
|
||||
const balance = this.$store.getters.getExchangedAmount(accountsBalance[i].balance, accountsBalance[i].currency, this.defaultCurrency);
|
||||
const balance = this.exchangeRatesStore.getExchangedAmount(accountsBalance[i].balance, accountsBalance[i].currency, this.defaultCurrency);
|
||||
|
||||
if (!this.$utilities.isNumber(balance)) {
|
||||
if (!isNumber(balance)) {
|
||||
hasUnCalculatedAmount = true;
|
||||
continue;
|
||||
}
|
||||
@@ -512,38 +553,61 @@ export default {
|
||||
return categorizedAccounts;
|
||||
},
|
||||
allCategories() {
|
||||
return this.$store.state.allTransactionCategories;
|
||||
return this.transactionCategoriesStore.allTransactionCategories;
|
||||
},
|
||||
allCategoriesMap() {
|
||||
return this.$store.state.allTransactionCategoriesMap;
|
||||
return this.transactionCategoriesStore.allTransactionCategoriesMap;
|
||||
},
|
||||
allTags() {
|
||||
return this.$store.state.allTransactionTags;
|
||||
return this.transactionTagsStore.allTransactionTags;
|
||||
},
|
||||
hasAvailableExpenseCategories() {
|
||||
if (!this.allCategories || !this.allCategories[this.$constants.category.allCategoryTypes.Expense] || !this.allCategories[this.$constants.category.allCategoryTypes.Expense].length) {
|
||||
if (!this.allCategories || !this.allCategories[this.allCategoryTypes.Expense] || !this.allCategories[this.allCategoryTypes.Expense].length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const firstAvailableCategoryId = this.getFirstAvailableCategoryId(this.allCategories[this.$constants.category.allCategoryTypes.Expense]);
|
||||
const firstAvailableCategoryId = this.getFirstAvailableCategoryId(this.allCategories[this.allCategoryTypes.Expense]);
|
||||
return firstAvailableCategoryId !== '';
|
||||
},
|
||||
hasAvailableIncomeCategories() {
|
||||
if (!this.allCategories || !this.allCategories[this.$constants.category.allCategoryTypes.Income] || !this.allCategories[this.$constants.category.allCategoryTypes.Income].length) {
|
||||
if (!this.allCategories || !this.allCategories[this.allCategoryTypes.Income] || !this.allCategories[this.allCategoryTypes.Income].length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const firstAvailableCategoryId = this.getFirstAvailableCategoryId(this.allCategories[this.$constants.category.allCategoryTypes.Income]);
|
||||
const firstAvailableCategoryId = this.getFirstAvailableCategoryId(this.allCategories[this.allCategoryTypes.Income]);
|
||||
return firstAvailableCategoryId !== '';
|
||||
},
|
||||
hasAvailableTransferCategories() {
|
||||
if (!this.allCategories || !this.allCategories[this.$constants.category.allCategoryTypes.Transfer] || !this.allCategories[this.$constants.category.allCategoryTypes.Transfer].length) {
|
||||
if (!this.allCategories || !this.allCategories[this.allCategoryTypes.Transfer] || !this.allCategories[this.allCategoryTypes.Transfer].length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const firstAvailableCategoryId = this.getFirstAvailableCategoryId(this.allCategories[this.$constants.category.allCategoryTypes.Transfer]);
|
||||
const firstAvailableCategoryId = this.getFirstAvailableCategoryId(this.allCategories[this.allCategoryTypes.Transfer]);
|
||||
return firstAvailableCategoryId !== '';
|
||||
},
|
||||
sourceAccountName() {
|
||||
if (this.transaction.sourceAccountId) {
|
||||
return getNameByKeyValue(this.allAccounts, this.transaction.sourceAccountId, 'id', 'name');
|
||||
} else {
|
||||
return this.$t('None');
|
||||
}
|
||||
},
|
||||
destinationAccountName() {
|
||||
if (this.transaction.destinationAccountId) {
|
||||
return getNameByKeyValue(this.allAccounts, this.transaction.destinationAccountId, 'id', 'name');
|
||||
} else {
|
||||
return this.$t('None');
|
||||
}
|
||||
},
|
||||
transactionDisplayTime() {
|
||||
return this.$locale.formatUnixTimeToLongDateTime(this.userStore, getActualUnixTimeForStore(this.transaction.time, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()))
|
||||
},
|
||||
transactionDisplayTimezone() {
|
||||
return `UTC${getUtcOffsetByUtcOffsetMinutes(this.transaction.utcOffset)}`;
|
||||
},
|
||||
transactionDisplayTimezoneName() {
|
||||
return getNameByKeyValue(this.allTimezones, this.transaction.timeZone, 'name', 'displayName');
|
||||
},
|
||||
sourceAmountFontSize() {
|
||||
return this.getFontSizeByAmount(this.transaction.sourceAmount);
|
||||
},
|
||||
@@ -559,6 +623,12 @@ export default {
|
||||
return this.$t('No Location');
|
||||
}
|
||||
},
|
||||
allowedMinAmount() {
|
||||
return transactionConstants.minAmount;
|
||||
},
|
||||
allowedMaxAmount() {
|
||||
return transactionConstants.maxAmount;
|
||||
},
|
||||
inputIsEmpty() {
|
||||
return !!this.inputEmptyProblemMessage;
|
||||
},
|
||||
@@ -572,28 +642,28 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.transaction.type === this.$constants.transaction.allTransactionTypes.Expense || this.transaction.type === this.$constants.transaction.allTransactionTypes.Income) {
|
||||
if (this.transaction.type === this.allTransactionTypes.Expense || this.transaction.type === this.allTransactionTypes.Income) {
|
||||
this.transaction.destinationAmount = newValue;
|
||||
} else if (this.transaction.type === this.$constants.transaction.allTransactionTypes.Transfer) {
|
||||
} else if (this.transaction.type === this.allTransactionTypes.Transfer) {
|
||||
const sourceAccount = this.allAccountsMap[this.transaction.sourceAccountId]
|
||||
const destinationAccount = this.allAccountsMap[this.transaction.destinationAccountId]
|
||||
|
||||
if (sourceAccount && destinationAccount && sourceAccount.currency !== destinationAccount.currency) {
|
||||
const exchangedOldValue = this.$store.getters.getExchangedAmount(oldValue, sourceAccount.currency, destinationAccount.currency);
|
||||
const exchangedNewValue = this.$store.getters.getExchangedAmount(newValue, sourceAccount.currency, destinationAccount.currency);
|
||||
const exchangedOldValue = this.exchangeRatesStore.getExchangedAmount(oldValue, sourceAccount.currency, destinationAccount.currency);
|
||||
const exchangedNewValue = this.exchangeRatesStore.getExchangedAmount(newValue, sourceAccount.currency, destinationAccount.currency);
|
||||
|
||||
if (this.$utilities.isNumber(exchangedOldValue)) {
|
||||
if (isNumber(exchangedOldValue)) {
|
||||
oldValue = Math.floor(exchangedOldValue);
|
||||
}
|
||||
|
||||
if (this.$utilities.isNumber(exchangedNewValue)) {
|
||||
if (isNumber(exchangedNewValue)) {
|
||||
newValue = Math.floor(exchangedNewValue);
|
||||
}
|
||||
}
|
||||
|
||||
if ((!sourceAccount || !destinationAccount || this.transaction.destinationAmount === oldValue) &&
|
||||
(this.$utilities.stringCurrencyToNumeric(this.$constants.transaction.minAmount) <= newValue &&
|
||||
newValue <= this.$utilities.stringCurrencyToNumeric(this.$constants.transaction.maxAmount))) {
|
||||
(stringCurrencyToNumeric(this.allowedMinAmount) <= newValue &&
|
||||
newValue <= stringCurrencyToNumeric(this.allowedMaxAmount))) {
|
||||
this.transaction.destinationAmount = newValue;
|
||||
}
|
||||
}
|
||||
@@ -603,7 +673,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.transaction.type === this.$constants.transaction.allTransactionTypes.Expense || this.transaction.type === this.$constants.transaction.allTransactionTypes.Income) {
|
||||
if (this.transaction.type === this.allTransactionTypes.Expense || this.transaction.type === this.allTransactionTypes.Income) {
|
||||
this.transaction.sourceAmount = newValue;
|
||||
}
|
||||
},
|
||||
@@ -629,9 +699,9 @@ export default {
|
||||
self.loading = true;
|
||||
|
||||
const promises = [
|
||||
self.$store.dispatch('loadAllAccounts', { force: false }),
|
||||
self.$store.dispatch('loadAllCategories', { force: false }),
|
||||
self.$store.dispatch('loadAllTags', { force: false })
|
||||
self.accountsStore.loadAllAccounts({ force: false }),
|
||||
self.transactionCategoriesStore.loadAllCategories({ force: false }),
|
||||
self.transactionTagsStore.loadAllTags({ force: false })
|
||||
];
|
||||
|
||||
if (query.id) {
|
||||
@@ -639,12 +709,12 @@ export default {
|
||||
self.editTransactionId = query.id;
|
||||
}
|
||||
|
||||
promises.push(self.$store.dispatch('getTransaction', { transactionId: query.id }));
|
||||
promises.push(self.transactionsStore.getTransaction({ transactionId: query.id }));
|
||||
}
|
||||
|
||||
if (query.type && query.type !== '0' &&
|
||||
query.type >= self.$constants.transaction.allTransactionTypes.Income &&
|
||||
query.type <= self.$constants.transaction.allTransactionTypes.Transfer) {
|
||||
query.type >= self.allTransactionTypes.Income &&
|
||||
query.type <= self.allTransactionTypes.Transfer) {
|
||||
self.transaction.type = parseInt(query.type);
|
||||
}
|
||||
|
||||
@@ -657,43 +727,43 @@ export default {
|
||||
|
||||
if ((!query.type || query.type === '0') && query.categoryId && query.categoryId !== '0' && self.allCategoriesMap[query.categoryId]) {
|
||||
const category = self.allCategoriesMap[query.categoryId];
|
||||
const type = self.$utilities.categoryTypeToTransactionType(category.type);
|
||||
const type = categoryTypeToTransactionType(category.type);
|
||||
|
||||
if (self.$utilities.isNumber(type)) {
|
||||
if (isNumber(type)) {
|
||||
self.transaction.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
if (self.allCategories[self.$constants.category.allCategoryTypes.Expense] &&
|
||||
self.allCategories[self.$constants.category.allCategoryTypes.Expense].length) {
|
||||
if (query.categoryId && query.categoryId !== '0' && self.isCategoryIdAvailable(self.allCategories[self.$constants.category.allCategoryTypes.Expense], query.categoryId)) {
|
||||
if (self.allCategories[self.allCategoryTypes.Expense] &&
|
||||
self.allCategories[self.allCategoryTypes.Expense].length) {
|
||||
if (query.categoryId && query.categoryId !== '0' && self.isCategoryIdAvailable(self.allCategories[self.allCategoryTypes.Expense], query.categoryId)) {
|
||||
self.transaction.expenseCategory = query.categoryId;
|
||||
}
|
||||
|
||||
if (!self.transaction.expenseCategory) {
|
||||
self.transaction.expenseCategory = self.getFirstAvailableCategoryId(self.allCategories[self.$constants.category.allCategoryTypes.Expense]);
|
||||
self.transaction.expenseCategory = self.getFirstAvailableCategoryId(self.allCategories[self.allCategoryTypes.Expense]);
|
||||
}
|
||||
}
|
||||
|
||||
if (self.allCategories[self.$constants.category.allCategoryTypes.Income] &&
|
||||
self.allCategories[self.$constants.category.allCategoryTypes.Income].length) {
|
||||
if (query.categoryId && query.categoryId !== '0' && self.isCategoryIdAvailable(self.allCategories[self.$constants.category.allCategoryTypes.Income], query.categoryId)) {
|
||||
if (self.allCategories[self.allCategoryTypes.Income] &&
|
||||
self.allCategories[self.allCategoryTypes.Income].length) {
|
||||
if (query.categoryId && query.categoryId !== '0' && self.isCategoryIdAvailable(self.allCategories[self.allCategoryTypes.Income], query.categoryId)) {
|
||||
self.transaction.incomeCategory = query.categoryId;
|
||||
}
|
||||
|
||||
if (!self.transaction.incomeCategory) {
|
||||
self.transaction.incomeCategory = self.getFirstAvailableCategoryId(self.allCategories[self.$constants.category.allCategoryTypes.Income]);
|
||||
self.transaction.incomeCategory = self.getFirstAvailableCategoryId(self.allCategories[self.allCategoryTypes.Income]);
|
||||
}
|
||||
}
|
||||
|
||||
if (self.allCategories[self.$constants.category.allCategoryTypes.Transfer] &&
|
||||
self.allCategories[self.$constants.category.allCategoryTypes.Transfer].length) {
|
||||
if (query.categoryId && query.categoryId !== '0' && self.isCategoryIdAvailable(self.allCategories[self.$constants.category.allCategoryTypes.Transfer], query.categoryId)) {
|
||||
if (self.allCategories[self.allCategoryTypes.Transfer] &&
|
||||
self.allCategories[self.allCategoryTypes.Transfer].length) {
|
||||
if (query.categoryId && query.categoryId !== '0' && self.isCategoryIdAvailable(self.allCategories[self.allCategoryTypes.Transfer], query.categoryId)) {
|
||||
self.transaction.transferCategory = query.categoryId;
|
||||
}
|
||||
|
||||
if (!self.transaction.transferCategory) {
|
||||
self.transaction.transferCategory = self.getFirstAvailableCategoryId(self.allCategories[self.$constants.category.allCategoryTypes.Transfer]);
|
||||
self.transaction.transferCategory = self.getFirstAvailableCategoryId(self.allCategories[self.allCategoryTypes.Transfer]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -734,18 +804,18 @@ export default {
|
||||
|
||||
self.transaction.type = transaction.type;
|
||||
|
||||
if (self.transaction.type === self.$constants.transaction.allTransactionTypes.Expense) {
|
||||
if (self.transaction.type === self.allTransactionTypes.Expense) {
|
||||
self.transaction.expenseCategory = transaction.categoryId;
|
||||
} else if (self.transaction.type === self.$constants.transaction.allTransactionTypes.Income) {
|
||||
} else if (self.transaction.type === self.allTransactionTypes.Income) {
|
||||
self.transaction.incomeCategory = transaction.categoryId;
|
||||
} else if (self.transaction.type === self.$constants.transaction.allTransactionTypes.Transfer) {
|
||||
} else if (self.transaction.type === self.allTransactionTypes.Transfer) {
|
||||
self.transaction.transferCategory = transaction.categoryId;
|
||||
}
|
||||
|
||||
if (self.mode === 'edit' || self.mode === 'view') {
|
||||
self.transaction.utcOffset = transaction.utcOffset;
|
||||
self.transaction.timeZone = null;
|
||||
self.transaction.time = self.$utilities.getDummyUnixTimeForLocalUsage(transaction.time, self.transaction.utcOffset, self.$utilities.getBrowserTimezoneOffsetMinutes());
|
||||
self.transaction.time = getDummyUnixTimeForLocalUsage(transaction.time, self.transaction.utcOffset, getBrowserTimezoneOffsetMinutes());
|
||||
}
|
||||
|
||||
self.transaction.sourceAccountId = transaction.sourceAccountId;
|
||||
@@ -771,7 +841,7 @@ export default {
|
||||
|
||||
self.loading = false;
|
||||
}).catch(error => {
|
||||
self.$logger.error('failed to load essential data for editing transaction', error);
|
||||
logger.error('failed to load essential data for editing transaction', error);
|
||||
|
||||
if (error.processed) {
|
||||
self.loading = false;
|
||||
@@ -800,7 +870,7 @@ export default {
|
||||
|
||||
const submitTransaction = {
|
||||
type: self.transaction.type,
|
||||
time: self.$utilities.getActualUnixTimeForStore(self.transaction.time, self.transaction.utcOffset, self.$utilities.getBrowserTimezoneOffsetMinutes()),
|
||||
time: getActualUnixTimeForStore(self.transaction.time, self.transaction.utcOffset, getBrowserTimezoneOffsetMinutes()),
|
||||
sourceAccountId: self.transaction.sourceAccountId,
|
||||
sourceAmount: self.transaction.sourceAmount,
|
||||
destinationAccountId: '0',
|
||||
@@ -812,11 +882,11 @@ export default {
|
||||
utcOffset: self.transaction.utcOffset
|
||||
};
|
||||
|
||||
if (self.transaction.type === self.$constants.transaction.allTransactionTypes.Expense) {
|
||||
if (self.transaction.type === self.allTransactionTypes.Expense) {
|
||||
submitTransaction.categoryId = self.transaction.expenseCategory;
|
||||
} else if (self.transaction.type === self.$constants.transaction.allTransactionTypes.Income) {
|
||||
} else if (self.transaction.type === self.allTransactionTypes.Income) {
|
||||
submitTransaction.categoryId = self.transaction.incomeCategory;
|
||||
} else if (self.transaction.type === self.$constants.transaction.allTransactionTypes.Transfer) {
|
||||
} else if (self.transaction.type === self.allTransactionTypes.Transfer) {
|
||||
submitTransaction.categoryId = self.transaction.transferCategory;
|
||||
submitTransaction.destinationAccountId = self.transaction.destinationAccountId;
|
||||
submitTransaction.destinationAmount = self.transaction.destinationAmount;
|
||||
@@ -833,7 +903,7 @@ export default {
|
||||
self.submitting = true;
|
||||
self.$showLoading(() => self.submitting);
|
||||
|
||||
self.$store.dispatch('saveTransaction', {
|
||||
self.transactionsStore.saveTransaction({
|
||||
transaction: submitTransaction,
|
||||
defaultCurrency: self.defaultCurrency
|
||||
}).then(() => {
|
||||
@@ -869,7 +939,7 @@ export default {
|
||||
const self = this;
|
||||
|
||||
if (!self.isSupportGeoLocation) {
|
||||
self.$logger.warn('this browser does not support geo location');
|
||||
logger.warn('this browser does not support geo location');
|
||||
|
||||
if (forceUpdate) {
|
||||
self.$toast('Unable to get current position');
|
||||
@@ -879,7 +949,7 @@ export default {
|
||||
|
||||
navigator.geolocation.getCurrentPosition(function (position) {
|
||||
if (!position || !position.coords) {
|
||||
self.$logger.error('current position is null');
|
||||
logger.error('current position is null');
|
||||
self.geoLocationStatus = 'error';
|
||||
|
||||
if (forceUpdate) {
|
||||
@@ -896,7 +966,7 @@ export default {
|
||||
longitude: position.coords.longitude
|
||||
};
|
||||
}, function (err) {
|
||||
self.$logger.error('cannot get current position', err);
|
||||
logger.error('cannot get current position', err);
|
||||
self.geoLocationStatus = 'error';
|
||||
|
||||
if (forceUpdate) {
|
||||
@@ -953,10 +1023,13 @@ export default {
|
||||
return this.$locale.getDisplayCurrency(amount);
|
||||
},
|
||||
getPrimaryCategoryName(categoryId, allCategories) {
|
||||
return this.$utilities.getTransactionPrimaryCategoryName(categoryId, allCategories);
|
||||
return getTransactionPrimaryCategoryName(categoryId, allCategories);
|
||||
},
|
||||
getSecondaryCategoryName(categoryId, allCategories) {
|
||||
return this.$utilities.getTransactionSecondaryCategoryName(categoryId, allCategories);
|
||||
return getTransactionSecondaryCategoryName(categoryId, allCategories);
|
||||
},
|
||||
getTagName(tagId) {
|
||||
return getNameByKeyValue(this.allTags, tagId, 'id', 'name');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
<f7-list-item>
|
||||
<template #title>
|
||||
<small>
|
||||
<span>{{ $utilities.formatTime(transactionMonthList.yearMonth, $locale.getLongYearMonthFormat()) }}</span>
|
||||
<span>{{ getDisplayYearMonth(transactionMonthList) }}</span>
|
||||
</small>
|
||||
<small class="transaction-amount-statistics" v-if="showTotalAmountInTransactionListPage && transactionMonthList.totalAmount">
|
||||
<span class="text-color-red">
|
||||
@@ -144,7 +144,7 @@
|
||||
<f7-list-item swipeout chevron-center
|
||||
class="transaction-info"
|
||||
:id="getTransactionDomId(transaction)"
|
||||
:link="transaction.type !== $constants.transaction.allTransactionTypes.ModifyBalance ? `/transaction/detail?id=${transaction.id}&type=${transaction.type}` : null"
|
||||
:link="transaction.type !== allTransactionTypes.ModifyBalance ? `/transaction/detail?id=${transaction.id}&type=${transaction.type}` : null"
|
||||
:key="transaction.id"
|
||||
v-for="(transaction, idx) in transactionMonthList.items"
|
||||
>
|
||||
@@ -175,20 +175,20 @@
|
||||
<div class="item-title-row">
|
||||
<div class="item-title">
|
||||
<div class="transaction-category-name no-padding">
|
||||
<span v-if="transaction.type === $constants.transaction.allTransactionTypes.ModifyBalance">
|
||||
<span v-if="transaction.type === allTransactionTypes.ModifyBalance">
|
||||
{{ $t('Modify Balance') }}
|
||||
</span>
|
||||
<span v-else-if="transaction.type !== $constants.transaction.allTransactionTypes.ModifyBalance && transaction.category">
|
||||
<span v-else-if="transaction.type !== allTransactionTypes.ModifyBalance && transaction.category">
|
||||
{{ transaction.category.name }}
|
||||
</span>
|
||||
<span v-else-if="transaction.type !== $constants.transaction.allTransactionTypes.ModifyBalance && !transaction.category">
|
||||
<span v-else-if="transaction.type !== allTransactionTypes.ModifyBalance && !transaction.category">
|
||||
{{ getTransactionTypeName(transaction.type, 'Transaction') }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-after">
|
||||
<div class="transaction-amount" v-if="transaction.sourceAccount"
|
||||
:class="{ 'text-color-teal': transaction.type === $constants.transaction.allTransactionTypes.Expense, 'text-color-red': transaction.type === $constants.transaction.allTransactionTypes.Income }">
|
||||
:class="{ 'text-color-teal': transaction.type === allTransactionTypes.Expense, 'text-color-red': transaction.type === allTransactionTypes.Income }">
|
||||
<span v-if="!query.accountId || query.accountId === '0' || (transaction.sourceAccount && (transaction.sourceAccount.id === query.accountId || transaction.sourceAccount.parentId === query.accountId))">{{ getDisplayAmount(transaction.sourceAmount, transaction.sourceAccount.currency, transaction.hideAmount) }}</span>
|
||||
<span v-else-if="query.accountId && query.accountId !== '0' && transaction.destinationAccount && (transaction.destinationAccount.id === query.accountId || transaction.destinationAccount.parentId === query.accountId)">{{ getDisplayAmount(transaction.destinationAmount, transaction.destinationAccount.currency, transaction.hideAmount) }}</span>
|
||||
<span v-else></span>
|
||||
@@ -202,12 +202,12 @@
|
||||
</div>
|
||||
<div class="item-footer">
|
||||
<div class="transaction-footer">
|
||||
<span>{{ $utilities.formatUnixTime(transaction.time, $locale.getShortTimeFormat(), transaction.utcOffset, currentTimezoneOffsetMinutes) }}</span>
|
||||
<span v-if="transaction.utcOffset !== currentTimezoneOffsetMinutes">{{ `(UTC${$utilities.getUtcOffsetByUtcOffsetMinutes(transaction.utcOffset)})` }}</span>
|
||||
<span>{{ getDisplayTime(transaction) }}</span>
|
||||
<span v-if="transaction.utcOffset !== currentTimezoneOffsetMinutes">{{ `(${getDisplayTimezone(transaction)})` }}</span>
|
||||
<span v-if="transaction.sourceAccount">·</span>
|
||||
<span v-if="transaction.sourceAccount">{{ transaction.sourceAccount.name }}</span>
|
||||
<span v-if="transaction.sourceAccount && transaction.type === $constants.transaction.allTransactionTypes.Transfer && transaction.destinationAccount && transaction.sourceAccount.id !== transaction.destinationAccount.id">→</span>
|
||||
<span v-if="transaction.sourceAccount && transaction.type === $constants.transaction.allTransactionTypes.Transfer && transaction.destinationAccount && transaction.sourceAccount.id !== transaction.destinationAccount.id">{{ transaction.destinationAccount.name }}</span>
|
||||
<span v-if="transaction.sourceAccount && transaction.type === allTransactionTypes.Transfer && transaction.destinationAccount && transaction.sourceAccount.id !== transaction.destinationAccount.id">→</span>
|
||||
<span v-if="transaction.sourceAccount && transaction.type === allTransactionTypes.Transfer && transaction.destinationAccount && transaction.sourceAccount.id !== transaction.destinationAccount.id">{{ transaction.destinationAccount.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -216,11 +216,11 @@
|
||||
<f7-swipeout-actions right>
|
||||
<f7-swipeout-button color="primary" close
|
||||
:text="$t('Duplicate')"
|
||||
v-if="transaction.type !== $constants.transaction.allTransactionTypes.ModifyBalance"
|
||||
v-if="transaction.type !== allTransactionTypes.ModifyBalance"
|
||||
@click="duplicate(transaction)"></f7-swipeout-button>
|
||||
<f7-swipeout-button color="orange" close
|
||||
:text="$t('Edit')"
|
||||
v-if="transaction.editable && transaction.type !== $constants.transaction.allTransactionTypes.ModifyBalance"
|
||||
v-if="transaction.editable && transaction.type !== allTransactionTypes.ModifyBalance"
|
||||
@click="edit(transaction)"></f7-swipeout-button>
|
||||
<f7-swipeout-button color="red" class="padding-left padding-right"
|
||||
v-if="transaction.editable"
|
||||
@@ -251,11 +251,11 @@
|
||||
<f7-icon class="list-item-checked-icon" f7="checkmark_alt" v-if="query.dateType === dateRange.type"></f7-icon>
|
||||
</template>
|
||||
<template #footer>
|
||||
<div v-if="dateRange.type === $constants.datetime.allDateRanges.Custom.type && query.dateType === $constants.datetime.allDateRanges.Custom.type && query.minTime && query.maxTime">
|
||||
<span>{{ $utilities.formatUnixTime(query.minTime, $locale.getLongDateTimeFormat()) }}</span>
|
||||
<div v-if="dateRange.type === allDateRanges.Custom.type && query.dateType === allDateRanges.Custom.type && query.minTime && query.maxTime">
|
||||
<span>{{ queryMinTime }}</span>
|
||||
<span> - </span>
|
||||
<br/>
|
||||
<span>{{ $utilities.formatUnixTime(query.maxTime, $locale.getLongDateTimeFormat()) }}</span>
|
||||
<span>{{ queryMaxTime }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</f7-list-item>
|
||||
@@ -318,9 +318,9 @@
|
||||
class="no-margin-vertical"
|
||||
:key="categoryType"
|
||||
v-for="(categories, categoryType) in allPrimaryCategories"
|
||||
v-show="!query.type || $utilities.categoryTypeToTransactionType(parseInt(categoryType)) === query.type"
|
||||
v-show="!query.type || getTransactionTypeFromCategoryType(categoryType) === query.type"
|
||||
>
|
||||
<f7-list-item divider :title="getTransactionTypeName($utilities.categoryTypeToTransactionType(parseInt(categoryType)), 'Type')"></f7-list-item>
|
||||
<f7-list-item divider :title="getTransactionTypeName(getTransactionTypeFromCategoryType(categoryType), 'Type')"></f7-list-item>
|
||||
<f7-list-item accordion-item
|
||||
:title="category.name"
|
||||
:class="getCategoryListItemCheckedClass(category, query.categoryId)"
|
||||
@@ -407,6 +407,25 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useUserStore } from '@/stores/user.js';
|
||||
import { useAccountsStore } from '@/stores/account.js';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
|
||||
import { useTransactionsStore } from '@/stores/transaction.js';
|
||||
|
||||
import datetimeConstants from '@/consts/datetime.js';
|
||||
import currencyConstants from '@/consts/currency.js';
|
||||
import accountConstants from '@/consts/account.js';
|
||||
import transactionConstants from '@/consts/transaction.js';
|
||||
import { getNameByKeyValue } from '@/lib/common.js';
|
||||
import {
|
||||
getUtcOffsetByUtcOffsetMinutes,
|
||||
getTimezoneOffsetMinutes,
|
||||
getDateRangeByDateType
|
||||
} from '@/lib/datetime.js';
|
||||
import { categoryTypeToTransactionType, transactionTypeToCategoryType } from '@/lib/category.js';
|
||||
import { onSwipeoutDeleted } from '@/lib/ui.mobile.js';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'f7route',
|
||||
@@ -430,22 +449,23 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useUserStore, useAccountsStore, useTransactionCategoriesStore, useTransactionsStore),
|
||||
defaultCurrency() {
|
||||
if (this.query.accountId && this.query.accountId !== '0') {
|
||||
const account = this.allAccounts[this.query.accountId];
|
||||
|
||||
if (account && account.currency && account.currency !== this.$constants.currency.parentAccountCurrencyPlaceholder) {
|
||||
if (account && account.currency && account.currency !== currencyConstants.parentAccountCurrencyPlaceholder) {
|
||||
return account.currency;
|
||||
}
|
||||
}
|
||||
|
||||
return this.$store.getters.currentUserDefaultCurrency;
|
||||
return this.userStore.currentUserDefaultCurrency;
|
||||
},
|
||||
canAddTransaction() {
|
||||
if (this.query.accountId && this.query.accountId !== '0') {
|
||||
const account = this.allAccounts[this.query.accountId];
|
||||
|
||||
if (account && account.type === this.$constants.account.allAccountTypes.MultiSubAccounts) {
|
||||
if (account && account.type === accountConstants.allAccountTypes.MultiSubAccounts) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -453,38 +473,13 @@ export default {
|
||||
return true;
|
||||
},
|
||||
currentTimezoneOffsetMinutes() {
|
||||
return this.$utilities.getTimezoneOffsetMinutes();
|
||||
return getTimezoneOffsetMinutes();
|
||||
},
|
||||
firstDayOfWeek() {
|
||||
return this.$store.getters.currentUserFirstDayOfWeek;
|
||||
return this.userStore.currentUserFirstDayOfWeek;
|
||||
},
|
||||
query() {
|
||||
return this.$store.state.transactionsFilter;
|
||||
},
|
||||
transactions() {
|
||||
if (this.loading) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return this.$store.state.transactions;
|
||||
},
|
||||
noTransaction() {
|
||||
return this.$store.getters.noTransaction;
|
||||
},
|
||||
hasMoreTransaction() {
|
||||
return this.$store.getters.hasMoreTransaction;
|
||||
},
|
||||
allAccounts() {
|
||||
return this.$store.state.allAccountsMap;
|
||||
},
|
||||
allCategories() {
|
||||
return this.$store.state.allTransactionCategoriesMap;
|
||||
},
|
||||
allPrimaryCategories() {
|
||||
return this.$store.state.allTransactionCategories;
|
||||
},
|
||||
allDateRanges() {
|
||||
return this.$constants.datetime.allDateRanges;
|
||||
return this.transactionsStore.transactionsFilter;
|
||||
},
|
||||
queryDateRangeName() {
|
||||
if (this.query.dateType === this.allDateRanges.All.type) {
|
||||
@@ -505,24 +500,58 @@ export default {
|
||||
|
||||
return this.$t('Date');
|
||||
},
|
||||
queryMinTime() {
|
||||
return this.$locale.formatUnixTimeToLongDateTime(this.userStore, this.query.minTime);
|
||||
},
|
||||
queryMaxTime() {
|
||||
return this.$locale.formatUnixTimeToLongDateTime(this.userStore, this.query.maxTime);
|
||||
},
|
||||
queryTransactionTypeName() {
|
||||
return this.getTransactionTypeName(this.query.type, 'Type');
|
||||
},
|
||||
queryCategoryName() {
|
||||
return this.$utilities.getNameByKeyValue(this.allCategories, this.query.categoryId, null, 'name', this.$t('Category'));
|
||||
return getNameByKeyValue(this.allCategories, this.query.categoryId, null, 'name', this.$t('Category'));
|
||||
},
|
||||
queryAccountName() {
|
||||
return this.$utilities.getNameByKeyValue(this.allAccounts, this.query.accountId, null, 'name', this.$t('Account'));
|
||||
return getNameByKeyValue(this.allAccounts, this.query.accountId, null, 'name', this.$t('Account'));
|
||||
},
|
||||
transactions() {
|
||||
if (this.loading) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return this.transactionsStore.transactions;
|
||||
},
|
||||
noTransaction() {
|
||||
return this.transactionsStore.noTransaction;
|
||||
},
|
||||
hasMoreTransaction() {
|
||||
return this.transactionsStore.hasMoreTransaction;
|
||||
},
|
||||
allTransactionTypes() {
|
||||
return transactionConstants.allTransactionTypes;
|
||||
},
|
||||
allAccounts() {
|
||||
return this.accountsStore.allAccountsMap;
|
||||
},
|
||||
allCategories() {
|
||||
return this.transactionCategoriesStore.allTransactionCategoriesMap;
|
||||
},
|
||||
allPrimaryCategories() {
|
||||
return this.transactionCategoriesStore.allTransactionCategories;
|
||||
},
|
||||
allDateRanges() {
|
||||
return datetimeConstants.allDateRanges;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
const self = this;
|
||||
const query = self.f7route.query;
|
||||
|
||||
let dateRange = self.$utilities.getDateRangeByDateType(query.dateType ? parseInt(query.dateType) : undefined, self.firstDayOfWeek);
|
||||
let dateRange = getDateRangeByDateType(query.dateType ? parseInt(query.dateType) : undefined, self.firstDayOfWeek);
|
||||
|
||||
if (!dateRange &&
|
||||
query.dateType === self.$constants.datetime.allDateRanges.Custom.type.toString() &&
|
||||
query.dateType === self.allDateRanges.Custom.type.toString() &&
|
||||
parseInt(query.maxTime) > 0 && parseInt(query.minTime) > 0) {
|
||||
dateRange = {
|
||||
dateType: parseInt(query.dateType),
|
||||
@@ -531,7 +560,7 @@ export default {
|
||||
};
|
||||
}
|
||||
|
||||
this.$store.dispatch('initTransactionListFilter', {
|
||||
this.transactionsStore.initTransactionListFilter({
|
||||
dateType: dateRange ? dateRange.dateType : undefined,
|
||||
maxTime: dateRange ? dateRange.maxTime : undefined,
|
||||
minTime: dateRange ? dateRange.minTime : undefined,
|
||||
@@ -544,7 +573,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
onPageAfterIn() {
|
||||
if (this.$store.state.transactionListStateInvalid && !this.loading) {
|
||||
if (this.transactionsStore.transactionListStateInvalid && !this.loading) {
|
||||
this.reload(null);
|
||||
}
|
||||
|
||||
@@ -558,10 +587,10 @@ export default {
|
||||
}
|
||||
|
||||
Promise.all([
|
||||
self.$store.dispatch('loadAllAccounts', { force: false }),
|
||||
self.$store.dispatch('loadAllCategories', { force: false })
|
||||
self.accountsStore.loadAllAccounts({ force: false }),
|
||||
self.transactionCategoriesStore.loadAllCategories({ force: false })
|
||||
]).then(() => {
|
||||
return self.$store.dispatch('loadTransactions', {
|
||||
return self.transactionsStore.loadTransactions({
|
||||
reload: true,
|
||||
autoExpand: true,
|
||||
defaultCurrency: self.defaultCurrency
|
||||
@@ -603,7 +632,7 @@ export default {
|
||||
|
||||
self.loadingMore = true;
|
||||
|
||||
self.$store.dispatch('loadTransactions', {
|
||||
self.transactionsStore.loadTransactions({
|
||||
reload: false,
|
||||
autoExpand: autoExpand,
|
||||
defaultCurrency: self.defaultCurrency
|
||||
@@ -618,13 +647,13 @@ export default {
|
||||
});
|
||||
},
|
||||
collapseTransactionMonthList(month, collapse) {
|
||||
this.$store.dispatch('collapseMonthInTransactionList', {
|
||||
this.transactionsStore.collapseMonthInTransactionList({
|
||||
month: month,
|
||||
collapse: collapse
|
||||
});
|
||||
},
|
||||
changeDateFilter(dateType) {
|
||||
if (dateType === this.$constants.datetime.allDateRanges.Custom.type) { // Custom
|
||||
if (dateType === this.allDateRanges.Custom.type) { // Custom
|
||||
this.showCustomDateRangeSheet = true;
|
||||
this.showDatePopover = false;
|
||||
return;
|
||||
@@ -632,13 +661,13 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
const dateRange = this.$utilities.getDateRangeByDateType(dateType, this.firstDayOfWeek);
|
||||
const dateRange = getDateRangeByDateType(dateType, this.firstDayOfWeek);
|
||||
|
||||
if (!dateRange) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$store.dispatch('updateTransactionListFilter', {
|
||||
this.transactionsStore.updateTransactionListFilter({
|
||||
dateType: dateRange.dateType,
|
||||
maxTime: dateRange.maxTime,
|
||||
minTime: dateRange.minTime
|
||||
@@ -652,8 +681,8 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$store.dispatch('updateTransactionListFilter', {
|
||||
dateType: this.$constants.datetime.allDateRanges.Custom.type,
|
||||
this.transactionsStore.updateTransactionListFilter({
|
||||
dateType: this.allDateRanges.Custom.type,
|
||||
maxTime: maxTime,
|
||||
minTime: minTime
|
||||
});
|
||||
@@ -672,12 +701,12 @@ export default {
|
||||
if (type && this.query.categoryId) {
|
||||
const category = this.allCategories[this.query.categoryId];
|
||||
|
||||
if (category && category.type !== this.$utilities.transactionTypeToCategoryType(type)) {
|
||||
if (category && category.type !== transactionTypeToCategoryType(type)) {
|
||||
removeCategoryFilter = true;
|
||||
}
|
||||
}
|
||||
|
||||
this.$store.dispatch('updateTransactionListFilter', {
|
||||
this.transactionsStore.updateTransactionListFilter({
|
||||
type: type,
|
||||
categoryId: removeCategoryFilter ? '0' : undefined
|
||||
});
|
||||
@@ -690,7 +719,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$store.dispatch('updateTransactionListFilter', {
|
||||
this.transactionsStore.updateTransactionListFilter({
|
||||
categoryId: categoryId
|
||||
});
|
||||
|
||||
@@ -702,7 +731,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$store.dispatch('updateTransactionListFilter', {
|
||||
this.transactionsStore.updateTransactionListFilter({
|
||||
accountId: accountId
|
||||
});
|
||||
|
||||
@@ -714,7 +743,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$store.dispatch('updateTransactionListFilter', {
|
||||
this.transactionsStore.updateTransactionListFilter({
|
||||
keyword: keyword
|
||||
});
|
||||
|
||||
@@ -744,11 +773,11 @@ export default {
|
||||
self.transactionToDelete = null;
|
||||
self.$showLoading();
|
||||
|
||||
self.$store.dispatch('deleteTransaction', {
|
||||
self.transactionsStore.deleteTransaction({
|
||||
transaction: transaction,
|
||||
defaultCurrency: self.defaultCurrency,
|
||||
beforeResolve: (done) => {
|
||||
self.$ui.onSwipeoutDeleted(self.getTransactionDomId(transaction), done);
|
||||
onSwipeoutDeleted(self.getTransactionDomId(transaction), done);
|
||||
}
|
||||
}).then(() => {
|
||||
self.$hideLoading();
|
||||
@@ -781,6 +810,15 @@ export default {
|
||||
|
||||
container.scrollTop(targetPos);
|
||||
},
|
||||
getDisplayYearMonth(transactionMonthList) {
|
||||
return this.$locale.formatTimeToLongYearMonth(this.userStore, transactionMonthList.yearMonth);
|
||||
},
|
||||
getDisplayTime(transaction) {
|
||||
return this.$locale.formatUnixTimeToShortTime(this.userStore, transaction.time, transaction.utcOffset, this.currentTimezoneOffsetMinutes);
|
||||
},
|
||||
getDisplayTimezone(transaction) {
|
||||
return `UTC${getUtcOffsetByUtcOffsetMinutes(transaction.utcOffset)}`;
|
||||
},
|
||||
getDisplayAmount(amount, currency, hideAmount) {
|
||||
if (hideAmount) {
|
||||
return this.$locale.getDisplayCurrency('***', currency);
|
||||
@@ -794,18 +832,21 @@ export default {
|
||||
},
|
||||
getTransactionTypeName(type, defaultName) {
|
||||
switch (type){
|
||||
case this.$constants.transaction.allTransactionTypes.ModifyBalance:
|
||||
case this.allTransactionTypes.ModifyBalance:
|
||||
return this.$t('Modify Balance');
|
||||
case this.$constants.transaction.allTransactionTypes.Income:
|
||||
case this.allTransactionTypes.Income:
|
||||
return this.$t('Income');
|
||||
case this.$constants.transaction.allTransactionTypes.Expense:
|
||||
case this.allTransactionTypes.Expense:
|
||||
return this.$t('Expense');
|
||||
case this.$constants.transaction.allTransactionTypes.Transfer:
|
||||
case this.allTransactionTypes.Transfer:
|
||||
return this.$t('Transfer');
|
||||
default:
|
||||
return this.$t(defaultName);
|
||||
}
|
||||
},
|
||||
getTransactionTypeFromCategoryType(categoryType) {
|
||||
return categoryTypeToTransactionType(parseInt(categoryType));
|
||||
},
|
||||
getTransactionDomId(transaction) {
|
||||
return 'transaction_' + transaction.id;
|
||||
},
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
</f7-list>
|
||||
|
||||
<f7-list strong inset dividers class="margin-vertical" v-else-if="!loading">
|
||||
<f7-list-item :title="$t('Accounts')" :after="$utilities.appendThousandsSeparator(dataStatistics.totalAccountCount)"></f7-list-item>
|
||||
<f7-list-item :title="$t('Transaction Categories')" :after="$utilities.appendThousandsSeparator(dataStatistics.totalTransactionCategoryCount)"></f7-list-item>
|
||||
<f7-list-item :title="$t('Transaction Tags')" :after="$utilities.appendThousandsSeparator(dataStatistics.totalTransactionTagCount)"></f7-list-item>
|
||||
<f7-list-item :title="$t('Transactions')" :after="$utilities.appendThousandsSeparator(dataStatistics.totalTransactionCount)"></f7-list-item>
|
||||
<f7-list-item :title="$t('Accounts')" :after="displayDataStatistics.totalAccountCount"></f7-list-item>
|
||||
<f7-list-item :title="$t('Transaction Categories')" :after="displayDataStatistics.totalTransactionCategoryCount"></f7-list-item>
|
||||
<f7-list-item :title="$t('Transaction Tags')" :after="displayDataStatistics.totalTransactionTagCount"></f7-list-item>
|
||||
<f7-list-item :title="$t('Transactions')" :after="displayDataStatistics.totalTransactionCount"></f7-list-item>
|
||||
</f7-list>
|
||||
|
||||
<f7-list strong inset dividers class="margin-vertical" :class="{ 'disabled': loading }">
|
||||
@@ -56,6 +56,12 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useRootStore } from '@/stores/index.js';
|
||||
import { useUserStore } from '@/stores/user.js';
|
||||
|
||||
import { appendThousandsSeparator } from '@/lib/common.js';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'f7router'
|
||||
@@ -74,11 +80,26 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useRootStore, useUserStore),
|
||||
displayDataStatistics() {
|
||||
const self = this;
|
||||
|
||||
if (!self.dataStatistics) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
totalAccountCount: appendThousandsSeparator(self.dataStatistics.totalAccountCount),
|
||||
totalTransactionCategoryCount: appendThousandsSeparator(self.dataStatistics.totalTransactionCategoryCount),
|
||||
totalTransactionTagCount: appendThousandsSeparator(self.dataStatistics.totalTransactionTagCount),
|
||||
totalTransactionCount: appendThousandsSeparator(self.dataStatistics.totalTransactionCount)
|
||||
};
|
||||
},
|
||||
isDataExportingEnabled() {
|
||||
return this.$settings.isDataExportingEnabled();
|
||||
},
|
||||
exportFileName() {
|
||||
const nickname = this.$store.getters.currentUserNickname;
|
||||
const nickname = this.userStore.currentUserNickname;
|
||||
|
||||
if (nickname) {
|
||||
return this.$t('dataExport.exportFilename', {
|
||||
@@ -94,7 +115,7 @@ export default {
|
||||
|
||||
self.loading = true;
|
||||
|
||||
self.$store.dispatch('getUserDataStatistics').then(dataStatistics => {
|
||||
self.userStore.getUserDataStatistics().then(dataStatistics => {
|
||||
self.dataStatistics = dataStatistics;
|
||||
self.loading = false;
|
||||
}).catch(error => {
|
||||
@@ -116,7 +137,7 @@ export default {
|
||||
self.$showLoading();
|
||||
self.exportingData = true;
|
||||
|
||||
self.$store.dispatch('getExportedUserData').then(data => {
|
||||
self.userStore.getExportedUserData().then(data => {
|
||||
self.exportedData = URL.createObjectURL(data);
|
||||
self.exportingData = false;
|
||||
self.$hideLoading();
|
||||
@@ -142,7 +163,7 @@ export default {
|
||||
self.clearingData = true;
|
||||
self.$showLoading(() => self.clearingData);
|
||||
|
||||
self.$store.dispatch('clearUserData', {
|
||||
self.rootStore.clearUserData({
|
||||
password: password
|
||||
}).then(() => {
|
||||
self.clearingData = false;
|
||||
@@ -153,7 +174,7 @@ export default {
|
||||
|
||||
self.loading = true;
|
||||
|
||||
self.$store.dispatch('getUserDataStatistics').then(dataStatistics => {
|
||||
self.userStore.getUserDataStatistics().then(dataStatistics => {
|
||||
self.dataStatistics = dataStatistics;
|
||||
self.loading = false;
|
||||
}).catch(error => {
|
||||
|
||||
@@ -43,6 +43,15 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useUserStore } from '@/stores/user.js';
|
||||
import { useTokensStore } from '@/stores/token.js';
|
||||
|
||||
import iconConstants from '@/consts/icon.js';
|
||||
import { parseDeviceInfo, parseUserAgent } from '@/lib/misc.js';
|
||||
|
||||
import { onSwipeoutDeleted } from '@/lib/ui.mobile.js';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'f7router'
|
||||
@@ -55,6 +64,7 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useUserStore, useTokensStore),
|
||||
sessions() {
|
||||
if (!this.tokens) {
|
||||
return this.tokens;
|
||||
@@ -70,9 +80,9 @@ export default {
|
||||
domId: this.getTokenDomId(token.tokenId),
|
||||
isCurrent: token.isCurrent,
|
||||
deviceType: this.$t(token.isCurrent ? 'Current' : 'Other Device'),
|
||||
deviceInfo: this.$utilities.parseDeviceInfo(token.userAgent),
|
||||
deviceInfo: parseDeviceInfo(token.userAgent),
|
||||
icon: this.getTokenIcon(token),
|
||||
createdAt: this.$utilities.formatUnixTime(token.createdAt, this.$locale.getLongDateTimeFormat())
|
||||
createdAt: this.$locale.formatUnixTimeToLongDateTime(this.userStore, token.createdAt)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -84,7 +94,7 @@ export default {
|
||||
|
||||
self.loading = true;
|
||||
|
||||
self.$store.dispatch('getAllTokens').then(tokens => {
|
||||
self.tokensStore.getAllTokens().then(tokens => {
|
||||
self.tokens = tokens;
|
||||
self.loading = false;
|
||||
}).catch(error => {
|
||||
@@ -103,7 +113,7 @@ export default {
|
||||
reload(done) {
|
||||
const self = this;
|
||||
|
||||
self.$store.dispatch('getAllTokens').then(tokens => {
|
||||
self.tokensStore.getAllTokens().then(tokens => {
|
||||
if (done) {
|
||||
done();
|
||||
}
|
||||
@@ -125,12 +135,12 @@ export default {
|
||||
self.$confirm('Are you sure you want to logout from this session?', () => {
|
||||
self.$showLoading();
|
||||
|
||||
self.$store.dispatch('revokeToken', {
|
||||
self.tokensStore.revokeToken({
|
||||
tokenId: session.tokenId
|
||||
}).then(() => {
|
||||
self.$hideLoading();
|
||||
|
||||
self.$ui.onSwipeoutDeleted(self.getTokenDomId(session.tokenId), () => {
|
||||
onSwipeoutDeleted(self.getTokenDomId(session.tokenId), () => {
|
||||
for (let i = 0; i < self.tokens.length; i++) {
|
||||
if (self.tokens[i].tokenId === session.tokenId) {
|
||||
self.tokens.splice(i, 1);
|
||||
@@ -156,7 +166,7 @@ export default {
|
||||
self.$confirm('Are you sure you want to logout all other sessions?', () => {
|
||||
self.$showLoading();
|
||||
|
||||
self.$store.dispatch('revokeAllTokens').then(() => {
|
||||
self.tokensStore.revokeAllTokens().then(() => {
|
||||
self.$hideLoading();
|
||||
|
||||
for (let i = self.tokens.length - 1; i >= 0; i--) {
|
||||
@@ -176,22 +186,22 @@ export default {
|
||||
});
|
||||
},
|
||||
getTokenIcon(token) {
|
||||
const ua = this.$utilities.parseUserAgent(token.userAgent);
|
||||
const ua = parseUserAgent(token.userAgent);
|
||||
|
||||
if (!ua || !ua.device) {
|
||||
return this.$constants.icons.deviceIcons.desktop.f7Icon;
|
||||
return iconConstants.deviceIcons.desktop.f7Icon;
|
||||
}
|
||||
|
||||
if (ua.device.type === 'mobile') {
|
||||
return this.$constants.icons.deviceIcons.mobile.f7Icon;
|
||||
return iconConstants.deviceIcons.mobile.f7Icon;
|
||||
} else if (ua.device.type === 'wearable') {
|
||||
return this.$constants.icons.deviceIcons.wearable.f7Icon;
|
||||
return iconConstants.deviceIcons.wearable.f7Icon;
|
||||
} else if (ua.device.type === 'tablet') {
|
||||
return this.$constants.icons.deviceIcons.tablet.f7Icon;
|
||||
return iconConstants.deviceIcons.tablet.f7Icon;
|
||||
} else if (ua.device.type === 'smarttv') {
|
||||
return this.$constants.icons.deviceIcons.tv.f7Icon;
|
||||
return iconConstants.deviceIcons.tv.f7Icon;
|
||||
} else {
|
||||
return this.$constants.icons.deviceIcons.desktop.f7Icon;
|
||||
return iconConstants.deviceIcons.desktop.f7Icon;
|
||||
}
|
||||
},
|
||||
getTokenDomId(tokenId) {
|
||||
|
||||
@@ -57,6 +57,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useTwoFactorAuthStore } from '@/stores/twoFactorAuth.js';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'f7router'
|
||||
@@ -82,12 +85,15 @@ export default {
|
||||
showBackupCodeSheet: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useTwoFactorAuthStore),
|
||||
},
|
||||
created() {
|
||||
const self = this;
|
||||
|
||||
self.loading = true;
|
||||
|
||||
self.$store.dispatch('get2FAStatus').then(response => {
|
||||
self.twoFactorAuthStore.get2FAStatus().then(response => {
|
||||
self.status = response.enable;
|
||||
self.loading = false;
|
||||
}).catch(error => {
|
||||
@@ -112,7 +118,7 @@ export default {
|
||||
self.enabling = true;
|
||||
self.$showLoading(() => self.enabling);
|
||||
|
||||
self.$store.dispatch('enable2FA').then(response => {
|
||||
self.twoFactorAuthStore.enable2FA().then(response => {
|
||||
self.enabling = false;
|
||||
self.$hideLoading();
|
||||
|
||||
@@ -135,7 +141,7 @@ export default {
|
||||
self.enableConfirming = true;
|
||||
self.$showLoading(() => self.enableConfirming);
|
||||
|
||||
self.$store.dispatch('confirmEnable2FA', {
|
||||
self.twoFactorAuthStore.confirmEnable2FA({
|
||||
secret: self.new2FASecret,
|
||||
passcode: self.currentPasscodeForEnable
|
||||
}).then(response => {
|
||||
@@ -173,7 +179,7 @@ export default {
|
||||
self.disabling = true;
|
||||
self.$showLoading(() => self.disabling);
|
||||
|
||||
self.$store.dispatch('disable2FA', {
|
||||
self.twoFactorAuthStore.disable2FA({
|
||||
password: password
|
||||
}).then(() => {
|
||||
self.disabling = false;
|
||||
@@ -203,7 +209,7 @@ export default {
|
||||
self.regenerating = true;
|
||||
self.$showLoading(() => self.regenerating);
|
||||
|
||||
self.$store.dispatch('regenerate2FARecoveryCode', {
|
||||
self.twoFactorAuthStore.regenerate2FARecoveryCode({
|
||||
password: password
|
||||
}).then(response => {
|
||||
self.regenerating = false;
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
link="#" no-chevron
|
||||
:class="{ 'disabled': !allVisibleAccounts.length }"
|
||||
:header="$t('Default Account')"
|
||||
:title="$utilities.getNameByKeyValue(allAccounts, newProfile.defaultAccountId, 'id', 'name', $t('Not Specified'))"
|
||||
:title="getNameByKeyValue(allAccounts, newProfile.defaultAccountId, 'id', 'name', $t('Not Specified'))"
|
||||
@click="showAccountSheet = true"
|
||||
>
|
||||
<two-column-list-item-selection-sheet primary-key-field="id" primary-value-field="category"
|
||||
@@ -96,7 +96,7 @@
|
||||
<f7-list-item
|
||||
class="list-item-with-header-and-title list-item-no-item-after"
|
||||
:header="$t('Editable Transaction Scope')"
|
||||
:title="$t($utilities.getNameByKeyValue(allTransactionEditScopeTypes, newProfile.transactionEditScope, 'value', 'name'))"
|
||||
:title="$t(getNameByKeyValue(allTransactionEditScopeTypes, newProfile.transactionEditScope, 'value', 'name'))"
|
||||
smart-select :smart-select-params="{ openIn: 'popup', popupPush: true, closeOnSelect: true, scrollToSelectedItem: true, searchbar: true, searchbarPlaceholder: $t('Date Range'), searchbarDisableText: $t('Cancel'), appendSearchbarNotFound: $t('No results'), pageTitle: $t('Editable Transaction Scope'), popupCloseLinkText: $t('Done') }"
|
||||
>
|
||||
<select v-model="newProfile.transactionEditScope">
|
||||
@@ -156,7 +156,7 @@
|
||||
<f7-list-item
|
||||
class="list-item-with-header-and-title list-item-no-item-after"
|
||||
:header="$t('Long Date Format')"
|
||||
:title="$utilities.getNameByKeyValue(allLongDateFormats, newProfile.longDateFormat, 'type', 'displayName')"
|
||||
:title="getNameByKeyValue(allLongDateFormats, newProfile.longDateFormat, 'type', 'displayName')"
|
||||
smart-select :smart-select-params="{ openIn: 'popup', popupPush: true, closeOnSelect: true, scrollToSelectedItem: true, searchbar: true, searchbarPlaceholder: $t('Long Date Format'), searchbarDisableText: $t('Cancel'), appendSearchbarNotFound: $t('No results'), pageTitle: $t('Long Date Format'), popupCloseLinkText: $t('Done') }"
|
||||
>
|
||||
<select v-model="newProfile.longDateFormat">
|
||||
@@ -169,7 +169,7 @@
|
||||
<f7-list-item
|
||||
class="list-item-with-header-and-title list-item-no-item-after"
|
||||
:header="$t('Short Date Format')"
|
||||
:title="$utilities.getNameByKeyValue(allShortDateFormats, newProfile.shortDateFormat, 'type', 'displayName')"
|
||||
:title="getNameByKeyValue(allShortDateFormats, newProfile.shortDateFormat, 'type', 'displayName')"
|
||||
smart-select :smart-select-params="{ openIn: 'popup', popupPush: true, closeOnSelect: true, scrollToSelectedItem: true, searchbar: true, searchbarPlaceholder: $t('Short Date Format'), searchbarDisableText: $t('Cancel'), appendSearchbarNotFound: $t('No results'), pageTitle: $t('Short Date Format'), popupCloseLinkText: $t('Done') }"
|
||||
>
|
||||
<select v-model="newProfile.shortDateFormat">
|
||||
@@ -182,7 +182,7 @@
|
||||
<f7-list-item
|
||||
class="list-item-with-header-and-title list-item-no-item-after"
|
||||
:header="$t('Long Time Format')"
|
||||
:title="$utilities.getNameByKeyValue(allLongTimeFormats, newProfile.longTimeFormat, 'type', 'displayName')"
|
||||
:title="getNameByKeyValue(allLongTimeFormats, newProfile.longTimeFormat, 'type', 'displayName')"
|
||||
smart-select :smart-select-params="{ openIn: 'popup', popupPush: true, closeOnSelect: true, scrollToSelectedItem: true, searchbar: true, searchbarPlaceholder: $t('Long Time Format'), searchbarDisableText: $t('Cancel'), appendSearchbarNotFound: $t('No results'), pageTitle: $t('Long Time Format'), popupCloseLinkText: $t('Done') }"
|
||||
>
|
||||
<select v-model="newProfile.longTimeFormat">
|
||||
@@ -195,7 +195,7 @@
|
||||
<f7-list-item
|
||||
class="list-item-with-header-and-title list-item-no-item-after"
|
||||
:header="$t('Short Time Format')"
|
||||
:title="$utilities.getNameByKeyValue(allShortTimeFormats, newProfile.shortTimeFormat, 'type', 'displayName')"
|
||||
:title="getNameByKeyValue(allShortTimeFormats, newProfile.shortTimeFormat, 'type', 'displayName')"
|
||||
smart-select :smart-select-params="{ openIn: 'popup', popupPush: true, closeOnSelect: true, scrollToSelectedItem: true, searchbar: true, searchbarPlaceholder: $t('Long Time Format'), searchbarDisableText: $t('Cancel'), appendSearchbarNotFound: $t('No results'), pageTitle: $t('Short Time Format'), popupCloseLinkText: $t('Done') }"
|
||||
>
|
||||
<select v-model="newProfile.shortTimeFormat">
|
||||
@@ -220,6 +220,15 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useRootStore } from '@/stores/index.js';
|
||||
import { useUserStore } from '@/stores/user.js';
|
||||
import { useAccountsStore } from '@/stores/account.js';
|
||||
|
||||
import datetimeConstants from '@/consts/datetime.js';
|
||||
import { getNameByKeyValue } from '@/lib/common.js';
|
||||
import { getCategorizedAccounts } from '@/lib/account.js';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'f7router'
|
||||
@@ -263,6 +272,7 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useRootStore, useUserStore, useAccountsStore),
|
||||
allLanguages() {
|
||||
const ret = [];
|
||||
const allLanguageInfo = this.$locale.getAllLanguageInfos();
|
||||
@@ -291,16 +301,16 @@ export default {
|
||||
return this.$locale.getAllCurrencies();
|
||||
},
|
||||
allAccounts() {
|
||||
return this.$store.getters.allPlainAccounts;
|
||||
return this.accountsStore.allPlainAccounts;
|
||||
},
|
||||
allVisibleAccounts() {
|
||||
return this.$store.getters.allVisiblePlainAccounts;
|
||||
return this.accountsStore.allVisiblePlainAccounts;
|
||||
},
|
||||
allCategorizedAccounts() {
|
||||
return this.$utilities.getCategorizedAccounts(this.allVisibleAccounts);
|
||||
return getCategorizedAccounts(this.allVisibleAccounts);
|
||||
},
|
||||
allWeekDays() {
|
||||
return this.$constants.datetime.allWeekDays;
|
||||
return datetimeConstants.allWeekDays;
|
||||
},
|
||||
allLongDateFormats() {
|
||||
return this.$locale.getAllLongDateFormats();
|
||||
@@ -348,7 +358,7 @@ export default {
|
||||
return this.$t('Unknown');
|
||||
},
|
||||
currentDayOfWeekName() {
|
||||
const weekName = this.$utilities.getNameByKeyValue(this.$constants.datetime.allWeekDays, this.newProfile.firstDayOfWeek, 'type', 'name');
|
||||
const weekName = getNameByKeyValue(datetimeConstants.allWeekDays, this.newProfile.firstDayOfWeek, 'type', 'name');
|
||||
const i18nWeekNameKey = `datetime.${weekName}.long`;
|
||||
return this.$t(i18nWeekNameKey);
|
||||
},
|
||||
@@ -418,8 +428,8 @@ export default {
|
||||
self.loading = true;
|
||||
|
||||
const promises = [
|
||||
self.$store.dispatch('loadAllAccounts', { force: false }),
|
||||
self.$store.dispatch('getCurrentUserProfile')
|
||||
self.accountsStore.loadAllAccounts({ force: false }),
|
||||
self.userStore.getCurrentUserProfile()
|
||||
];
|
||||
|
||||
Promise.all(promises).then(responses => {
|
||||
@@ -460,7 +470,7 @@ export default {
|
||||
self.saving = true;
|
||||
self.$showLoading(() => self.saving);
|
||||
|
||||
self.$store.dispatch('updateUserProfile', {
|
||||
self.rootStore.updateUserProfile({
|
||||
profile: self.newProfile,
|
||||
currentPassword: self.currentPassword
|
||||
}).then(response => {
|
||||
@@ -485,6 +495,9 @@ export default {
|
||||
}
|
||||
});
|
||||
},
|
||||
getNameByKeyValue(src, value, keyField, nameField, defaultName) {
|
||||
return getNameByKeyValue(src, value, keyField, nameField, defaultName);
|
||||
},
|
||||
setCurrentUserProfile(profile) {
|
||||
this.oldProfile.email = profile.email;
|
||||
this.oldProfile.nickname = profile.nickname;
|
||||
|
||||
@@ -88,10 +88,10 @@
|
||||
"licenseUrl": "https://github.com/vuejs/core/blob/v3.3.4/LICENSE"
|
||||
},
|
||||
{
|
||||
"name": "vuex",
|
||||
"copyright": "Copyright (c) 2015-present Evan You",
|
||||
"url": "https://github.com/vuejs/vuex",
|
||||
"licenseUrl": "https://github.com/vuejs/vuex/blob/v4.1.0/LICENSE"
|
||||
"name": "Pinia",
|
||||
"copyright": "Copyright (c) 2019-present Eduardo San Martin Morote",
|
||||
"url": "https://github.com/vuejs/pinia",
|
||||
"licenseUrl": "https://github.com/vuejs/pinia/blob/pinia%402.1.3/LICENSE"
|
||||
},
|
||||
{
|
||||
"name": "vue-i18n",
|
||||
|
||||
Reference in New Issue
Block a user