add parts of statistics ui
This commit is contained in:
Generated
+47
-2
@@ -4623,6 +4623,22 @@
|
||||
"safer-buffer": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"echarts": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/echarts/-/echarts-5.0.1.tgz",
|
||||
"integrity": "sha512-JYn22Dolt2esY2jEzUsw1OxbobuW67oGjIoTjZO3rW89SWkfJ4kbrmC2OW9JjsBrD1rdkmaWBuZZ2HgmThyxJw==",
|
||||
"requires": {
|
||||
"tslib": "2.0.3",
|
||||
"zrender": "5.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
|
||||
"integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
@@ -7058,8 +7074,7 @@
|
||||
"lodash": {
|
||||
"version": "4.17.20",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
|
||||
},
|
||||
"lodash.defaultsdeep": {
|
||||
"version": "4.6.1",
|
||||
@@ -9346,6 +9361,11 @@
|
||||
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
|
||||
"dev": true
|
||||
},
|
||||
"resize-detector": {
|
||||
"version": "0.1.10",
|
||||
"resolved": "https://registry.npmjs.org/resize-detector/-/resize-detector-0.1.10.tgz",
|
||||
"integrity": "sha512-iLcXC8A6Fb0DfA+TRiywrK/0A22bFqkhntjMJMEzXDA4XkcEkfwpNbv7W8iewUiD0xYIaeiXOfiEehTqGKsUFw=="
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.17.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
|
||||
@@ -11081,6 +11101,16 @@
|
||||
"clipboard": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"vue-echarts": {
|
||||
"version": "5.0.0-beta.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-echarts/-/vue-echarts-5.0.0-beta.0.tgz",
|
||||
"integrity": "sha512-QZFKGXDAYFQo+F20REpzcdLx79nsl4kOorJRpN+08aYq4YiIlmtWss1Lxadm7Fo+NYyWm8nnT+h4xHv3uqWIDQ==",
|
||||
"requires": {
|
||||
"core-js": "^3.4.4",
|
||||
"lodash": "^4.17.15",
|
||||
"resize-detector": "^0.1.10"
|
||||
}
|
||||
},
|
||||
"vue-eslint-parser": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-7.1.1.tgz",
|
||||
@@ -12144,6 +12174,21 @@
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"zrender": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/zrender/-/zrender-5.0.3.tgz",
|
||||
"integrity": "sha512-TVcN2IMdo7je3GEq/E4CER4AGBe/n50/izILdupppyHf/hVHuiXCRliqdu8+32Z1OmGg6RfKt5qQlkX+bOtU0g==",
|
||||
"requires": {
|
||||
"tslib": "2.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
|
||||
"integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"cbor-js": "^0.1.0",
|
||||
"core-js": "^3.6.5",
|
||||
"crypto-js": "^4.0.0",
|
||||
"echarts": "^5.0.1",
|
||||
"framework7": "^5.7.14",
|
||||
"framework7-icons": "^3.0.1",
|
||||
"framework7-vue": "^5.7.14",
|
||||
@@ -21,6 +22,7 @@
|
||||
"ua-parser-js": "^0.7.22",
|
||||
"vue": "^2.6.12",
|
||||
"vue-clipboard2": "^0.3.1",
|
||||
"vue-echarts": "^5.0.0-beta.0",
|
||||
"vue-i18n": "^8.22.1",
|
||||
"vue-i18n-filter": "^0.1.6",
|
||||
"vue-moment": "^4.1.0",
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
const allChartTypes = {
|
||||
Pie: 0,
|
||||
Bar: 1
|
||||
};
|
||||
|
||||
const defaultChartType = allChartTypes.Pie;
|
||||
|
||||
const allChartLegendTypes = {
|
||||
Account: 0,
|
||||
PrimaryCategory: 1,
|
||||
SecondaryCategory: 1
|
||||
};
|
||||
|
||||
const defaultChartLegendType = allChartLegendTypes.SecondaryCategory;
|
||||
|
||||
export default {
|
||||
allChartTypes: allChartTypes,
|
||||
defaultChartType: defaultChartType,
|
||||
allChartLegendTypes: allChartLegendTypes,
|
||||
defaultChartLegendType: defaultChartLegendType,
|
||||
};
|
||||
@@ -185,6 +185,19 @@ export default {
|
||||
|
||||
return axios.get('v1/overviews/transaction.json' + (queryParams.length ? '?query=' + queryParams.join('|') : ''));
|
||||
},
|
||||
getTransactionStatistics: ({ startTime, endTime }) => {
|
||||
const queryParams = [];
|
||||
|
||||
if (startTime) {
|
||||
queryParams.push(`start_time=${startTime}`);
|
||||
}
|
||||
|
||||
if (endTime) {
|
||||
queryParams.push(`end_time=${endTime}`);
|
||||
}
|
||||
|
||||
return axios.get('v1/statistics/transaction.json' + (queryParams.length ? '?' + queryParams.join('&') : ''));
|
||||
},
|
||||
getAllAccounts: ({ visibleOnly }) => {
|
||||
return axios.get('v1/accounts/list.json?visible_only=' + !!visibleOnly);
|
||||
},
|
||||
|
||||
@@ -4,6 +4,7 @@ import VueI18n from 'vue-i18n';
|
||||
import VueI18nFilter from 'vue-i18n-filter';
|
||||
import Framework7 from 'framework7/framework7.esm.bundle.js';
|
||||
import Framework7Vue from 'framework7-vue/framework7-vue.esm.bundle.js';
|
||||
import ECharts from 'vue-echarts';
|
||||
import PincodeInput from 'vue-pincode-input';
|
||||
import VueMoment from 'vue-moment';
|
||||
import VueClipboard from 'vue-clipboard2';
|
||||
@@ -15,6 +16,13 @@ import 'framework7-icons';
|
||||
|
||||
import 'line-awesome/dist/line-awesome/css/line-awesome.css';
|
||||
|
||||
import 'echarts/lib/chart/line';
|
||||
import 'echarts/lib/chart/pie';
|
||||
import 'echarts/lib/chart/bar';
|
||||
import 'echarts/lib/component/legend';
|
||||
import 'echarts/lib/component/title';
|
||||
import 'echarts/lib/component/tooltip';
|
||||
|
||||
import { getAllLanguages, getLanguage, getDefaultLanguage, getI18nOptions, getLocalizedError, getLocalizedErrorParameters } from './lib/i18n.js';
|
||||
import api from './consts/api.js';
|
||||
import datetime from './consts/datetime.js';
|
||||
@@ -24,6 +32,7 @@ import icons from './consts/icon.js';
|
||||
import account from './consts/account.js';
|
||||
import transaction from './consts/transaction.js';
|
||||
import category from './consts/category.js';
|
||||
import statistics from './consts/statistics.js';
|
||||
import licenses from './lib/licenses.js';
|
||||
import version from './lib/version.js';
|
||||
import logger from './lib/logger.js';
|
||||
@@ -62,6 +71,7 @@ Vue.use(VueI18n);
|
||||
Vue.use(VueI18nFilter);
|
||||
Vue.use(VueMoment, { moment });
|
||||
Vue.use(VueClipboard);
|
||||
Vue.component('v-chart', ECharts);
|
||||
Vue.component('PincodeInput', PincodeInput);
|
||||
Vue.component('PasswordInputSheet', PasswordInputSheet);
|
||||
Vue.component('PasscodeInputSheet', PasscodeInputSheet);
|
||||
@@ -92,6 +102,7 @@ Vue.prototype.$constants = {
|
||||
account: account,
|
||||
transaction: transaction,
|
||||
category: category,
|
||||
statistics: statistics,
|
||||
};
|
||||
|
||||
Vue.prototype.$utilities = utils;
|
||||
|
||||
@@ -11,7 +11,7 @@ import TransactionEditPage from '../views/mobile/transactions/Edit.vue';
|
||||
import AccountListPage from '../views/mobile/accounts/List.vue';
|
||||
import AccountEditPage from '../views/mobile/accounts/Edit.vue';
|
||||
|
||||
import StatisticsOverviewPage from '../views/mobile/statistics/Overview.vue';
|
||||
import StatisticsTransactionPage from '../views/mobile/statistics/Transaction.vue';
|
||||
|
||||
import SettingsPage from '../views/mobile/Settings.vue';
|
||||
import ApplicationLockPage from '../views/mobile/ApplicationLock.vue';
|
||||
@@ -171,8 +171,8 @@ const routes = [
|
||||
beforeEnter: checkLogin
|
||||
},
|
||||
{
|
||||
path: '/statistic/overview',
|
||||
component: StatisticsOverviewPage,
|
||||
path: '/statistic/transaction',
|
||||
component: StatisticsTransactionPage,
|
||||
beforeEnter: checkLogin
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import datetimeConstants from "../consts/datetime.js";
|
||||
import statisticsConstants from "../consts/statistics.js";
|
||||
import userState from "../lib/userstate.js";
|
||||
import utils from "../lib/utils.js";
|
||||
|
||||
@@ -44,6 +45,11 @@ import {
|
||||
|
||||
LOAD_TRANSACTION_OVERVIEW,
|
||||
UPDATE_TRANSACTION_OVERVIEW_INVALID_STATE,
|
||||
|
||||
LOAD_TRANSACTION_STATISTICS,
|
||||
INIT_TRANSACTION_STATISTICS_FILTER,
|
||||
UPDATE_TRANSACTION_STATISTICS_FILTER,
|
||||
UPDATE_TRANSACTION_STATISTICS_INVALID_STATE,
|
||||
} from './mutations.js';
|
||||
|
||||
import {
|
||||
@@ -88,6 +94,12 @@ import {
|
||||
loadTransactionOverview
|
||||
} from './overview.js';
|
||||
|
||||
import {
|
||||
loadTransactionStatistics,
|
||||
initTransactionStatisticsFilter,
|
||||
updateTransactionStatisticsFilter
|
||||
} from './statistics.js';
|
||||
|
||||
import {
|
||||
loadAllAccounts,
|
||||
getAccount,
|
||||
@@ -165,6 +177,15 @@ const stores = {
|
||||
transactionTagListStateInvalid: true,
|
||||
transactionOverview: {},
|
||||
transactionOverviewStateInvalid: true,
|
||||
transactionStatisticsFilter: {
|
||||
dateType: datetimeConstants.allDateRanges.ThisMonth.type,
|
||||
startTime: 0,
|
||||
endTime: 0,
|
||||
chartType: statisticsConstants.defaultChartType,
|
||||
chartLegendType: statisticsConstants.defaultChartLegendType,
|
||||
},
|
||||
transactionStatistics: [],
|
||||
transactionStatisticsStateInvalid: true,
|
||||
},
|
||||
getters: {
|
||||
// user
|
||||
@@ -216,6 +237,14 @@ const stores = {
|
||||
state.transactionOverview = {};
|
||||
state.transactionOverviewStateInvalid = true;
|
||||
|
||||
state.transactionStatisticsFilter.dateType = datetimeConstants.allDateRanges.ThisMonth.type;
|
||||
state.transactionStatisticsFilter.startTime = 0;
|
||||
state.transactionStatisticsFilter.endTime = 0;
|
||||
state.transactionStatisticsFilter.chartType = statisticsConstants.defaultChartType;
|
||||
state.transactionStatisticsFilter.chartLegendType = statisticsConstants.defaultChartLegendType;
|
||||
state.transactionStatistics = {};
|
||||
state.transactionStatisticsStateInvalid = true;
|
||||
|
||||
clearExchangeRatesFromLocalStorage();
|
||||
},
|
||||
[STORE_USER_INFO] (state, userInfo) {
|
||||
@@ -731,6 +760,86 @@ const stores = {
|
||||
[UPDATE_TRANSACTION_OVERVIEW_INVALID_STATE] (state, invalidState) {
|
||||
state.transactionOverviewStateInvalid = invalidState;
|
||||
},
|
||||
[LOAD_TRANSACTION_STATISTICS] (state, { statistics, defaultCurrency }) {
|
||||
if (statistics && statistics.items && statistics.items.length) {
|
||||
for (let i = 0; i < statistics.items.length; i++) {
|
||||
const item = statistics.items[i];
|
||||
|
||||
if (item.accountId) {
|
||||
item.account = state.allAccountsMap[item.accountId];
|
||||
}
|
||||
|
||||
if (item.categoryId) {
|
||||
item.category = state.allTransactionCategoriesMap[item.categoryId];
|
||||
}
|
||||
|
||||
if (item.account && item.account.currency !== defaultCurrency) {
|
||||
item.amountInDefaultCurrency = getExchangedAmount(state)(item.amount, item.account.currency, defaultCurrency);
|
||||
} else if (item.account && item.account.currency === defaultCurrency) {
|
||||
item.amountInDefaultCurrency = item.amount;
|
||||
} else {
|
||||
item.amountInDefaultCurrency = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state.transactionStatistics = statistics;
|
||||
},
|
||||
[INIT_TRANSACTION_STATISTICS_FILTER] (state, filter) {
|
||||
if (filter && utils.isNumber(filter.dateType)) {
|
||||
state.transactionStatisticsFilter.dateType = filter.dateType;
|
||||
} else {
|
||||
state.transactionStatisticsFilter.dateType = datetimeConstants.allDateRanges.ThisMonth.type;
|
||||
}
|
||||
|
||||
if (filter && utils.isNumber(filter.startTime)) {
|
||||
state.transactionStatisticsFilter.startTime = filter.startTime;
|
||||
} else {
|
||||
state.transactionStatisticsFilter.startTime = 0;
|
||||
}
|
||||
|
||||
if (filter && utils.isNumber(filter.endTime)) {
|
||||
state.transactionStatisticsFilter.endTime = filter.endTime;
|
||||
} else {
|
||||
state.transactionStatisticsFilter.endTime = 0;
|
||||
}
|
||||
|
||||
if (filter && utils.isNumber(filter.chartType)) {
|
||||
state.transactionStatisticsFilter.chartType = filter.chartType;
|
||||
} else {
|
||||
state.transactionStatisticsFilter.chartType = statisticsConstants.defaultChartType;
|
||||
}
|
||||
|
||||
if (filter && utils.isNumber(filter.chartLegendType)) {
|
||||
state.transactionStatisticsFilter.chartLegendType = filter.chartLegendType;
|
||||
} else {
|
||||
state.transactionStatisticsFilter.chartLegendType = statisticsConstants.defaultChartLegendType;
|
||||
}
|
||||
},
|
||||
[UPDATE_TRANSACTION_STATISTICS_FILTER] (state, filter) {
|
||||
if (filter && utils.isNumber(filter.dateType)) {
|
||||
state.transactionStatisticsFilter.dateType = filter.dateType;
|
||||
}
|
||||
|
||||
if (filter && utils.isNumber(filter.startTime)) {
|
||||
state.transactionStatisticsFilter.startTime = filter.startTime;
|
||||
}
|
||||
|
||||
if (filter && utils.isNumber(filter.endTime)) {
|
||||
state.transactionStatisticsFilter.endTime = filter.endTime;
|
||||
}
|
||||
|
||||
if (filter && utils.isNumber(filter.chartType)) {
|
||||
state.transactionStatisticsFilter.chartType = filter.chartType;
|
||||
}
|
||||
|
||||
if (filter && utils.isNumber(filter.chartLegendType)) {
|
||||
state.transactionStatisticsFilter.chartLegendType = filter.chartLegendType;
|
||||
}
|
||||
},
|
||||
[UPDATE_TRANSACTION_STATISTICS_INVALID_STATE] (state, invalidState) {
|
||||
state.transactionStatisticsStateInvalid = invalidState;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
// user
|
||||
@@ -763,6 +872,11 @@ const stores = {
|
||||
// overview
|
||||
loadTransactionOverview,
|
||||
|
||||
// statistics
|
||||
loadTransactionStatistics,
|
||||
initTransactionStatisticsFilter,
|
||||
updateTransactionStatisticsFilter,
|
||||
|
||||
// account
|
||||
loadAllAccounts,
|
||||
saveAccount,
|
||||
|
||||
@@ -39,3 +39,8 @@ export const UPDATE_TRANSACTION_TAG_LIST_INVALID_STATE = 'UPDATE_TRANSACTION_TAG
|
||||
|
||||
export const LOAD_TRANSACTION_OVERVIEW = 'LOAD_TRANSACTION_OVERVIEW';
|
||||
export const UPDATE_TRANSACTION_OVERVIEW_INVALID_STATE = 'UPDATE_TRANSACTION_OVERVIEW_INVALID_STATE';
|
||||
|
||||
export const LOAD_TRANSACTION_STATISTICS = 'LOAD_TRANSACTION_STATISTICS';
|
||||
export const INIT_TRANSACTION_STATISTICS_FILTER = 'INIT_TRANSACTION_STATISTICS_FILTER';
|
||||
export const UPDATE_TRANSACTION_STATISTICS_FILTER = 'UPDATE_TRANSACTION_STATISTICS_FILTER';
|
||||
export const UPDATE_TRANSACTION_STATISTICS_INVALID_STATE = 'UPDATE_TRANSACTION_STATISTICS_INVALID_STATE';
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import services from '../lib/services.js';
|
||||
import logger from '../lib/logger.js';
|
||||
|
||||
import {
|
||||
LOAD_TRANSACTION_STATISTICS,
|
||||
INIT_TRANSACTION_STATISTICS_FILTER,
|
||||
UPDATE_TRANSACTION_STATISTICS_FILTER,
|
||||
UPDATE_TRANSACTION_STATISTICS_INVALID_STATE
|
||||
} from "./mutations.js";
|
||||
|
||||
export function loadTransactionStatistics(context, { defaultCurrency }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getTransactionStatistics({
|
||||
startTime: context.state.transactionStatisticsFilter.startTime,
|
||||
endTime: context.state.transactionStatisticsFilter.endTime
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to get transaction statistics' });
|
||||
return;
|
||||
}
|
||||
|
||||
context.commit(LOAD_TRANSACTION_STATISTICS, {
|
||||
statistics: data.result,
|
||||
defaultCurrency: defaultCurrency
|
||||
});
|
||||
|
||||
if (context.state.transactionStatisticsStateInvalid) {
|
||||
context.commit(UPDATE_TRANSACTION_STATISTICS_INVALID_STATE, false);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to get transaction statistics', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to get transaction statistics' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function initTransactionStatisticsFilter(context, filter) {
|
||||
context.commit(INIT_TRANSACTION_STATISTICS_FILTER, filter);
|
||||
}
|
||||
|
||||
export function updateTransactionStatisticsFilter(context, filter) {
|
||||
context.commit(UPDATE_TRANSACTION_STATISTICS_FILTER, filter);
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
UPDATE_TRANSACTION_LIST_INVALID_STATE,
|
||||
UPDATE_ACCOUNT_LIST_INVALID_STATE,
|
||||
UPDATE_TRANSACTION_OVERVIEW_INVALID_STATE,
|
||||
UPDATE_TRANSACTION_STATISTICS_INVALID_STATE,
|
||||
} from './mutations.js';
|
||||
|
||||
const emptyTransactionResult = {
|
||||
@@ -177,6 +178,10 @@ export function saveTransaction(context, { transaction, defaultCurrency }) {
|
||||
context.commit(UPDATE_TRANSACTION_OVERVIEW_INVALID_STATE, true);
|
||||
}
|
||||
|
||||
if (!context.state.transactionStatisticsStateInvalid) {
|
||||
context.commit(UPDATE_TRANSACTION_STATISTICS_INVALID_STATE, true);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to save transaction', error);
|
||||
@@ -230,6 +235,10 @@ export function deleteTransaction(context, { transaction, defaultCurrency, befor
|
||||
context.commit(UPDATE_TRANSACTION_OVERVIEW_INVALID_STATE, true);
|
||||
}
|
||||
|
||||
if (!context.state.transactionStatisticsStateInvalid) {
|
||||
context.commit(UPDATE_TRANSACTION_STATISTICS_INVALID_STATE, true);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to delete transaction', error);
|
||||
|
||||
+10
-1
@@ -13,7 +13,8 @@ import {
|
||||
UPDATE_ACCOUNT_LIST_INVALID_STATE,
|
||||
UPDATE_TRANSACTION_CATEGORY_LIST_INVALID_STATE,
|
||||
UPDATE_TRANSACTION_TAG_LIST_INVALID_STATE,
|
||||
UPDATE_TRANSACTION_OVERVIEW_INVALID_STATE
|
||||
UPDATE_TRANSACTION_OVERVIEW_INVALID_STATE,
|
||||
UPDATE_TRANSACTION_STATISTICS_INVALID_STATE
|
||||
} from './mutations.js';
|
||||
|
||||
export function authorize(context, { loginName, password }) {
|
||||
@@ -258,6 +259,10 @@ export function updateUserProfile(context, { profile, currentPassword }) {
|
||||
context.commit(UPDATE_TRANSACTION_OVERVIEW_INVALID_STATE, true);
|
||||
}
|
||||
|
||||
if (!context.state.transactionStatisticsStateInvalid) {
|
||||
context.commit(UPDATE_TRANSACTION_STATISTICS_INVALID_STATE, true);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to save user profile', error);
|
||||
@@ -301,6 +306,10 @@ export function clearUserData(context, { password }) {
|
||||
context.commit(UPDATE_TRANSACTION_OVERVIEW_INVALID_STATE, true);
|
||||
}
|
||||
|
||||
if (!context.state.transactionStatisticsStateInvalid) {
|
||||
context.commit(UPDATE_TRANSACTION_STATISTICS_INVALID_STATE, true);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to clear user data', error);
|
||||
|
||||
@@ -155,7 +155,7 @@
|
||||
<f7-link href="/transaction/add">
|
||||
<f7-icon f7="plus_square" class="lab-tarbar-big-icon"></f7-icon>
|
||||
</f7-link>
|
||||
<f7-link href="/statistic/overview">
|
||||
<f7-link href="/statistic/transaction">
|
||||
<f7-icon f7="chart_pie"></f7-icon>
|
||||
<span class="tabbar-label">{{ $t('Statistics') }}</span>
|
||||
</f7-link>
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
<template>
|
||||
<f7-page>
|
||||
<f7-navbar :title="$t('Statistics')" :back-link="$t('Back')"></f7-navbar>
|
||||
|
||||
<f7-list media-list class="skeleton-text">
|
||||
<f7-list-item title="Placeholder"></f7-list-item>
|
||||
</f7-list>
|
||||
</f7-page>
|
||||
</template>
|
||||
@@ -0,0 +1,178 @@
|
||||
<template>
|
||||
<f7-page>
|
||||
<f7-navbar :title="$t('Statistics')" :back-link="$t('Back')"></f7-navbar>
|
||||
|
||||
<f7-card>
|
||||
<f7-card-content class="no-safe-areas chart-container" :padding="false">
|
||||
<v-chart :options="chartData" v-if="chartData" />
|
||||
</f7-card-content>
|
||||
</f7-card>
|
||||
</f7-page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
loading: true
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
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) {
|
||||
return account.currency;
|
||||
}
|
||||
}
|
||||
|
||||
return this.$store.getters.currentUserDefaultCurrency || this.$t('default.currency');
|
||||
},
|
||||
query() {
|
||||
return this.$store.state.transactionStatisticsFilter;
|
||||
},
|
||||
chartData() {
|
||||
if (!this.$store.state.transactionStatistics ||
|
||||
!this.$store.state.transactionStatistics.items ||
|
||||
!this.$store.state.transactionStatistics.items.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const combinedData = {};
|
||||
const data = [];
|
||||
|
||||
for (let i = 0; i < this.$store.state.transactionStatistics.items.length; i++) {
|
||||
const item = this.$store.state.transactionStatistics.items[i];
|
||||
|
||||
if (!item.account || !item.category) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item.category.type !== this.$constants.category.allCategoryTypes.Expense) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.query.chartLegendType === this.$constants.statistics.allChartLegendTypes.Account) {
|
||||
if (this.$utilities.isNumber(item.amountInDefaultCurrency)) {
|
||||
let totalAmount = combinedData[item.account.name];
|
||||
|
||||
if (totalAmount) {
|
||||
totalAmount += totalAmount = item.amountInDefaultCurrency;
|
||||
} else {
|
||||
totalAmount = item.amountInDefaultCurrency;
|
||||
}
|
||||
|
||||
combinedData[item.account.name] = totalAmount;
|
||||
}
|
||||
} else if (this.query.chartLegendType === this.$constants.statistics.allChartLegendTypes.SecondaryCategory) {
|
||||
if (this.$utilities.isNumber(item.amountInDefaultCurrency)) {
|
||||
let totalAmount = combinedData[item.category.name];
|
||||
|
||||
if (totalAmount) {
|
||||
totalAmount += totalAmount = item.amountInDefaultCurrency;
|
||||
} else {
|
||||
totalAmount = item.amountInDefaultCurrency;
|
||||
}
|
||||
|
||||
combinedData[item.category.name] = totalAmount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let chartType = 'pie';
|
||||
|
||||
if (this.query.chartType === this.$constants.statistics.allChartTypes.Bar) {
|
||||
chartType = 'bar';
|
||||
}
|
||||
|
||||
for (let legendName in combinedData) {
|
||||
if (!Object.prototype.hasOwnProperty.call(combinedData, legendName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
data.push({
|
||||
name: legendName,
|
||||
value: combinedData[legendName] / 100
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
series: [
|
||||
{
|
||||
type: chartType,
|
||||
data: data,
|
||||
label: {
|
||||
position: 'inside'
|
||||
},
|
||||
animation: false,
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
},
|
||||
created() {
|
||||
const self = this;
|
||||
const router = self.$f7router;
|
||||
|
||||
const dateParam = self.$utilities.getDateRangeByDateType(self.query.dateType);
|
||||
|
||||
if (dateParam.minTime !== self.query.startTime || dateParam.maxTime !== self.query.endTime) {
|
||||
self.$store.dispatch('updateTransactionStatisticsFilter', {
|
||||
startTime: dateParam.minTime,
|
||||
endTime: dateParam.maxTime
|
||||
});
|
||||
}
|
||||
|
||||
Promise.all([
|
||||
self.$store.dispatch('loadAllAccounts', { force: false }),
|
||||
self.$store.dispatch('loadAllCategories', { force: false })
|
||||
]).then(() => {
|
||||
return self.$store.dispatch('loadTransactionStatistics', {
|
||||
defaultCurrency: self.defaultCurrency
|
||||
});
|
||||
}).then(() => {
|
||||
self.loading = false;
|
||||
}).catch(error => {
|
||||
self.loading = false;
|
||||
|
||||
if (!error.processed) {
|
||||
self.$toast(error.message || error);
|
||||
router.back();
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
reload(done) {
|
||||
const self = this;
|
||||
|
||||
self.$store.dispatch('loadTransactionStatistics', {
|
||||
defaultCurrency: self.defaultCurrency
|
||||
}).then(() => {
|
||||
if (done) {
|
||||
done();
|
||||
}
|
||||
}).catch(error => {
|
||||
if (done) {
|
||||
done();
|
||||
}
|
||||
|
||||
if (!error.processed) {
|
||||
self.$toast(error.message || error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.chart-container {
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.chart-container .echarts {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user