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;
|
||||
}
|
||||
|
||||
public static values(analysisType: StatisticsAnalysisType | undefined): ChartDataType[] {
|
||||
public static values(analysisType?: StatisticsAnalysisType): ChartDataType[] {
|
||||
if (analysisType === undefined) {
|
||||
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
|
||||
export function isFunction(val: unknown): val is Function {
|
||||
@@ -37,7 +37,7 @@ export function isBoolean(val: unknown): val is boolean {
|
||||
return typeof(val) === 'boolean';
|
||||
}
|
||||
|
||||
export function isYearMonth(val: unknown): boolean {
|
||||
export function isYearMonth(val: unknown): val is string {
|
||||
if (!isString(val)) {
|
||||
return false;
|
||||
}
|
||||
@@ -312,6 +312,16 @@ export function getItemByKeyValue<T>(src: Record<string, T>[] | Record<string, R
|
||||
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 {
|
||||
for (const item of items) {
|
||||
if (item.type === type) {
|
||||
|
||||
+54
-1
@@ -105,13 +105,17 @@ import {
|
||||
isPM,
|
||||
formatUnixTime,
|
||||
formatCurrentTime,
|
||||
parseDateFromUnixTime,
|
||||
getYear,
|
||||
getTimezoneOffset,
|
||||
getTimezoneOffsetMinutes,
|
||||
getBrowserTimezoneOffset,
|
||||
getBrowserTimezoneOffsetMinutes,
|
||||
getTimeDifferenceHoursAndMinutes,
|
||||
getDateTimeFormatType,
|
||||
getRecentMonthDateRanges
|
||||
getRecentMonthDateRanges,
|
||||
isDateRangeMatchFullYears,
|
||||
isDateRangeMatchFullMonths
|
||||
} from '@/lib/datetime.ts';
|
||||
|
||||
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 {
|
||||
const defaultTimezoneOffset = getTimezoneOffsetMinutes();
|
||||
const offsetTime = getTimeDifferenceHoursAndMinutes(utcOffset - defaultTimezoneOffset);
|
||||
@@ -1486,6 +1538,7 @@ export function useI18n() {
|
||||
formatUnixTimeToLongTime: (unixTime: number, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, getLocalizedLongTimeFormat(), utcOffset, currentUtcOffset),
|
||||
formatUnixTimeToShortTime: (unixTime: number, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, getLocalizedShortTimeFormat(), utcOffset, currentUtcOffset),
|
||||
formatYearQuarter,
|
||||
formatDateRange,
|
||||
getTimezoneDifferenceDisplayText,
|
||||
appendDigitGroupingSymbol: getNumberWithDigitGroupingSymbol,
|
||||
parseAmount: getParsedAmountNumber,
|
||||
|
||||
@@ -101,7 +101,24 @@ interface WritableTransactioTrendsAnalysisDataItem {
|
||||
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;
|
||||
categoricalChartType: number;
|
||||
categoricalChartDateType: number;
|
||||
@@ -672,7 +689,7 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
||||
transactionStatisticsStateInvalid.value = true;
|
||||
}
|
||||
|
||||
function initTransactionStatisticsFilter(analysisType: StatisticsAnalysisType, filter?: TransactionStatisticsFilter): void {
|
||||
function initTransactionStatisticsFilter(analysisType: StatisticsAnalysisType, filter?: TransactionStatisticsPartialFilter): void {
|
||||
if (filter && isInteger(filter.chartDataType)) {
|
||||
transactionStatisticsFilter.value.chartDataType = filter.chartDataType;
|
||||
} else {
|
||||
@@ -810,7 +827,7 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
||||
}
|
||||
}
|
||||
|
||||
function updateTransactionStatisticsFilter(filter: TransactionStatisticsFilter): boolean {
|
||||
function updateTransactionStatisticsFilter(filter: TransactionStatisticsPartialFilter): boolean {
|
||||
let changed = false;
|
||||
|
||||
if (filter && isInteger(filter.chartDataType) && transactionStatisticsFilter.value.chartDataType !== filter.chartDataType) {
|
||||
@@ -943,7 +960,7 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
||||
return querys.join('&');
|
||||
}
|
||||
|
||||
function getTransactionListPageParams(analysisType: StatisticsAnalysisType, itemId: string, dateRange: TimeRangeAndDateType): string {
|
||||
function getTransactionListPageParams(analysisType: StatisticsAnalysisType, itemId: string, dateRange?: TimeRangeAndDateType): string {
|
||||
const querys: string[] = [];
|
||||
|
||||
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
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user