use pinia to replace vuex, code refactor

This commit is contained in:
MaysWind
2023-06-10 23:13:31 +08:00
parent 0d84f2857f
commit 46d85e92cd
80 changed files with 4972 additions and 4859 deletions
+9 -3
View File
@@ -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();
}
}
}
+14 -5
View File
@@ -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');
+29 -31
View File
@@ -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) {
+50 -29
View File
@@ -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
+12 -5
View File
@@ -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;
+19 -8
View File
@@ -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();
+29 -17
View File
@@ -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');
+24 -16
View File
@@ -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();
+66 -50
View File
@@ -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;
+50 -267
View File
@@ -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();
+8 -2
View File
@@ -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) {
+17 -9
View File
@@ -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;
+25 -18
View File
@@ -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();
+22 -15
View File
@@ -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
+14 -8
View File
@@ -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);
}
}
+84 -53
View File
@@ -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, '&lt;0.01') }}</small>
<small class="statistics-percent" v-if="item.percent >= 0">{{ getDisplayPercent(item.percent, 2, '&lt;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>&nbsp;-&nbsp;</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 = [];
+16 -9
View File
@@ -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();
+179 -106
View File
@@ -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');
}
}
};
+116 -75
View File
@@ -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>&nbsp;-&nbsp;</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;
},
+30 -9
View File
@@ -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 => {
+24 -14
View File
@@ -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) {
+11 -5
View File
@@ -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;
+27 -14
View File
@@ -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;