mirror of
https://github.com/mayswind/ezbookkeeping.git
synced 2026-05-21 10:14:26 +08:00
migrate transaction statistics page to composition API and typescript
This commit is contained in:
@@ -119,7 +119,7 @@ export class ChartDataType implements TypeAndName {
|
|||||||
return this.availableAnalysisTypes[analysisType] || false;
|
return this.availableAnalysisTypes[analysisType] || false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static values(analysisType: StatisticsAnalysisType | undefined): ChartDataType[] {
|
public static values(analysisType?: StatisticsAnalysisType): ChartDataType[] {
|
||||||
if (analysisType === undefined) {
|
if (analysisType === undefined) {
|
||||||
return ChartDataType.allInstances;
|
return ChartDataType.allInstances;
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-2
@@ -1,4 +1,4 @@
|
|||||||
import type { TypeAndDisplayName } from '@/core/base.ts';
|
import type { TypeAndName, TypeAndDisplayName } from '@/core/base.ts';
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||||
export function isFunction(val: unknown): val is Function {
|
export function isFunction(val: unknown): val is Function {
|
||||||
@@ -37,7 +37,7 @@ export function isBoolean(val: unknown): val is boolean {
|
|||||||
return typeof(val) === 'boolean';
|
return typeof(val) === 'boolean';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isYearMonth(val: unknown): boolean {
|
export function isYearMonth(val: unknown): val is string {
|
||||||
if (!isString(val)) {
|
if (!isString(val)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -312,6 +312,16 @@ export function getItemByKeyValue<T>(src: Record<string, T>[] | Record<string, R
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function findNameByType(items: TypeAndName[], type: number): string | null {
|
||||||
|
for (const item of items) {
|
||||||
|
if (item.type === type) {
|
||||||
|
return item.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
export function findDisplayNameByType(items: TypeAndDisplayName[], type: number): string | null {
|
export function findDisplayNameByType(items: TypeAndDisplayName[], type: number): string | null {
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
if (item.type === type) {
|
if (item.type === type) {
|
||||||
|
|||||||
+54
-1
@@ -105,13 +105,17 @@ import {
|
|||||||
isPM,
|
isPM,
|
||||||
formatUnixTime,
|
formatUnixTime,
|
||||||
formatCurrentTime,
|
formatCurrentTime,
|
||||||
|
parseDateFromUnixTime,
|
||||||
|
getYear,
|
||||||
getTimezoneOffset,
|
getTimezoneOffset,
|
||||||
getTimezoneOffsetMinutes,
|
getTimezoneOffsetMinutes,
|
||||||
getBrowserTimezoneOffset,
|
getBrowserTimezoneOffset,
|
||||||
getBrowserTimezoneOffsetMinutes,
|
getBrowserTimezoneOffsetMinutes,
|
||||||
getTimeDifferenceHoursAndMinutes,
|
getTimeDifferenceHoursAndMinutes,
|
||||||
getDateTimeFormatType,
|
getDateTimeFormatType,
|
||||||
getRecentMonthDateRanges
|
getRecentMonthDateRanges,
|
||||||
|
isDateRangeMatchFullYears,
|
||||||
|
isDateRangeMatchFullMonths
|
||||||
} from '@/lib/datetime.ts';
|
} from '@/lib/datetime.ts';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -1206,6 +1210,54 @@ export function useI18n() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatDateRange(dateType: number, startTime: number, endTime: number): string {
|
||||||
|
if (dateType === DateRange.All.type) {
|
||||||
|
return t(DateRange.All.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
const allDateRanges = DateRange.values();
|
||||||
|
|
||||||
|
for (let i = 0; i < allDateRanges.length; i++) {
|
||||||
|
const dateRange = allDateRanges[i];
|
||||||
|
|
||||||
|
if (dateRange && dateRange.type !== DateRange.Custom.type && dateRange.type === dateType && dateRange.name) {
|
||||||
|
return t(dateRange.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDateRangeMatchFullYears(startTime, endTime)) {
|
||||||
|
const format = getLocalizedShortYearFormat();
|
||||||
|
const displayStartTime = formatUnixTime(startTime, format);
|
||||||
|
const displayEndTime = formatUnixTime(endTime, format);
|
||||||
|
|
||||||
|
return displayStartTime !== displayEndTime ? `${displayStartTime} ~ ${displayEndTime}` : displayStartTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDateRangeMatchFullMonths(startTime, endTime)) {
|
||||||
|
const format = getLocalizedShortYearMonthFormat();
|
||||||
|
const displayStartTime = formatUnixTime(startTime, format);
|
||||||
|
const displayEndTime = formatUnixTime(endTime, format);
|
||||||
|
|
||||||
|
return displayStartTime !== displayEndTime ? `${displayStartTime} ~ ${displayEndTime}` : displayStartTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
const startTimeYear = getYear(parseDateFromUnixTime(startTime));
|
||||||
|
const endTimeYear = getYear(parseDateFromUnixTime(endTime));
|
||||||
|
|
||||||
|
const format = getLocalizedShortDateFormat();
|
||||||
|
const displayStartTime = formatUnixTime(startTime, format);
|
||||||
|
const displayEndTime = formatUnixTime(endTime, format);
|
||||||
|
|
||||||
|
if (displayStartTime === displayEndTime) {
|
||||||
|
return displayStartTime;
|
||||||
|
} else if (startTimeYear === endTimeYear) {
|
||||||
|
const displayShortEndTime = formatUnixTime(endTime, getLocalizedShortMonthDayFormat());
|
||||||
|
return `${displayStartTime} ~ ${displayShortEndTime}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${displayStartTime} ~ ${displayEndTime}`;
|
||||||
|
}
|
||||||
|
|
||||||
function getTimezoneDifferenceDisplayText(utcOffset: number): string {
|
function getTimezoneDifferenceDisplayText(utcOffset: number): string {
|
||||||
const defaultTimezoneOffset = getTimezoneOffsetMinutes();
|
const defaultTimezoneOffset = getTimezoneOffsetMinutes();
|
||||||
const offsetTime = getTimeDifferenceHoursAndMinutes(utcOffset - defaultTimezoneOffset);
|
const offsetTime = getTimeDifferenceHoursAndMinutes(utcOffset - defaultTimezoneOffset);
|
||||||
@@ -1486,6 +1538,7 @@ export function useI18n() {
|
|||||||
formatUnixTimeToLongTime: (unixTime: number, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, getLocalizedLongTimeFormat(), utcOffset, currentUtcOffset),
|
formatUnixTimeToLongTime: (unixTime: number, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, getLocalizedLongTimeFormat(), utcOffset, currentUtcOffset),
|
||||||
formatUnixTimeToShortTime: (unixTime: number, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, getLocalizedShortTimeFormat(), utcOffset, currentUtcOffset),
|
formatUnixTimeToShortTime: (unixTime: number, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, getLocalizedShortTimeFormat(), utcOffset, currentUtcOffset),
|
||||||
formatYearQuarter,
|
formatYearQuarter,
|
||||||
|
formatDateRange,
|
||||||
getTimezoneDifferenceDisplayText,
|
getTimezoneDifferenceDisplayText,
|
||||||
appendDigitGroupingSymbol: getNumberWithDigitGroupingSymbol,
|
appendDigitGroupingSymbol: getNumberWithDigitGroupingSymbol,
|
||||||
parseAmount: getParsedAmountNumber,
|
parseAmount: getParsedAmountNumber,
|
||||||
|
|||||||
@@ -101,7 +101,24 @@ interface WritableTransactioTrendsAnalysisDataItem {
|
|||||||
items: TransactionTrendsAnalysisDataAmount[];
|
items: TransactionTrendsAnalysisDataAmount[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TransactionStatisticsFilter {
|
export interface TransactionStatisticsPartialFilter {
|
||||||
|
chartDataType?: number;
|
||||||
|
categoricalChartType?: number;
|
||||||
|
categoricalChartDateType?: number;
|
||||||
|
categoricalChartStartTime?: number;
|
||||||
|
categoricalChartEndTime?: number;
|
||||||
|
trendChartType?: number;
|
||||||
|
trendChartDateType?: number;
|
||||||
|
trendChartStartYearMonth?: string;
|
||||||
|
trendChartEndYearMonth?: string;
|
||||||
|
filterAccountIds?: Record<string, boolean>;
|
||||||
|
filterCategoryIds?: Record<string, boolean>;
|
||||||
|
tagIds?: string;
|
||||||
|
tagFilterType?: number;
|
||||||
|
sortingType?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TransactionStatisticsFilter extends TransactionStatisticsPartialFilter {
|
||||||
chartDataType: number;
|
chartDataType: number;
|
||||||
categoricalChartType: number;
|
categoricalChartType: number;
|
||||||
categoricalChartDateType: number;
|
categoricalChartDateType: number;
|
||||||
@@ -672,7 +689,7 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
|||||||
transactionStatisticsStateInvalid.value = true;
|
transactionStatisticsStateInvalid.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function initTransactionStatisticsFilter(analysisType: StatisticsAnalysisType, filter?: TransactionStatisticsFilter): void {
|
function initTransactionStatisticsFilter(analysisType: StatisticsAnalysisType, filter?: TransactionStatisticsPartialFilter): void {
|
||||||
if (filter && isInteger(filter.chartDataType)) {
|
if (filter && isInteger(filter.chartDataType)) {
|
||||||
transactionStatisticsFilter.value.chartDataType = filter.chartDataType;
|
transactionStatisticsFilter.value.chartDataType = filter.chartDataType;
|
||||||
} else {
|
} else {
|
||||||
@@ -810,7 +827,7 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateTransactionStatisticsFilter(filter: TransactionStatisticsFilter): boolean {
|
function updateTransactionStatisticsFilter(filter: TransactionStatisticsPartialFilter): boolean {
|
||||||
let changed = false;
|
let changed = false;
|
||||||
|
|
||||||
if (filter && isInteger(filter.chartDataType) && transactionStatisticsFilter.value.chartDataType !== filter.chartDataType) {
|
if (filter && isInteger(filter.chartDataType) && transactionStatisticsFilter.value.chartDataType !== filter.chartDataType) {
|
||||||
@@ -943,7 +960,7 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
|||||||
return querys.join('&');
|
return querys.join('&');
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTransactionListPageParams(analysisType: StatisticsAnalysisType, itemId: string, dateRange: TimeRangeAndDateType): string {
|
function getTransactionListPageParams(analysisType: StatisticsAnalysisType, itemId: string, dateRange?: TimeRangeAndDateType): string {
|
||||||
const querys: string[] = [];
|
const querys: string[] = [];
|
||||||
|
|
||||||
if (transactionStatisticsFilter.value.chartDataType === ChartDataType.IncomeByAccount.type
|
if (transactionStatisticsFilter.value.chartDataType === ChartDataType.IncomeByAccount.type
|
||||||
|
|||||||
@@ -0,0 +1,243 @@
|
|||||||
|
import { ref, computed } from 'vue';
|
||||||
|
|
||||||
|
import { useI18n } from '@/locales/helpers.ts';
|
||||||
|
|
||||||
|
import { useSettingsStore } from '@/stores/setting.ts';
|
||||||
|
import { useUserStore } from '@/stores/user.ts';
|
||||||
|
import { type TransactionStatisticsFilter, useStatisticsStore } from '@/stores/statistics.ts';
|
||||||
|
|
||||||
|
import type { TypeAndDisplayName } from '@/core/base.ts';
|
||||||
|
import { type LocalizedDateRange, DateRangeScene, DateRange } from '@/core/datetime.ts';
|
||||||
|
import { StatisticsAnalysisType, ChartDataType, ChartSortingType, ChartDateAggregationType } from '@/core/statistics.ts';
|
||||||
|
import type { TransactionCategoricalAnalysisData, TransactionTrendsAnalysisData } from '@/models/transaction.ts';
|
||||||
|
|
||||||
|
import { limitText, findNameByType, findDisplayNameByType } from '@/lib/common.ts';
|
||||||
|
import { getYearMonthFirstUnixTime, getYearMonthLastUnixTime } from '@/lib/datetime.ts';
|
||||||
|
|
||||||
|
export function useStatisticsTransactionPageBase() {
|
||||||
|
const {
|
||||||
|
tt,
|
||||||
|
getAllDateRanges,
|
||||||
|
getAllStatisticsSortingTypes,
|
||||||
|
getAllStatisticsDateAggregationTypes,
|
||||||
|
formatUnixTimeToLongDateTime,
|
||||||
|
formatUnixTimeToLongYearMonth,
|
||||||
|
formatDateRange,
|
||||||
|
formatAmountWithCurrency
|
||||||
|
} = useI18n();
|
||||||
|
|
||||||
|
const settingsStore = useSettingsStore();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const statisticsStore = useStatisticsStore();
|
||||||
|
|
||||||
|
const loading = ref<boolean>(true);
|
||||||
|
const analysisType = ref<StatisticsAnalysisType>(StatisticsAnalysisType.CategoricalAnalysis);
|
||||||
|
const trendDateAggregationType = ref<number>(ChartDateAggregationType.Default.type);
|
||||||
|
|
||||||
|
const showAccountBalance = computed<boolean>(() => settingsStore.appSettings.showAccountBalance);
|
||||||
|
const defaultCurrency = computed<string>(() => userStore.currentUserDefaultCurrency);
|
||||||
|
const firstDayOfWeek = computed<number>(() => userStore.currentUserFirstDayOfWeek);
|
||||||
|
|
||||||
|
const allDateRanges = computed<LocalizedDateRange[]>(() => {
|
||||||
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||||
|
return getAllDateRanges(DateRangeScene.Normal, true);
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
||||||
|
return getAllDateRanges(DateRangeScene.TrendAnalysis, true);
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const allSortingTypes = computed<TypeAndDisplayName[]>(() => getAllStatisticsSortingTypes());
|
||||||
|
const allDateAggregationTypes = computed<TypeAndDisplayName[]>(() => getAllStatisticsDateAggregationTypes());
|
||||||
|
|
||||||
|
const query = computed<TransactionStatisticsFilter>(() => statisticsStore.transactionStatisticsFilter);
|
||||||
|
const queryChartDataCategory = computed<string>(() => statisticsStore.categoricalAnalysisChartDataCategory);
|
||||||
|
const queryDateType = computed<number | null>(() => {
|
||||||
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||||
|
return query.value.categoricalChartDateType;
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
||||||
|
return query.value.trendChartDateType;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const queryStartTime = computed<string>(() => {
|
||||||
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||||
|
return formatUnixTimeToLongDateTime(query.value.categoricalChartStartTime);
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
||||||
|
return formatUnixTimeToLongYearMonth(getYearMonthFirstUnixTime(query.value.trendChartStartYearMonth));
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const queryEndTime = computed<string>(() => {
|
||||||
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||||
|
return formatUnixTimeToLongDateTime(query.value.categoricalChartEndTime);
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
||||||
|
return formatUnixTimeToLongYearMonth(getYearMonthLastUnixTime(query.value.trendChartEndYearMonth));
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const queryDateRangeName = computed<string>(() => {
|
||||||
|
if (query.value.chartDataType === ChartDataType.AccountTotalAssets.type ||
|
||||||
|
query.value.chartDataType === ChartDataType.AccountTotalLiabilities.type) {
|
||||||
|
return tt(DateRange.All.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||||
|
return formatDateRange(query.value.categoricalChartDateType, query.value.categoricalChartStartTime, query.value.categoricalChartEndTime);
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
||||||
|
return formatDateRange(query.value.trendChartDateType, getYearMonthFirstUnixTime(query.value.trendChartStartYearMonth), getYearMonthLastUnixTime(query.value.trendChartEndYearMonth));
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const queryChartDataTypeName = computed<string>(() => {
|
||||||
|
const queryChartDataTypeName = findNameByType(ChartDataType.values(), query.value.chartDataType) || 'Statistics';
|
||||||
|
return tt(queryChartDataTypeName);
|
||||||
|
});
|
||||||
|
|
||||||
|
const querySortingTypeName = computed<string>(() => {
|
||||||
|
const querySortingTypeName = findNameByType(ChartSortingType.values(), query.value.sortingType) || 'System Default';
|
||||||
|
return tt(querySortingTypeName);
|
||||||
|
});
|
||||||
|
|
||||||
|
const queryTrendDateAggregationTypeName = computed<string>(() => findDisplayNameByType(allDateAggregationTypes.value, trendDateAggregationType.value) || '');
|
||||||
|
|
||||||
|
const isQueryDateRangeChanged = computed<boolean>(() => {
|
||||||
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||||
|
if (query.value.chartDataType === ChartDataType.AccountTotalAssets.type ||
|
||||||
|
query.value.chartDataType === ChartDataType.AccountTotalLiabilities.type) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !!query.value.categoricalChartStartTime || !!query.value.categoricalChartEndTime;
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
||||||
|
return !!query.value.trendChartStartYearMonth || !!query.value.trendChartEndYearMonth;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const canShiftDateRange = computed<boolean>(() => {
|
||||||
|
if (query.value.chartDataType === ChartDataType.AccountTotalAssets.type || query.value.chartDataType === ChartDataType.AccountTotalLiabilities.type) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||||
|
return query.value.categoricalChartDateType !== DateRange.All.type;
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
||||||
|
return query.value.trendChartDateType !== DateRange.All.type;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const showCustomDateRange = computed<boolean>(() => {
|
||||||
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||||
|
return query.value.categoricalChartDateType === DateRange.Custom.type && !!query.value.categoricalChartStartTime && !!query.value.categoricalChartEndTime;
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
||||||
|
return query.value.trendChartDateType === DateRange.Custom.type && !!query.value.trendChartStartYearMonth && !!query.value.trendChartEndYearMonth;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const showAmountInChart = computed<boolean>(() => {
|
||||||
|
if (!showAccountBalance.value
|
||||||
|
&& (query.value.chartDataType === ChartDataType.AccountTotalAssets.type || query.value.chartDataType === ChartDataType.AccountTotalLiabilities.type)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
const totalAmountName = computed<string>(() => {
|
||||||
|
if (query.value.chartDataType === ChartDataType.IncomeByAccount.type
|
||||||
|
|| query.value.chartDataType === ChartDataType.IncomeByPrimaryCategory.type
|
||||||
|
|| query.value.chartDataType === ChartDataType.IncomeBySecondaryCategory.type) {
|
||||||
|
return tt('Total Income');
|
||||||
|
} else if (query.value.chartDataType === ChartDataType.ExpenseByAccount.type
|
||||||
|
|| query.value.chartDataType === ChartDataType.ExpenseByPrimaryCategory.type
|
||||||
|
|| query.value.chartDataType === ChartDataType.ExpenseBySecondaryCategory.type) {
|
||||||
|
return tt('Total Expense');
|
||||||
|
} else if (query.value.chartDataType === ChartDataType.AccountTotalAssets.type) {
|
||||||
|
return tt('Total Assets');
|
||||||
|
} else if (query.value.chartDataType === ChartDataType.AccountTotalLiabilities.type) {
|
||||||
|
return tt('Total Liabilities');
|
||||||
|
}
|
||||||
|
|
||||||
|
return tt('Total Amount');
|
||||||
|
});
|
||||||
|
|
||||||
|
const showTotalAmountInTrendsChart = computed<boolean>(() => {
|
||||||
|
return query.value.chartDataType !== ChartDataType.TotalExpense.type &&
|
||||||
|
query.value.chartDataType !== ChartDataType.TotalIncome.type &&
|
||||||
|
query.value.chartDataType !== ChartDataType.TotalBalance.type;
|
||||||
|
});
|
||||||
|
|
||||||
|
const translateNameInTrendsChart = computed<boolean>(() => {
|
||||||
|
return query.value.chartDataType === ChartDataType.TotalExpense.type ||
|
||||||
|
query.value.chartDataType === ChartDataType.TotalIncome.type ||
|
||||||
|
query.value.chartDataType === ChartDataType.TotalBalance.type;
|
||||||
|
});
|
||||||
|
|
||||||
|
const categoricalAnalysisData = computed<TransactionCategoricalAnalysisData>(() => statisticsStore.categoricalAnalysisData);
|
||||||
|
const trendsAnalysisData = computed<TransactionTrendsAnalysisData | null>(() => statisticsStore.trendsAnalysisData);
|
||||||
|
|
||||||
|
function getDisplayAmount(amount: number, currency: string, textLimit?: number): string {
|
||||||
|
const finalAmount = formatAmountWithCurrency(amount, currency) as string;
|
||||||
|
|
||||||
|
if (!showAccountBalance.value
|
||||||
|
&& (query.value.chartDataType === ChartDataType.AccountTotalAssets.type
|
||||||
|
|| query.value.chartDataType === ChartDataType.AccountTotalLiabilities.type)
|
||||||
|
) {
|
||||||
|
return '***';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (textLimit) {
|
||||||
|
return limitText(finalAmount, textLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
return finalAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
// states
|
||||||
|
loading,
|
||||||
|
analysisType,
|
||||||
|
trendDateAggregationType,
|
||||||
|
// computed states
|
||||||
|
showAccountBalance,
|
||||||
|
defaultCurrency,
|
||||||
|
firstDayOfWeek,
|
||||||
|
allDateRanges,
|
||||||
|
allSortingTypes,
|
||||||
|
allDateAggregationTypes,
|
||||||
|
query,
|
||||||
|
queryChartDataCategory,
|
||||||
|
queryDateType,
|
||||||
|
queryStartTime,
|
||||||
|
queryEndTime,
|
||||||
|
queryDateRangeName,
|
||||||
|
queryChartDataTypeName,
|
||||||
|
querySortingTypeName,
|
||||||
|
queryTrendDateAggregationTypeName,
|
||||||
|
isQueryDateRangeChanged,
|
||||||
|
canShiftDateRange,
|
||||||
|
showCustomDateRange,
|
||||||
|
showAmountInChart,
|
||||||
|
totalAmountName,
|
||||||
|
showTotalAmountInTrendsChart,
|
||||||
|
translateNameInTrendsChart,
|
||||||
|
categoricalAnalysisData,
|
||||||
|
trendsAnalysisData,
|
||||||
|
// functions
|
||||||
|
getDisplayAmount
|
||||||
|
};
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<f7-page ptr @ptr:refresh="reload" @page:afterin="onPageAfterIn">
|
<f7-page ptr @ptr:refresh="reload" @page:afterin="onPageAfterIn">
|
||||||
<f7-navbar>
|
<f7-navbar>
|
||||||
<f7-nav-left :back-link="$t('Back')"></f7-nav-left>
|
<f7-nav-left :back-link="tt('Back')"></f7-nav-left>
|
||||||
<f7-nav-title>
|
<f7-nav-title>
|
||||||
<f7-link popover-open=".chart-data-type-popover-menu">
|
<f7-link popover-open=".chart-data-type-popover-menu">
|
||||||
<span>{{ queryChartDataTypeName }}</span>
|
<span>{{ queryChartDataTypeName }}</span>
|
||||||
@@ -18,38 +18,38 @@
|
|||||||
@popover:open="scrollPopoverToSelectedItem">
|
@popover:open="scrollPopoverToSelectedItem">
|
||||||
<f7-list dividers>
|
<f7-list dividers>
|
||||||
<f7-list-group>
|
<f7-list-group>
|
||||||
<f7-list-item group-title :title="$t('Categorical Analysis')" />
|
<f7-list-item group-title :title="tt('Categorical Analysis')" />
|
||||||
<f7-list-item :title="$t(dataType.name)"
|
<f7-list-item :title="tt(dataType.name)"
|
||||||
:class="{ 'list-item-selected': analysisType === allAnalysisTypes.CategoricalAnalysis && query.chartDataType === dataType.type }"
|
:class="{ 'list-item-selected': analysisType === StatisticsAnalysisType.CategoricalAnalysis && query.chartDataType === dataType.type }"
|
||||||
:key="dataType.type"
|
:key="dataType.type"
|
||||||
v-for="dataType in allChartDataTypes"
|
v-for="dataType in ChartDataType.all()"
|
||||||
v-show="dataType.isAvailableAnalysisType(allAnalysisTypes.CategoricalAnalysis)"
|
v-show="dataType.isAvailableAnalysisType(StatisticsAnalysisType.CategoricalAnalysis)"
|
||||||
@click="setChartDataType(allAnalysisTypes.CategoricalAnalysis, dataType.type)">
|
@click="setChartDataType(StatisticsAnalysisType.CategoricalAnalysis, dataType.type)">
|
||||||
<template #after>
|
<template #after>
|
||||||
<f7-icon class="list-item-checked-icon" f7="checkmark_alt" v-if="analysisType === allAnalysisTypes.CategoricalAnalysis && query.chartDataType === dataType.type"></f7-icon>
|
<f7-icon class="list-item-checked-icon" f7="checkmark_alt" v-if="analysisType === StatisticsAnalysisType.CategoricalAnalysis && query.chartDataType === dataType.type"></f7-icon>
|
||||||
</template>
|
</template>
|
||||||
</f7-list-item>
|
</f7-list-item>
|
||||||
</f7-list-group>
|
</f7-list-group>
|
||||||
<f7-list-group>
|
<f7-list-group>
|
||||||
<f7-list-item group-title :title="$t('Trend Analysis')" />
|
<f7-list-item group-title :title="tt('Trend Analysis')" />
|
||||||
<f7-list-item :title="$t(dataType.name)"
|
<f7-list-item :title="tt(dataType.name)"
|
||||||
:class="{ 'list-item-selected': analysisType === allAnalysisTypes.TrendAnalysis && query.chartDataType === dataType.type }"
|
:class="{ 'list-item-selected': analysisType === StatisticsAnalysisType.TrendAnalysis && query.chartDataType === dataType.type }"
|
||||||
:key="dataType.type"
|
:key="dataType.type"
|
||||||
v-for="dataType in allChartDataTypes"
|
v-for="dataType in ChartDataType.all()"
|
||||||
v-show="dataType.isAvailableAnalysisType(allAnalysisTypes.TrendAnalysis)"
|
v-show="dataType.isAvailableAnalysisType(StatisticsAnalysisType.TrendAnalysis)"
|
||||||
@click="setChartDataType(allAnalysisTypes.TrendAnalysis, dataType.type)">
|
@click="setChartDataType(StatisticsAnalysisType.TrendAnalysis, dataType.type)">
|
||||||
<template #after>
|
<template #after>
|
||||||
<f7-icon class="list-item-checked-icon" f7="checkmark_alt" v-if="analysisType === allAnalysisTypes.TrendAnalysis && query.chartDataType === dataType.type"></f7-icon>
|
<f7-icon class="list-item-checked-icon" f7="checkmark_alt" v-if="analysisType === StatisticsAnalysisType.TrendAnalysis && query.chartDataType === dataType.type"></f7-icon>
|
||||||
</template>
|
</template>
|
||||||
</f7-list-item>
|
</f7-list-item>
|
||||||
</f7-list-group>
|
</f7-list-group>
|
||||||
</f7-list>
|
</f7-list>
|
||||||
</f7-popover>
|
</f7-popover>
|
||||||
|
|
||||||
<f7-card v-if="analysisType === allAnalysisTypes.CategoricalAnalysis && query.categoricalChartType === allCategoricalChartTypes.Pie.type">
|
<f7-card v-if="analysisType === StatisticsAnalysisType.CategoricalAnalysis && query.categoricalChartType === CategoricalChartType.Pie.type">
|
||||||
<f7-card-header class="no-border display-block">
|
<f7-card-header class="no-border display-block">
|
||||||
<div class="statistics-chart-header full-line text-align-right">
|
<div class="statistics-chart-header full-line text-align-right">
|
||||||
<span style="margin-right: 4px;">{{ $t('Sort by') }}</span>
|
<span style="margin-right: 4px;">{{ tt('Sort by') }}</span>
|
||||||
<f7-link href="#" popover-open=".sorting-type-popover-menu">{{ querySortingTypeName }}</f7-link>
|
<f7-link href="#" popover-open=".sorting-type-popover-menu">{{ querySortingTypeName }}</f7-link>
|
||||||
</div>
|
</div>
|
||||||
</f7-card-header>
|
</f7-card-header>
|
||||||
@@ -80,7 +80,7 @@
|
|||||||
percent-field="percent"
|
percent-field="percent"
|
||||||
hidden-field="hidden"
|
hidden-field="hidden"
|
||||||
v-else-if="!loading"
|
v-else-if="!loading"
|
||||||
@click="clickPieChartItem"
|
@click="onClickPieChartItem"
|
||||||
>
|
>
|
||||||
<text class="statistics-pie-chart-total-amount-title" v-if="categoricalAnalysisData.items && categoricalAnalysisData.items.length">
|
<text class="statistics-pie-chart-total-amount-title" v-if="categoricalAnalysisData.items && categoricalAnalysisData.items.length">
|
||||||
{{ totalAmountName }}
|
{{ totalAmountName }}
|
||||||
@@ -89,25 +89,25 @@
|
|||||||
{{ getDisplayAmount(categoricalAnalysisData.totalAmount, defaultCurrency, 16) }}
|
{{ getDisplayAmount(categoricalAnalysisData.totalAmount, defaultCurrency, 16) }}
|
||||||
</text>
|
</text>
|
||||||
<text class="statistics-pie-chart-total-no-data" cy="50%" v-if="!categoricalAnalysisData.items || !categoricalAnalysisData.items.length">
|
<text class="statistics-pie-chart-total-no-data" cy="50%" v-if="!categoricalAnalysisData.items || !categoricalAnalysisData.items.length">
|
||||||
{{ $t('No data') }}
|
{{ tt('No data') }}
|
||||||
</text>
|
</text>
|
||||||
</pie-chart>
|
</pie-chart>
|
||||||
</f7-card-content>
|
</f7-card-content>
|
||||||
</f7-card>
|
</f7-card>
|
||||||
|
|
||||||
<f7-card v-else-if="analysisType === allAnalysisTypes.CategoricalAnalysis && query.categoricalChartType === allCategoricalChartTypes.Bar.type">
|
<f7-card v-else-if="analysisType === StatisticsAnalysisType.CategoricalAnalysis && query.categoricalChartType === CategoricalChartType.Bar.type">
|
||||||
<f7-card-header class="no-border display-block">
|
<f7-card-header class="no-border display-block">
|
||||||
<div class="statistics-chart-header display-flex full-line justify-content-space-between">
|
<div class="statistics-chart-header display-flex full-line justify-content-space-between">
|
||||||
<div>
|
<div>
|
||||||
{{ totalAmountName }}
|
{{ totalAmountName }}
|
||||||
</div>
|
</div>
|
||||||
<div class="align-self-flex-end">
|
<div class="align-self-flex-end">
|
||||||
<span style="margin-right: 4px;">{{ $t('Sort by') }}</span>
|
<span style="margin-right: 4px;">{{ tt('Sort by') }}</span>
|
||||||
<f7-link href="#" popover-open=".sorting-type-popover-menu">{{ querySortingTypeName }}</f7-link>
|
<f7-link href="#" popover-open=".sorting-type-popover-menu">{{ querySortingTypeName }}</f7-link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="display-flex full-line">
|
<div class="display-flex full-line">
|
||||||
<div :class="{ 'statistics-list-item-overview-amount': true, 'text-expense': query.chartDataType === allChartDataTypes.ExpenseByAccount.type || query.chartDataType === allChartDataTypes.ExpenseByPrimaryCategory.type || query.chartDataType === allChartDataTypes.ExpenseBySecondaryCategory.type, 'text-income': query.chartDataType === allChartDataTypes.IncomeByAccount.type || query.chartDataType === allChartDataTypes.IncomeByPrimaryCategory.type || query.chartDataType === allChartDataTypes.IncomeBySecondaryCategory.type }">
|
<div :class="{ 'statistics-list-item-overview-amount': true, 'text-expense': query.chartDataType === ChartDataType.ExpenseByAccount.type || query.chartDataType === ChartDataType.ExpenseByPrimaryCategory.type || query.chartDataType === ChartDataType.ExpenseBySecondaryCategory.type, 'text-income': query.chartDataType === ChartDataType.IncomeByAccount.type || query.chartDataType === ChartDataType.IncomeByPrimaryCategory.type || query.chartDataType === ChartDataType.IncomeBySecondaryCategory.type }">
|
||||||
<span v-if="!loading && categoricalAnalysisData && categoricalAnalysisData.items && categoricalAnalysisData.items.length">
|
<span v-if="!loading && categoricalAnalysisData && categoricalAnalysisData.items && categoricalAnalysisData.items.length">
|
||||||
{{ getDisplayAmount(categoricalAnalysisData.totalAmount, defaultCurrency) }}
|
{{ getDisplayAmount(categoricalAnalysisData.totalAmount, defaultCurrency) }}
|
||||||
</span>
|
</span>
|
||||||
@@ -147,7 +147,7 @@
|
|||||||
</f7-list>
|
</f7-list>
|
||||||
|
|
||||||
<f7-list v-else-if="!loading && (!categoricalAnalysisData || !categoricalAnalysisData.items || !categoricalAnalysisData.items.length)">
|
<f7-list v-else-if="!loading && (!categoricalAnalysisData || !categoricalAnalysisData.items || !categoricalAnalysisData.items.length)">
|
||||||
<f7-list-item :title="$t('No transaction data')"></f7-list-item>
|
<f7-list-item :title="tt('No transaction data')"></f7-list-item>
|
||||||
</f7-list>
|
</f7-list>
|
||||||
|
|
||||||
<f7-list v-else-if="!loading && categoricalAnalysisData && categoricalAnalysisData.items && categoricalAnalysisData.items.length">
|
<f7-list v-else-if="!loading && categoricalAnalysisData && categoricalAnalysisData.items && categoricalAnalysisData.items.length">
|
||||||
@@ -169,12 +169,12 @@
|
|||||||
<template #title>
|
<template #title>
|
||||||
<div class="statistics-list-item-text">
|
<div class="statistics-list-item-text">
|
||||||
<span>{{ item.name }}</span>
|
<span>{{ item.name }}</span>
|
||||||
<small class="statistics-percent" v-if="item.percent >= 0">{{ getDisplayPercent(item.percent, 2, '<0.01') }}</small>
|
<small class="statistics-percent" v-if="item.percent >= 0">{{ formatPercent(item.percent, 2, '<0.01') }}</small>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #after>
|
<template #after>
|
||||||
<span>{{ getDisplayAmount(item.totalAmount, (item.currency || defaultCurrency)) }}</span>
|
<span>{{ getDisplayAmount(item.totalAmount, defaultCurrency) }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #inner-end>
|
<template #inner-end>
|
||||||
@@ -189,12 +189,12 @@
|
|||||||
</f7-card-content>
|
</f7-card-content>
|
||||||
</f7-card>
|
</f7-card>
|
||||||
|
|
||||||
<f7-card v-else-if="analysisType === allAnalysisTypes.TrendAnalysis">
|
<f7-card v-else-if="analysisType === StatisticsAnalysisType.TrendAnalysis">
|
||||||
<f7-card-header class="no-border display-block">
|
<f7-card-header class="no-border display-block">
|
||||||
<div class="statistics-chart-header display-flex full-line justify-content-space-between">
|
<div class="statistics-chart-header display-flex full-line justify-content-space-between">
|
||||||
<div></div>
|
<div></div>
|
||||||
<div class="align-self-flex-end">
|
<div class="align-self-flex-end">
|
||||||
<span style="margin-right: 4px;">{{ $t('Sort by') }}</span>
|
<span style="margin-right: 4px;">{{ tt('Sort by') }}</span>
|
||||||
<f7-link href="#" popover-open=".sorting-type-popover-menu">{{ querySortingTypeName }}</f7-link>
|
<f7-link href="#" popover-open=".sorting-type-popover-menu">{{ querySortingTypeName }}</f7-link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -214,7 +214,7 @@
|
|||||||
value-field="totalAmount"
|
value-field="totalAmount"
|
||||||
hidden-field="hidden"
|
hidden-field="hidden"
|
||||||
display-orders-field="displayOrders"
|
display-orders-field="displayOrders"
|
||||||
@click="clickTrendChartItem"
|
@click="onClickTrendChartItem"
|
||||||
/>
|
/>
|
||||||
</f7-card-content>
|
</f7-card-content>
|
||||||
</f7-card>
|
</f7-card>
|
||||||
@@ -235,18 +235,18 @@
|
|||||||
</f7-popover>
|
</f7-popover>
|
||||||
|
|
||||||
<f7-toolbar tabbar bottom class="toolbar-item-auto-size">
|
<f7-toolbar tabbar bottom class="toolbar-item-auto-size">
|
||||||
<f7-link :class="{ 'disabled': reloading || !canShiftDateRange(query) }" @click="shiftDateRange(query, -1)">
|
<f7-link :class="{ 'disabled': reloading || !canShiftDateRange }" @click="shiftDateRange(-1)">
|
||||||
<f7-icon f7="arrow_left_square"></f7-icon>
|
<f7-icon f7="arrow_left_square"></f7-icon>
|
||||||
</f7-link>
|
</f7-link>
|
||||||
<f7-link :class="{ 'tabbar-text-with-ellipsis': true, 'disabled': reloading || query.chartDataType === allChartDataTypes.AccountTotalAssets.type || query.chartDataType === allChartDataTypes.AccountTotalLiabilities.type }" popover-open=".date-popover-menu">
|
<f7-link :class="{ 'tabbar-text-with-ellipsis': true, 'disabled': reloading || query.chartDataType === ChartDataType.AccountTotalAssets.type || query.chartDataType === ChartDataType.AccountTotalLiabilities.type }" popover-open=".date-popover-menu">
|
||||||
<span :class="{ 'tabbar-item-changed': query.maxTime > 0 || query.minTime > 0 }">{{ dateRangeName(query) }}</span>
|
<span :class="{ 'tabbar-item-changed': isQueryDateRangeChanged }">{{ queryDateRangeName }}</span>
|
||||||
</f7-link>
|
</f7-link>
|
||||||
<f7-link :class="{ 'disabled': reloading || !canShiftDateRange(query) }" @click="shiftDateRange(query, 1)">
|
<f7-link :class="{ 'disabled': reloading || !canShiftDateRange }" @click="shiftDateRange(1)">
|
||||||
<f7-icon f7="arrow_right_square"></f7-icon>
|
<f7-icon f7="arrow_right_square"></f7-icon>
|
||||||
</f7-link>
|
</f7-link>
|
||||||
<f7-link :class="{ 'tabbar-text-with-ellipsis': true, 'disabled': reloading }" popover-open=".date-aggregation-popover-menu"
|
<f7-link :class="{ 'tabbar-text-with-ellipsis': true, 'disabled': reloading }" popover-open=".date-aggregation-popover-menu"
|
||||||
v-if="analysisType === allAnalysisTypes.TrendAnalysis">
|
v-if="analysisType === StatisticsAnalysisType.TrendAnalysis">
|
||||||
<span :class="{ 'tabbar-item-changed': trendDateAggregationType !== defaultTrendDateAggregationType }">{{ queryTrendDateAggregationTypeName }}</span>
|
<span :class="{ 'tabbar-item-changed': trendDateAggregationType !== ChartDateAggregationType.Default.type }">{{ queryTrendDateAggregationTypeName }}</span>
|
||||||
</f7-link>
|
</f7-link>
|
||||||
<f7-link class="tabbar-text-with-ellipsis" :key="chartType.type"
|
<f7-link class="tabbar-text-with-ellipsis" :key="chartType.type"
|
||||||
v-for="chartType in allChartTypes" @click="setChartType(chartType.type)">
|
v-for="chartType in allChartTypes" @click="setChartType(chartType.type)">
|
||||||
@@ -261,13 +261,13 @@
|
|||||||
<f7-list-item :title="dateRange.displayName"
|
<f7-list-item :title="dateRange.displayName"
|
||||||
:class="{ 'list-item-selected': queryDateType === dateRange.type }"
|
:class="{ 'list-item-selected': queryDateType === dateRange.type }"
|
||||||
:key="dateRange.type"
|
:key="dateRange.type"
|
||||||
v-for="dateRange in allDateRangesArray"
|
v-for="dateRange in allDateRanges"
|
||||||
@click="setDateFilter(dateRange.type)">
|
@click="setDateFilter(dateRange.type)">
|
||||||
<template #after>
|
<template #after>
|
||||||
<f7-icon class="list-item-checked-icon" f7="checkmark_alt" v-if="queryDateType === dateRange.type"></f7-icon>
|
<f7-icon class="list-item-checked-icon" f7="checkmark_alt" v-if="queryDateType === dateRange.type"></f7-icon>
|
||||||
</template>
|
</template>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div v-if="dateRange.type === allDateRanges.Custom.type && showCustomDateRange()">
|
<div v-if="dateRange.type === DateRange.Custom.type && showCustomDateRange">
|
||||||
<span>{{ queryStartTime }}</span>
|
<span>{{ queryStartTime }}</span>
|
||||||
<span> - </span>
|
<span> - </span>
|
||||||
<br/>
|
<br/>
|
||||||
@@ -294,14 +294,14 @@
|
|||||||
</f7-list>
|
</f7-list>
|
||||||
</f7-popover>
|
</f7-popover>
|
||||||
|
|
||||||
<date-range-selection-sheet :title="$t('Custom Date Range')"
|
<date-range-selection-sheet :title="tt('Custom Date Range')"
|
||||||
:min-time="query.categoricalChartStartTime"
|
:min-time="query.categoricalChartStartTime"
|
||||||
:max-time="query.categoricalChartEndTime"
|
:max-time="query.categoricalChartEndTime"
|
||||||
v-model:show="showCustomDateRangeSheet"
|
v-model:show="showCustomDateRangeSheet"
|
||||||
@dateRange:change="setCustomDateFilter">
|
@dateRange:change="setCustomDateFilter">
|
||||||
</date-range-selection-sheet>
|
</date-range-selection-sheet>
|
||||||
|
|
||||||
<month-range-selection-sheet :title="$t('Custom Date Range')"
|
<month-range-selection-sheet :title="tt('Custom Date Range')"
|
||||||
:min-time="query.trendChartStartYearMonth"
|
:min-time="query.trendChartStartYearMonth"
|
||||||
:max-time="query.trendChartEndYearMonth"
|
:max-time="query.trendChartEndYearMonth"
|
||||||
v-model:show="showCustomMonthRangeSheet"
|
v-model:show="showCustomMonthRangeSheet"
|
||||||
@@ -310,29 +310,33 @@
|
|||||||
|
|
||||||
<f7-actions close-by-outside-click close-on-escape :opened="showMoreActionSheet" @actions:closed="showMoreActionSheet = false">
|
<f7-actions close-by-outside-click close-on-escape :opened="showMoreActionSheet" @actions:closed="showMoreActionSheet = false">
|
||||||
<f7-actions-group>
|
<f7-actions-group>
|
||||||
<f7-actions-button @click="filterAccounts">{{ $t('Filter Accounts') }}</f7-actions-button>
|
<f7-actions-button @click="filterAccounts">{{ tt('Filter Accounts') }}</f7-actions-button>
|
||||||
<f7-actions-button @click="filterCategories">{{ $t('Filter Transaction Categories') }}</f7-actions-button>
|
<f7-actions-button @click="filterCategories">{{ tt('Filter Transaction Categories') }}</f7-actions-button>
|
||||||
<f7-actions-button @click="filterTags">{{ $t('Filter Transaction Tags') }}</f7-actions-button>
|
<f7-actions-button @click="filterTags">{{ tt('Filter Transaction Tags') }}</f7-actions-button>
|
||||||
</f7-actions-group>
|
</f7-actions-group>
|
||||||
<f7-actions-group>
|
<f7-actions-group>
|
||||||
<f7-actions-button @click="settings">{{ $t('Settings') }}</f7-actions-button>
|
<f7-actions-button @click="settings">{{ tt('Settings') }}</f7-actions-button>
|
||||||
</f7-actions-group>
|
</f7-actions-group>
|
||||||
<f7-actions-group>
|
<f7-actions-group>
|
||||||
<f7-actions-button bold close>{{ $t('Cancel') }}</f7-actions-button>
|
<f7-actions-button bold close>{{ tt('Cancel') }}</f7-actions-button>
|
||||||
</f7-actions-group>
|
</f7-actions-group>
|
||||||
</f7-actions>
|
</f7-actions>
|
||||||
</f7-page>
|
</f7-page>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
import { mapStores } from 'pinia';
|
import { ref, computed } from 'vue';
|
||||||
import { useSettingsStore } from '@/stores/setting.ts';
|
import type { Router } from 'framework7/types';
|
||||||
import { useUserStore } from '@/stores/user.ts';
|
|
||||||
|
import { useI18n } from '@/locales/helpers.ts';
|
||||||
|
import { useStatisticsTransactionPageBase } from '@/views/base/statistics/StatisticsTransactionPageBase.ts';
|
||||||
|
|
||||||
import { useAccountsStore } from '@/stores/account.ts';
|
import { useAccountsStore } from '@/stores/account.ts';
|
||||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
|
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
|
||||||
import { useStatisticsStore } from '@/stores/statistics.ts';
|
import { useStatisticsStore } from '@/stores/statistics.ts';
|
||||||
|
|
||||||
import { DateRangeScene, DateRange } from '@/core/datetime.ts';
|
import type { TypeAndDisplayName } from '@/core/base.ts';
|
||||||
|
import { type TimeRangeAndDateType, DateRangeScene, DateRange } from '@/core/datetime.ts';
|
||||||
import {
|
import {
|
||||||
StatisticsAnalysisType,
|
StatisticsAnalysisType,
|
||||||
CategoricalChartType,
|
CategoricalChartType,
|
||||||
@@ -340,7 +344,8 @@ import {
|
|||||||
ChartSortingType,
|
ChartSortingType,
|
||||||
ChartDateAggregationType
|
ChartDateAggregationType
|
||||||
} from '@/core/statistics.ts';
|
} from '@/core/statistics.ts';
|
||||||
import { getNameByKeyValue, limitText } from '@/lib/common.ts';
|
|
||||||
|
import { isString, isNumber } from '@/lib/common.ts';
|
||||||
import { formatPercent } from '@/lib/numeral.ts';
|
import { formatPercent } from '@/lib/numeral.ts';
|
||||||
import {
|
import {
|
||||||
getYearAndMonthFromUnixTime,
|
getYearAndMonthFromUnixTime,
|
||||||
@@ -350,338 +355,245 @@ import {
|
|||||||
getDateTypeByDateRange,
|
getDateTypeByDateRange,
|
||||||
getDateRangeByDateType
|
getDateRangeByDateType
|
||||||
} from '@/lib/datetime.ts';
|
} from '@/lib/datetime.ts';
|
||||||
import { scrollToSelectedItem } from '@/lib/ui/mobile.ts';
|
import { type Framework7Dom, useI18nUIComponents, scrollToSelectedItem } from '@/lib/ui/mobile.ts';
|
||||||
|
|
||||||
export default {
|
const props = defineProps<{
|
||||||
props: [
|
f7router: Router.Router;
|
||||||
'f7router'
|
}>();
|
||||||
],
|
|
||||||
data() {
|
const { tt, getAllCategoricalChartTypes } = useI18n();
|
||||||
return {
|
const { showToast, routeBackOnError } = useI18nUIComponents();
|
||||||
loading: true,
|
|
||||||
loadingError: null,
|
const {
|
||||||
reloading: false,
|
loading,
|
||||||
analysisType: StatisticsAnalysisType.CategoricalAnalysis,
|
analysisType,
|
||||||
trendDateAggregationType: ChartDateAggregationType.Default.type,
|
trendDateAggregationType,
|
||||||
showChartDataTypePopover: false,
|
defaultCurrency,
|
||||||
showSortingTypePopover: false,
|
firstDayOfWeek,
|
||||||
showDatePopover: false,
|
allDateRanges,
|
||||||
showDateAggregationPopover: false,
|
allSortingTypes,
|
||||||
showCustomDateRangeSheet: false,
|
allDateAggregationTypes,
|
||||||
showCustomMonthRangeSheet: false,
|
query,
|
||||||
showMoreActionSheet: false
|
queryChartDataCategory,
|
||||||
};
|
queryDateType,
|
||||||
},
|
queryStartTime,
|
||||||
computed: {
|
queryEndTime,
|
||||||
...mapStores(useSettingsStore, useUserStore, useAccountsStore, useTransactionCategoriesStore, useStatisticsStore),
|
queryDateRangeName,
|
||||||
defaultCurrency() {
|
queryChartDataTypeName,
|
||||||
return this.userStore.currentUserDefaultCurrency;
|
querySortingTypeName,
|
||||||
},
|
queryTrendDateAggregationTypeName,
|
||||||
defaultTrendDateAggregationType() {
|
isQueryDateRangeChanged,
|
||||||
return ChartDateAggregationType.Default.type;
|
canShiftDateRange,
|
||||||
},
|
showCustomDateRange,
|
||||||
firstDayOfWeek() {
|
showAmountInChart,
|
||||||
return this.userStore.currentUserFirstDayOfWeek;
|
totalAmountName,
|
||||||
},
|
translateNameInTrendsChart,
|
||||||
query() {
|
categoricalAnalysisData,
|
||||||
return this.statisticsStore.transactionStatisticsFilter;
|
trendsAnalysisData,
|
||||||
},
|
getDisplayAmount
|
||||||
queryChartDataCategory() {
|
} = useStatisticsTransactionPageBase();
|
||||||
return this.statisticsStore.categoricalAnalysisChartDataCategory;
|
|
||||||
},
|
const accountsStore = useAccountsStore();
|
||||||
queryChartType: {
|
const transactionCategoriesStore = useTransactionCategoriesStore();
|
||||||
get: function () {
|
const statisticsStore = useStatisticsStore();
|
||||||
if (this.analysisType === StatisticsAnalysisType.CategoricalAnalysis) {
|
|
||||||
return this.query.categoricalChartType;
|
const loadingError = ref<unknown | null>(null);
|
||||||
} else if (this.analysisType === StatisticsAnalysisType.TrendAnalysis) {
|
const reloading = ref<boolean>(false);
|
||||||
return this.query.trendChartType;
|
const showChartDataTypePopover = ref<boolean>(false);
|
||||||
} else {
|
const showSortingTypePopover = ref<boolean>(false);
|
||||||
return null;
|
const showDatePopover = ref<boolean>(false);
|
||||||
}
|
const showDateAggregationPopover = ref<boolean>(false);
|
||||||
},
|
const showCustomDateRangeSheet = ref<boolean>(false);
|
||||||
set: function(value) {
|
const showCustomMonthRangeSheet = ref<boolean>(false);
|
||||||
this.setChartType(value);
|
const showMoreActionSheet = ref<boolean>(false);
|
||||||
}
|
|
||||||
},
|
const allChartTypes = computed<TypeAndDisplayName[]>(() => {
|
||||||
queryChartDataTypeName() {
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||||
const queryChartDataTypeName = getNameByKeyValue(ChartDataType.values(), this.query.chartDataType, 'type', 'name', 'Statistics');
|
return getAllCategoricalChartTypes();
|
||||||
return this.$t(queryChartDataTypeName);
|
|
||||||
},
|
|
||||||
querySortingTypeName() {
|
|
||||||
const querySortingTypeName = getNameByKeyValue(ChartSortingType.values(), this.query.sortingType, 'type', 'name', 'System Default');
|
|
||||||
return this.$t(querySortingTypeName);
|
|
||||||
},
|
|
||||||
queryDateType() {
|
|
||||||
if (this.analysisType === StatisticsAnalysisType.CategoricalAnalysis) {
|
|
||||||
return this.query.categoricalChartDateType;
|
|
||||||
} else if (this.analysisType === StatisticsAnalysisType.TrendAnalysis) {
|
|
||||||
return this.query.trendChartDateType;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
queryTrendDateAggregationTypeName() {
|
|
||||||
return getNameByKeyValue(this.allDateAggregationTypes, this.trendDateAggregationType, 'type', 'displayName', '');
|
|
||||||
},
|
|
||||||
queryStartTime() {
|
|
||||||
if (this.analysisType === StatisticsAnalysisType.CategoricalAnalysis) {
|
|
||||||
return this.$locale.formatUnixTimeToLongDateTime(this.userStore, this.query.categoricalChartStartTime);
|
|
||||||
} else if (this.analysisType === StatisticsAnalysisType.TrendAnalysis) {
|
|
||||||
return this.$locale.formatUnixTimeToLongYearMonth(this.userStore, getYearMonthFirstUnixTime(this.query.trendChartStartYearMonth));
|
|
||||||
} else {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
queryEndTime() {
|
|
||||||
if (this.analysisType === StatisticsAnalysisType.CategoricalAnalysis) {
|
|
||||||
return this.$locale.formatUnixTimeToLongDateTime(this.userStore, this.query.categoricalChartEndTime);
|
|
||||||
} else if (this.analysisType === StatisticsAnalysisType.TrendAnalysis) {
|
|
||||||
return this.$locale.formatUnixTimeToLongYearMonth(this.userStore, getYearMonthLastUnixTime(this.query.trendChartEndYearMonth));
|
|
||||||
} else {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
allAnalysisTypes() {
|
|
||||||
return StatisticsAnalysisType;
|
|
||||||
},
|
|
||||||
allChartTypes() {
|
|
||||||
if (this.analysisType === StatisticsAnalysisType.CategoricalAnalysis) {
|
|
||||||
return this.$locale.getAllCategoricalChartTypes();
|
|
||||||
} else {
|
} else {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
allCategoricalChartTypes() {
|
|
||||||
return CategoricalChartType.all();
|
const queryChartType = computed<number | undefined>({
|
||||||
},
|
get: () => {
|
||||||
allChartDataTypes() {
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||||
return ChartDataType.all();
|
return query.value.categoricalChartType;
|
||||||
},
|
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
||||||
allSortingTypes() {
|
return query.value.trendChartType;
|
||||||
return this.$locale.getAllStatisticsSortingTypes();
|
|
||||||
},
|
|
||||||
allDateAggregationTypes() {
|
|
||||||
return this.$locale.getAllStatisticsDateAggregationTypes();
|
|
||||||
},
|
|
||||||
allDateRanges() {
|
|
||||||
return DateRange.all();
|
|
||||||
},
|
|
||||||
allDateRangesArray() {
|
|
||||||
if (this.analysisType === StatisticsAnalysisType.CategoricalAnalysis) {
|
|
||||||
return this.$locale.getAllDateRanges(DateRangeScene.Normal, true);
|
|
||||||
} else if (this.analysisType === StatisticsAnalysisType.TrendAnalysis) {
|
|
||||||
return this.$locale.getAllDateRanges(DateRangeScene.TrendAnalysis, true);
|
|
||||||
} else {
|
} else {
|
||||||
return [];
|
return undefined;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
showAccountBalance() {
|
set: (value: number | undefined) => {
|
||||||
return this.settingsStore.appSettings.showAccountBalance;
|
setChartType(value);
|
||||||
},
|
}
|
||||||
totalAmountName() {
|
});
|
||||||
if (this.query.chartDataType === ChartDataType.IncomeByAccount.type
|
|
||||||
|| this.query.chartDataType === ChartDataType.IncomeByPrimaryCategory.type
|
function getTransactionItemLinkUrl(itemId: string, dateRange?: TimeRangeAndDateType): string {
|
||||||
|| this.query.chartDataType === ChartDataType.IncomeBySecondaryCategory.type) {
|
return `/transaction/list?${statisticsStore.getTransactionListPageParams(analysisType.value, itemId, dateRange)}`;
|
||||||
return this.$t('Total Income');
|
|
||||||
} else if (this.query.chartDataType === ChartDataType.ExpenseByAccount.type
|
|
||||||
|| this.query.chartDataType === ChartDataType.ExpenseByPrimaryCategory.type
|
|
||||||
|| this.query.chartDataType === ChartDataType.ExpenseBySecondaryCategory.type) {
|
|
||||||
return this.$t('Total Expense');
|
|
||||||
} else if (this.query.chartDataType === ChartDataType.AccountTotalAssets.type) {
|
|
||||||
return this.$t('Total Assets');
|
|
||||||
} else if (this.query.chartDataType === ChartDataType.AccountTotalLiabilities.type) {
|
|
||||||
return this.$t('Total Liabilities');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.$t('Total Amount');
|
function init(): void {
|
||||||
},
|
statisticsStore.initTransactionStatisticsFilter(analysisType.value);
|
||||||
categoricalAnalysisData() {
|
|
||||||
return this.statisticsStore.categoricalAnalysisData;
|
|
||||||
},
|
|
||||||
trendsAnalysisData() {
|
|
||||||
return this.statisticsStore.trendsAnalysisData;
|
|
||||||
},
|
|
||||||
translateNameInTrendsChart() {
|
|
||||||
return this.query.chartDataType === ChartDataType.TotalExpense.type ||
|
|
||||||
this.query.chartDataType === ChartDataType.TotalIncome.type ||
|
|
||||||
this.query.chartDataType === ChartDataType.TotalBalance.type;
|
|
||||||
},
|
|
||||||
showAmountInChart() {
|
|
||||||
if (!this.showAccountBalance
|
|
||||||
&& (this.query.chartDataType === ChartDataType.AccountTotalAssets.type || this.query.chartDataType === ChartDataType.AccountTotalLiabilities.type)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
const self = this;
|
|
||||||
|
|
||||||
self.statisticsStore.initTransactionStatisticsFilter(self.analysisType);
|
|
||||||
|
|
||||||
Promise.all([
|
Promise.all([
|
||||||
self.accountsStore.loadAllAccounts({ force: false }),
|
accountsStore.loadAllAccounts({ force: false }),
|
||||||
self.transactionCategoriesStore.loadAllCategories({ force: false })
|
transactionCategoriesStore.loadAllCategories({ force: false })
|
||||||
]).then(() => {
|
]).then(() => {
|
||||||
if (self.analysisType === StatisticsAnalysisType.CategoricalAnalysis) {
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||||
return self.statisticsStore.loadCategoricalAnalysis({
|
return statisticsStore.loadCategoricalAnalysis({
|
||||||
force: false
|
force: false
|
||||||
});
|
}) as Promise<unknown>;
|
||||||
} else if (self.analysisType === StatisticsAnalysisType.TrendAnalysis) {
|
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
||||||
return self.statisticsStore.loadTrendAnalysis({
|
return statisticsStore.loadTrendAnalysis({
|
||||||
force: false
|
force: false
|
||||||
});
|
}) as Promise<unknown>;
|
||||||
|
} else {
|
||||||
|
return Promise.reject('An error occurred');
|
||||||
}
|
}
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
self.loading = false;
|
loading.value = false;
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
if (error.processed) {
|
if (error.processed) {
|
||||||
self.loading = false;
|
loading.value = false;
|
||||||
} else {
|
} else {
|
||||||
self.loadingError = error;
|
loadingError.value = error;
|
||||||
self.$toast(error.message || error);
|
showToast(error.message || error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
onPageAfterIn() {
|
|
||||||
if (this.statisticsStore.transactionStatisticsStateInvalid && !this.loading) {
|
|
||||||
this.reload(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$routeBackOnError(this.f7router, 'loadingError');
|
function reload(done?: () => void): void {
|
||||||
},
|
|
||||||
reload(done) {
|
|
||||||
const self = this;
|
|
||||||
const force = !!done;
|
const force = !!done;
|
||||||
let dispatchPromise = null;
|
let dispatchPromise: Promise<unknown> | null = null;
|
||||||
|
|
||||||
self.reloading = true;
|
reloading.value = true;
|
||||||
|
|
||||||
if (self.query.chartDataType === ChartDataType.ExpenseByAccount.type ||
|
if (query.value.chartDataType === ChartDataType.ExpenseByAccount.type ||
|
||||||
self.query.chartDataType === ChartDataType.ExpenseByPrimaryCategory.type ||
|
query.value.chartDataType === ChartDataType.ExpenseByPrimaryCategory.type ||
|
||||||
self.query.chartDataType === ChartDataType.ExpenseBySecondaryCategory.type ||
|
query.value.chartDataType === ChartDataType.ExpenseBySecondaryCategory.type ||
|
||||||
self.query.chartDataType === ChartDataType.IncomeByAccount.type ||
|
query.value.chartDataType === ChartDataType.IncomeByAccount.type ||
|
||||||
self.query.chartDataType === ChartDataType.IncomeByPrimaryCategory.type ||
|
query.value.chartDataType === ChartDataType.IncomeByPrimaryCategory.type ||
|
||||||
self.query.chartDataType === ChartDataType.IncomeBySecondaryCategory.type ||
|
query.value.chartDataType === ChartDataType.IncomeBySecondaryCategory.type ||
|
||||||
self.query.chartDataType === ChartDataType.TotalExpense.type ||
|
query.value.chartDataType === ChartDataType.TotalExpense.type ||
|
||||||
self.query.chartDataType === ChartDataType.TotalIncome.type ||
|
query.value.chartDataType === ChartDataType.TotalIncome.type ||
|
||||||
self.query.chartDataType === ChartDataType.TotalBalance.type) {
|
query.value.chartDataType === ChartDataType.TotalBalance.type) {
|
||||||
if (self.analysisType === StatisticsAnalysisType.CategoricalAnalysis) {
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||||
dispatchPromise = self.statisticsStore.loadCategoricalAnalysis({
|
dispatchPromise = statisticsStore.loadCategoricalAnalysis({
|
||||||
force: force
|
force: force
|
||||||
});
|
});
|
||||||
} else if (self.analysisType === StatisticsAnalysisType.TrendAnalysis) {
|
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
||||||
dispatchPromise = self.statisticsStore.loadTrendAnalysis({
|
dispatchPromise = statisticsStore.loadTrendAnalysis({
|
||||||
force: force
|
force: force
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (self.query.chartDataType === ChartDataType.AccountTotalAssets.type ||
|
} else if (query.value.chartDataType === ChartDataType.AccountTotalAssets.type ||
|
||||||
self.query.chartDataType === ChartDataType.AccountTotalLiabilities.type) {
|
query.value.chartDataType === ChartDataType.AccountTotalLiabilities.type) {
|
||||||
dispatchPromise = self.accountsStore.loadAllAccounts({
|
dispatchPromise = accountsStore.loadAllAccounts({
|
||||||
force: force
|
force: force
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dispatchPromise) {
|
if (dispatchPromise) {
|
||||||
dispatchPromise.then(() => {
|
dispatchPromise.then(() => {
|
||||||
self.reloading = false;
|
reloading.value = false;
|
||||||
|
|
||||||
if (done) {
|
done?.();
|
||||||
done();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (force) {
|
if (force) {
|
||||||
self.$toast('Data has been updated');
|
showToast('Data has been updated');
|
||||||
}
|
}
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
self.reloading = false;
|
reloading.value = false;
|
||||||
|
|
||||||
if (done) {
|
done?.();
|
||||||
done();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!error.processed) {
|
if (!error.processed) {
|
||||||
self.$toast(error.message || error);
|
showToast(error.message || error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
self.reloading = false;
|
reloading.value = false;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
setChartType(chartType) {
|
|
||||||
if (this.analysisType === StatisticsAnalysisType.CategoricalAnalysis) {
|
function setChartType(type?: number): void {
|
||||||
this.statisticsStore.updateTransactionStatisticsFilter({
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||||
categoricalChartType: chartType
|
statisticsStore.updateTransactionStatisticsFilter({
|
||||||
|
categoricalChartType: type
|
||||||
});
|
});
|
||||||
} else if (this.analysisType === StatisticsAnalysisType.TrendAnalysis) {
|
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
||||||
this.statisticsStore.updateTransactionStatisticsFilter({
|
statisticsStore.updateTransactionStatisticsFilter({
|
||||||
trendChartType: chartType
|
trendChartType: type
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
setChartDataType(analysisType, chartDataType) {
|
|
||||||
|
function setChartDataType(type: number, chartDataType: number): void {
|
||||||
let analysisTypeChanged = false;
|
let analysisTypeChanged = false;
|
||||||
|
|
||||||
if (this.analysisType !== analysisType) {
|
if (analysisType.value !== type) {
|
||||||
if (!ChartDataType.isAvailableForAnalysisType(this.queryChartDataType, analysisType)) {
|
if (!ChartDataType.isAvailableForAnalysisType(query.value.chartDataType, type)) {
|
||||||
this.statisticsStore.updateTransactionStatisticsFilter({
|
statisticsStore.updateTransactionStatisticsFilter({
|
||||||
chartDataType: ChartDataType.Default.type
|
chartDataType: ChartDataType.Default.type
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.analysisType = analysisType;
|
analysisType.value = type;
|
||||||
this.statisticsStore.updateTransactionStatisticsInvalidState(true);
|
statisticsStore.updateTransactionStatisticsInvalidState(true);
|
||||||
analysisTypeChanged = true;
|
analysisTypeChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.statisticsStore.updateTransactionStatisticsFilter({
|
statisticsStore.updateTransactionStatisticsFilter({
|
||||||
chartDataType: chartDataType
|
chartDataType: chartDataType
|
||||||
});
|
});
|
||||||
|
|
||||||
this.showChartDataTypePopover = false;
|
showChartDataTypePopover.value = false;
|
||||||
|
|
||||||
if (analysisTypeChanged) {
|
if (analysisTypeChanged) {
|
||||||
this.reload(null);
|
reload();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
setSortingType(sortingType) {
|
|
||||||
if (sortingType < ChartSortingType.Amount.type || sortingType > ChartSortingType.Name.type) {
|
function setSortingType(type: number): void {
|
||||||
this.showSortingTypePopover = false;
|
if (type < ChartSortingType.Amount.type || type > ChartSortingType.Name.type) {
|
||||||
|
showSortingTypePopover.value = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.statisticsStore.updateTransactionStatisticsFilter({
|
statisticsStore.updateTransactionStatisticsFilter({
|
||||||
sortingType: sortingType
|
sortingType: type
|
||||||
});
|
});
|
||||||
|
|
||||||
this.showSortingTypePopover = false;
|
showSortingTypePopover.value = false;
|
||||||
},
|
}
|
||||||
setTrendDateAggregationType(aggregationType) {
|
|
||||||
this.trendDateAggregationType = aggregationType;
|
function setTrendDateAggregationType(type: number): void {
|
||||||
this.showDateAggregationPopover = false;
|
trendDateAggregationType.value = type;
|
||||||
},
|
showDateAggregationPopover.value = false;
|
||||||
setDateFilter(dateType) {
|
}
|
||||||
if (this.analysisType === StatisticsAnalysisType.CategoricalAnalysis) {
|
|
||||||
if (dateType === this.allDateRanges.Custom.type) { // Custom
|
function setDateFilter(dateType: number): void {
|
||||||
this.showCustomDateRangeSheet = true;
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||||
this.showDatePopover = false;
|
if (dateType === DateRange.Custom.type) { // Custom
|
||||||
|
showCustomDateRangeSheet.value = true;
|
||||||
|
showDatePopover.value = false;
|
||||||
return;
|
return;
|
||||||
} else if (this.query.categoricalChartDateType === dateType) {
|
} else if (query.value.categoricalChartDateType === dateType) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (this.analysisType === StatisticsAnalysisType.TrendAnalysis) {
|
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
||||||
if (dateType === this.allDateRanges.Custom.type) { // Custom
|
if (dateType === DateRange.Custom.type) { // Custom
|
||||||
this.showCustomMonthRangeSheet = true;
|
showCustomMonthRangeSheet.value = true;
|
||||||
this.showDatePopover = false;
|
showDatePopover.value = false;
|
||||||
return;
|
return;
|
||||||
} else if (this.query.trendChartDateType === dateType) {
|
} else if (query.value.trendChartDateType === dateType) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const dateRange = getDateRangeByDateType(dateType, this.firstDayOfWeek);
|
const dateRange = getDateRangeByDateType(dateType, firstDayOfWeek.value);
|
||||||
|
|
||||||
if (!dateRange) {
|
if (!dateRange) {
|
||||||
return;
|
return;
|
||||||
@@ -689,100 +601,80 @@ export default {
|
|||||||
|
|
||||||
let changed = false;
|
let changed = false;
|
||||||
|
|
||||||
if (this.analysisType === StatisticsAnalysisType.CategoricalAnalysis) {
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||||
changed = this.statisticsStore.updateTransactionStatisticsFilter({
|
changed = statisticsStore.updateTransactionStatisticsFilter({
|
||||||
categoricalChartDateType: dateRange.dateType,
|
categoricalChartDateType: dateRange.dateType,
|
||||||
categoricalChartStartTime: dateRange.minTime,
|
categoricalChartStartTime: dateRange.minTime,
|
||||||
categoricalChartEndTime: dateRange.maxTime
|
categoricalChartEndTime: dateRange.maxTime
|
||||||
});
|
});
|
||||||
} else if (this.analysisType === StatisticsAnalysisType.TrendAnalysis) {
|
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
||||||
changed = this.statisticsStore.updateTransactionStatisticsFilter({
|
changed = statisticsStore.updateTransactionStatisticsFilter({
|
||||||
trendChartDateType: dateRange.dateType,
|
trendChartDateType: dateRange.dateType,
|
||||||
trendChartStartYearMonth: getYearAndMonthFromUnixTime(dateRange.minTime),
|
trendChartStartYearMonth: getYearAndMonthFromUnixTime(dateRange.minTime),
|
||||||
trendChartEndYearMonth: getYearAndMonthFromUnixTime(dateRange.maxTime)
|
trendChartEndYearMonth: getYearAndMonthFromUnixTime(dateRange.maxTime)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.showDatePopover = false;
|
showDatePopover.value = false;
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
this.reload(null);
|
reload();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
setCustomDateFilter(startTime, endTime) {
|
|
||||||
|
function setCustomDateFilter(startTime: number | string, endTime: number | string): void {
|
||||||
if (!startTime || !endTime) {
|
if (!startTime || !endTime) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let changed = false;
|
let changed = false;
|
||||||
|
|
||||||
if (this.analysisType === StatisticsAnalysisType.CategoricalAnalysis) {
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis && isNumber(startTime) && isNumber(endTime)) {
|
||||||
const chartDateType = getDateTypeByDateRange(startTime, endTime, this.firstDayOfWeek, DateRangeScene.Normal);
|
const chartDateType = getDateTypeByDateRange(startTime, endTime, firstDayOfWeek.value, DateRangeScene.Normal);
|
||||||
|
|
||||||
changed = this.statisticsStore.updateTransactionStatisticsFilter({
|
changed = statisticsStore.updateTransactionStatisticsFilter({
|
||||||
categoricalChartDateType: chartDateType,
|
categoricalChartDateType: chartDateType,
|
||||||
categoricalChartStartTime: startTime,
|
categoricalChartStartTime: startTime,
|
||||||
categoricalChartEndTime: endTime
|
categoricalChartEndTime: endTime
|
||||||
});
|
});
|
||||||
|
|
||||||
this.showCustomDateRangeSheet = false;
|
showCustomDateRangeSheet.value = false;
|
||||||
} else if (this.analysisType === StatisticsAnalysisType.TrendAnalysis) {
|
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis && isString(startTime) && isString(endTime)) {
|
||||||
const chartDateType = getDateTypeByDateRange(getYearMonthFirstUnixTime(startTime), getYearMonthLastUnixTime(endTime), this.firstDayOfWeek, DateRangeScene.TrendAnalysis);
|
const chartDateType = getDateTypeByDateRange(getYearMonthFirstUnixTime(startTime), getYearMonthLastUnixTime(endTime), firstDayOfWeek.value, DateRangeScene.TrendAnalysis);
|
||||||
|
|
||||||
changed = this.statisticsStore.updateTransactionStatisticsFilter({
|
changed = statisticsStore.updateTransactionStatisticsFilter({
|
||||||
trendChartDateType: chartDateType,
|
trendChartDateType: chartDateType,
|
||||||
trendChartStartYearMonth: startTime,
|
trendChartStartYearMonth: startTime,
|
||||||
trendChartEndYearMonth: endTime
|
trendChartEndYearMonth: endTime
|
||||||
});
|
});
|
||||||
|
|
||||||
this.showCustomMonthRangeSheet = false;
|
showCustomMonthRangeSheet.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
this.reload(null);
|
reload();
|
||||||
}
|
}
|
||||||
},
|
|
||||||
showCustomDateRange() {
|
|
||||||
if (this.analysisType === StatisticsAnalysisType.CategoricalAnalysis) {
|
|
||||||
return this.query.categoricalChartDateType === this.allDateRanges.Custom.type && this.query.categoricalChartStartTime && this.query.categoricalChartEndTime;
|
|
||||||
} else if (this.analysisType === StatisticsAnalysisType.TrendAnalysis) {
|
|
||||||
return this.query.trendChartDateType === this.allDateRanges.Custom.type && this.query.trendChartStartYearMonth && this.query.trendChartEndYearMonth;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
canShiftDateRange(query) {
|
|
||||||
if (query.chartDataType === ChartDataType.AccountTotalAssets.type || query.chartDataType === ChartDataType.AccountTotalLiabilities.type) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.analysisType === StatisticsAnalysisType.CategoricalAnalysis) {
|
function shiftDateRange(scale: number): void {
|
||||||
return query.categoricalChartDateType !== this.allDateRanges.All.type;
|
if (query.value.categoricalChartDateType === DateRange.All.type) {
|
||||||
} else if (this.analysisType === StatisticsAnalysisType.TrendAnalysis) {
|
|
||||||
return query.trendChartDateType !== this.allDateRanges.All.type;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
shiftDateRange(query, scale) {
|
|
||||||
if (this.query.categoricalChartDateType === this.allDateRanges.All.type) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let changed = false;
|
let changed = false;
|
||||||
|
|
||||||
if (this.analysisType === StatisticsAnalysisType.CategoricalAnalysis) {
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||||
const newDateRange = getShiftedDateRangeAndDateType(query.categoricalChartStartTime, query.categoricalChartEndTime, scale, this.firstDayOfWeek, DateRangeScene.Normal);
|
const newDateRange = getShiftedDateRangeAndDateType(query.value.categoricalChartStartTime, query.value.categoricalChartEndTime, scale, firstDayOfWeek.value, DateRangeScene.Normal);
|
||||||
|
|
||||||
changed = this.statisticsStore.updateTransactionStatisticsFilter({
|
changed = statisticsStore.updateTransactionStatisticsFilter({
|
||||||
categoricalChartDateType: newDateRange.dateType,
|
categoricalChartDateType: newDateRange.dateType,
|
||||||
categoricalChartStartTime: newDateRange.minTime,
|
categoricalChartStartTime: newDateRange.minTime,
|
||||||
categoricalChartEndTime: newDateRange.maxTime
|
categoricalChartEndTime: newDateRange.maxTime
|
||||||
});
|
});
|
||||||
} else if (this.analysisType === StatisticsAnalysisType.TrendAnalysis) {
|
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
||||||
const newDateRange = getShiftedDateRangeAndDateType(getYearMonthFirstUnixTime(query.trendChartStartYearMonth), getYearMonthLastUnixTime(query.trendChartEndYearMonth), scale, this.firstDayOfWeek, DateRangeScene.TrendAnalysis);
|
const newDateRange = getShiftedDateRangeAndDateType(getYearMonthFirstUnixTime(query.value.trendChartStartYearMonth), getYearMonthLastUnixTime(query.value.trendChartEndYearMonth), scale, firstDayOfWeek.value, DateRangeScene.TrendAnalysis);
|
||||||
|
|
||||||
changed = this.statisticsStore.updateTransactionStatisticsFilter({
|
changed = statisticsStore.updateTransactionStatisticsFilter({
|
||||||
trendChartDateType: newDateRange.dateType,
|
trendChartDateType: newDateRange.dateType,
|
||||||
trendChartStartYearMonth: getYearAndMonthFromUnixTime(newDateRange.minTime),
|
trendChartStartYearMonth: getYearAndMonthFromUnixTime(newDateRange.minTime),
|
||||||
trendChartEndYearMonth: getYearAndMonthFromUnixTime(newDateRange.maxTime)
|
trendChartEndYearMonth: getYearAndMonthFromUnixTime(newDateRange.maxTime)
|
||||||
@@ -790,71 +682,47 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
this.reload(null);
|
reload();
|
||||||
}
|
}
|
||||||
},
|
|
||||||
dateRangeName(query) {
|
|
||||||
if (this.query.chartDataType === ChartDataType.AccountTotalAssets.type ||
|
|
||||||
this.query.chartDataType === ChartDataType.AccountTotalLiabilities.type) {
|
|
||||||
return this.$t(this.allDateRanges.All.name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.analysisType === StatisticsAnalysisType.CategoricalAnalysis) {
|
function filterAccounts(): void {
|
||||||
return this.$locale.getDateRangeDisplayName(this.userStore, query.categoricalChartDateType, query.categoricalChartStartTime, query.categoricalChartEndTime);
|
props.f7router.navigate('/settings/filter/account?type=statisticsCurrent');
|
||||||
} else if (this.analysisType === StatisticsAnalysisType.TrendAnalysis) {
|
|
||||||
return this.$locale.getDateRangeDisplayName(this.userStore, query.trendChartDateType, getYearMonthFirstUnixTime(query.trendChartStartYearMonth), getYearMonthLastUnixTime(query.trendChartEndYearMonth));
|
|
||||||
} else {
|
|
||||||
return '';
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
clickPieChartItem(item) {
|
function filterCategories(): void {
|
||||||
this.f7router.navigate(this.getTransactionItemLinkUrl(item.id));
|
props.f7router.navigate('/settings/filter/category?type=statisticsCurrent');
|
||||||
},
|
}
|
||||||
clickTrendChartItem(item) {
|
|
||||||
this.f7router.navigate(this.getTransactionItemLinkUrl(item.itemId, item.dateRange));
|
function filterTags(): void {
|
||||||
},
|
props.f7router.navigate('/settings/filter/tag?type=statisticsCurrent');
|
||||||
filterAccounts() {
|
}
|
||||||
this.f7router.navigate('/settings/filter/account?type=statisticsCurrent');
|
|
||||||
},
|
function settings(): void {
|
||||||
filterCategories() {
|
props.f7router.navigate('/statistic/settings');
|
||||||
this.f7router.navigate('/settings/filter/category?type=statisticsCurrent');
|
}
|
||||||
},
|
|
||||||
filterTags() {
|
function scrollPopoverToSelectedItem(event: { $el: Framework7Dom }): void {
|
||||||
this.f7router.navigate('/settings/filter/tag?type=statisticsCurrent');
|
|
||||||
},
|
|
||||||
settings() {
|
|
||||||
this.f7router.navigate('/statistic/settings');
|
|
||||||
},
|
|
||||||
scrollPopoverToSelectedItem(event) {
|
|
||||||
scrollToSelectedItem(event.$el, '.popover-inner', 'li.list-item-selected');
|
scrollToSelectedItem(event.$el, '.popover-inner', 'li.list-item-selected');
|
||||||
},
|
|
||||||
getDisplayAmount(amount, currency, textLimit) {
|
|
||||||
amount = this.getDisplayCurrency(amount, currency);
|
|
||||||
|
|
||||||
if (!this.showAccountBalance
|
|
||||||
&& (this.query.chartDataType === ChartDataType.AccountTotalAssets.type
|
|
||||||
|| this.query.chartDataType === ChartDataType.AccountTotalLiabilities.type)
|
|
||||||
) {
|
|
||||||
return '***';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (textLimit) {
|
function onClickPieChartItem(item: Record<string, unknown>): void {
|
||||||
return limitText(amount, textLimit);
|
props.f7router.navigate(getTransactionItemLinkUrl(item['id'] as string));
|
||||||
}
|
}
|
||||||
|
|
||||||
return amount;
|
function onClickTrendChartItem(item: { itemId: string, dateRange: TimeRangeAndDateType }): void {
|
||||||
},
|
props.f7router.navigate(getTransactionItemLinkUrl(item.itemId, item.dateRange));
|
||||||
getDisplayCurrency(value, currencyCode) {
|
|
||||||
return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, currencyCode);
|
|
||||||
},
|
|
||||||
getDisplayPercent(value, precision, lowPrecisionValue) {
|
|
||||||
return formatPercent(value, precision, lowPrecisionValue);
|
|
||||||
},
|
|
||||||
getTransactionItemLinkUrl(itemId, dateRange) {
|
|
||||||
return `/transaction/list?${this.statisticsStore.getTransactionListPageParams(this.analysisType, itemId, dateRange)}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onPageAfterIn(): void {
|
||||||
|
if (statisticsStore.transactionStatisticsStateInvalid && !loading.value) {
|
||||||
|
reload();
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
routeBackOnError(props.f7router, loadingError);
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
Reference in New Issue
Block a user