add insights & explore page

This commit is contained in:
MaysWind
2025-12-18 00:49:14 +08:00
parent 861e4c036b
commit e9b4392163
43 changed files with 3579 additions and 43 deletions
+4 -4
View File
@@ -21,7 +21,7 @@ export function* reversedItemAndIndex<T>(arr: T[]): Iterable<[T, number]> {
}
}
export function* entries<K extends string | number | symbol, V>(obj: Record<K, V>): Iterable<[string, V]> {
export function* entries<K extends string | number | symbol, V>(obj: Record<K, V> | PartialRecord<K, V>): Iterable<[string, V]> {
for (const key in obj) {
if (!Object.prototype.hasOwnProperty.call(obj, key)) {
continue;
@@ -31,7 +31,7 @@ export function* entries<K extends string | number | symbol, V>(obj: Record<K, V
}
}
export function* keys<K extends string | number | symbol, V>(obj: Record<K, V>): Iterable<string> {
export function* keys<K extends string | number | symbol, V>(obj: Record<K, V> | PartialRecord<K, V>): Iterable<string> {
for (const key in obj) {
if (!Object.prototype.hasOwnProperty.call(obj, key)) {
continue;
@@ -41,7 +41,7 @@ export function* keys<K extends string | number | symbol, V>(obj: Record<K, V>):
}
}
export function* keysIfValueEquals<K extends string | number | symbol, V>(obj: Record<K, V>, value: V): Iterable<string> {
export function* keysIfValueEquals<K extends string | number | symbol, V>(obj: Record<K, V> | PartialRecord<K, V>, value: V): Iterable<string> {
for (const key in obj) {
if (!Object.prototype.hasOwnProperty.call(obj, key)) {
continue;
@@ -55,7 +55,7 @@ export function* keysIfValueEquals<K extends string | number | symbol, V>(obj: R
}
}
export function* values<K extends string | number | symbol, V>(obj: Record<K, V>): Iterable<V> {
export function* values<K extends string | number | symbol, V>(obj: Record<K, V> | PartialRecord<K, V>): Iterable<V> {
for (const key in obj) {
if (!Object.prototype.hasOwnProperty.call(obj, key)) {
continue;
+22 -21
View File
@@ -589,7 +589,8 @@ export class ShortTimeFormat implements TimeFormat {
export enum DateRangeScene {
Normal = 0,
TrendAnalysis = 1,
AssetTrends = 2
AssetTrends = 2,
InsightsExplore = 3
}
export class DateRange implements TypeAndName {
@@ -597,38 +598,38 @@ export class DateRange implements TypeAndName {
private static readonly allInstancesByType: Record<number, DateRange> = {};
// All date range
public static readonly All = new DateRange(0, 'All', false, false, DateRangeScene.Normal, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends);
public static readonly All = new DateRange(0, 'All', false, false, DateRangeScene.Normal, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplore);
// Date ranges for normal scene only
public static readonly Today = new DateRange(1, 'Today', false, false, DateRangeScene.Normal);
public static readonly Yesterday = new DateRange(2, 'Yesterday', false, false, DateRangeScene.Normal);
public static readonly LastSevenDays = new DateRange(3, 'Recent 7 days', false, false, DateRangeScene.Normal, DateRangeScene.AssetTrends);
public static readonly LastThirtyDays = new DateRange(4, 'Recent 30 days', false, false, DateRangeScene.Normal, DateRangeScene.AssetTrends);
public static readonly ThisWeek = new DateRange(5, 'This week', false, false, DateRangeScene.Normal, DateRangeScene.AssetTrends);
public static readonly LastWeek = new DateRange(6, 'Last week', false, false, DateRangeScene.Normal, DateRangeScene.AssetTrends);
public static readonly ThisMonth = new DateRange(7, 'This month', false, false, DateRangeScene.Normal, DateRangeScene.AssetTrends);
public static readonly LastMonth = new DateRange(8, 'Last month', false, false, DateRangeScene.Normal, DateRangeScene.AssetTrends);
public static readonly Today = new DateRange(1, 'Today', false, false, DateRangeScene.Normal, DateRangeScene.InsightsExplore);
public static readonly Yesterday = new DateRange(2, 'Yesterday', false, false, DateRangeScene.Normal, DateRangeScene.InsightsExplore);
public static readonly LastSevenDays = new DateRange(3, 'Recent 7 days', false, false, DateRangeScene.Normal, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplore);
public static readonly LastThirtyDays = new DateRange(4, 'Recent 30 days', false, false, DateRangeScene.Normal, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplore);
public static readonly ThisWeek = new DateRange(5, 'This week', false, false, DateRangeScene.Normal, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplore);
public static readonly LastWeek = new DateRange(6, 'Last week', false, false, DateRangeScene.Normal, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplore);
public static readonly ThisMonth = new DateRange(7, 'This month', false, false, DateRangeScene.Normal, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplore);
public static readonly LastMonth = new DateRange(8, 'Last month', false, false, DateRangeScene.Normal, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplore);
// Date ranges for normal and trend analysis scene
public static readonly ThisYear = new DateRange(9, 'This year', false, false, DateRangeScene.Normal, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends);
public static readonly LastYear = new DateRange(10, 'Last year', false, false, DateRangeScene.Normal, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends);
public static readonly ThisFiscalYear = new DateRange(11, 'This fiscal year', false, true, DateRangeScene.Normal, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends);
public static readonly LastFiscalYear = new DateRange(12, 'Last fiscal year', false, true, DateRangeScene.Normal, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends);
public static readonly ThisYear = new DateRange(9, 'This year', false, false, DateRangeScene.Normal, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplore);
public static readonly LastYear = new DateRange(10, 'Last year', false, false, DateRangeScene.Normal, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplore);
public static readonly ThisFiscalYear = new DateRange(11, 'This fiscal year', false, true, DateRangeScene.Normal, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplore);
public static readonly LastFiscalYear = new DateRange(12, 'Last fiscal year', false, true, DateRangeScene.Normal, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplore);
// Billing cycle date ranges for normal scene only
public static readonly CurrentBillingCycle = new DateRange(51, 'Current Billing Cycle', true, true, DateRangeScene.Normal);
public static readonly PreviousBillingCycle = new DateRange(52, 'Previous Billing Cycle', true, true, DateRangeScene.Normal);
// Date ranges for trend analysis scene only
public static readonly RecentTwelveMonths = new DateRange(101, 'Recent 12 months', false, false, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends);
public static readonly RecentTwentyFourMonths = new DateRange(102, 'Recent 24 months', false, false, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends);
public static readonly RecentThirtySixMonths = new DateRange(103, 'Recent 36 months', false, false, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends);
public static readonly RecentTwoYears = new DateRange(104, 'Recent 2 years', false, false, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends);
public static readonly RecentThreeYears = new DateRange(105, 'Recent 3 years', false, false, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends);
public static readonly RecentFiveYears = new DateRange(106, 'Recent 5 years', false, false, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends);
public static readonly RecentTwelveMonths = new DateRange(101, 'Recent 12 months', false, false, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplore);
public static readonly RecentTwentyFourMonths = new DateRange(102, 'Recent 24 months', false, false, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplore);
public static readonly RecentThirtySixMonths = new DateRange(103, 'Recent 36 months', false, false, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplore);
public static readonly RecentTwoYears = new DateRange(104, 'Recent 2 years', false, false, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplore);
public static readonly RecentThreeYears = new DateRange(105, 'Recent 3 years', false, false, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplore);
public static readonly RecentFiveYears = new DateRange(106, 'Recent 5 years', false, false, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplore);
// Custom date range
public static readonly Custom = new DateRange(255, 'Custom Date', false, true, DateRangeScene.Normal, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends);
public static readonly Custom = new DateRange(255, 'Custom Date', false, true, DateRangeScene.Normal, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplore);
public readonly type: number;
public readonly name: string;
+127
View File
@@ -0,0 +1,127 @@
import { type NameValue } from '@/core/base.ts';
import { DateRange } from '@/core/datetime.ts';
export enum TransactionExploreConditionRelation {
First = 'first',
And = 'and',
Or = 'or'
}
export const TransactionExploreConditionRelationPriority: Record<TransactionExploreConditionRelation, number> = {
[TransactionExploreConditionRelation.First]: 0,
[TransactionExploreConditionRelation.Or]: 1,
[TransactionExploreConditionRelation.And]: 2
};
export enum TransactionExploreConditionFieldType {
TransactionType = 'transactionType',
TransactionCategory = 'transactionCategory',
SourceAccount = 'sourceAccount',
DestinationAccount = 'destinationAccount',
SourceAmount = 'sourceAmount',
DestinationAmount = 'destinationAmount',
TransactionTag = 'transactionTag',
Description = 'description'
}
export class TransactionExploreConditionField implements NameValue {
private static readonly allInstances: TransactionExploreConditionField[] = [];
private static readonly allInstancesByValue: Record<string, TransactionExploreConditionField> = {};
public static readonly TransactionType = new TransactionExploreConditionField('Transaction Type', TransactionExploreConditionFieldType.TransactionType);
public static readonly TransactionCategory = new TransactionExploreConditionField('Category', TransactionExploreConditionFieldType.TransactionCategory);
public static readonly SourceAccount = new TransactionExploreConditionField('Source Account', TransactionExploreConditionFieldType.SourceAccount);
public static readonly DestinationAccount = new TransactionExploreConditionField('Destination Account', TransactionExploreConditionFieldType.DestinationAccount);
public static readonly SourceAmount = new TransactionExploreConditionField('Amount', TransactionExploreConditionFieldType.SourceAmount);
public static readonly DestinationAmount = new TransactionExploreConditionField('Transfer In Amount', TransactionExploreConditionFieldType.DestinationAmount);
public static readonly TransactionTag = new TransactionExploreConditionField('Tags', TransactionExploreConditionFieldType.TransactionTag);
public static readonly Description = new TransactionExploreConditionField('Description', TransactionExploreConditionFieldType.Description);
public readonly name: string;
public readonly value: TransactionExploreConditionFieldType;
private constructor(name: string, value: TransactionExploreConditionFieldType) {
this.name = name;
this.value = value;
TransactionExploreConditionField.allInstances.push(this);
TransactionExploreConditionField.allInstancesByValue[value] = this;
}
public static values(): TransactionExploreConditionField[] {
return TransactionExploreConditionField.allInstances;
}
public static valueOf(type: string): TransactionExploreConditionField | undefined {
return TransactionExploreConditionField.allInstancesByValue[type];
}
}
export enum TransactionExploreConditionOperatorType {
In = 'in',
GreaterThan = 'greaterThan',
LessThan = 'lessThan',
Equals = 'equals',
NotEquals = 'notEquals',
Between = 'between',
NotBetween = 'notBetween',
HasAny = 'hasAny',
HasAll = 'hasAll',
NotHasAny = 'notHasAny',
NotHasAll = 'notHasAll',
IsEmpty = 'isEmpty',
IsNotEmpty = 'isNotEmpty',
Contains = 'contains',
NotContains = 'notContains',
StartsWith = 'startsWith',
NotStartsWith = 'notStartsWith',
EndsWith = 'endsWith',
NotEndsWith = 'notEndsWith'
}
export class TransactionExploreConditionOperator implements NameValue {
private static readonly allInstances: TransactionExploreConditionOperator[] = [];
private static readonly allInstancesByValue: Record<string, TransactionExploreConditionOperator> = {};
public static readonly In = new TransactionExploreConditionOperator('In', TransactionExploreConditionOperatorType.In);
public static readonly GreaterThan = new TransactionExploreConditionOperator('Greater than', TransactionExploreConditionOperatorType.GreaterThan);
public static readonly LessThan = new TransactionExploreConditionOperator('Less than', TransactionExploreConditionOperatorType.LessThan);
public static readonly Equals = new TransactionExploreConditionOperator('Equal to', TransactionExploreConditionOperatorType.Equals);
public static readonly NotEquals = new TransactionExploreConditionOperator('Not equal to', TransactionExploreConditionOperatorType.NotEquals);
public static readonly Between = new TransactionExploreConditionOperator('Between', TransactionExploreConditionOperatorType.Between);
public static readonly NotBetween = new TransactionExploreConditionOperator('Not between', TransactionExploreConditionOperatorType.NotBetween);
public static readonly HasAny = new TransactionExploreConditionOperator('Has any', TransactionExploreConditionOperatorType.HasAny);
public static readonly HasAll = new TransactionExploreConditionOperator('Has all', TransactionExploreConditionOperatorType.HasAll);
public static readonly NotHasAny = new TransactionExploreConditionOperator('Not has any', TransactionExploreConditionOperatorType.NotHasAny);
public static readonly NotHasAll = new TransactionExploreConditionOperator('Not has all', TransactionExploreConditionOperatorType.NotHasAll);
public static readonly IsEmpty = new TransactionExploreConditionOperator('Is empty', TransactionExploreConditionOperatorType.IsEmpty);
public static readonly IsNotEmpty = new TransactionExploreConditionOperator('Is not empty', TransactionExploreConditionOperatorType.IsNotEmpty);
public static readonly Contains = new TransactionExploreConditionOperator('Contains', TransactionExploreConditionOperatorType.Contains);
public static readonly NotContains = new TransactionExploreConditionOperator('Not contains', TransactionExploreConditionOperatorType.NotContains);
public static readonly StartsWith = new TransactionExploreConditionOperator('Starts with', TransactionExploreConditionOperatorType.StartsWith);
public static readonly NotStartsWith = new TransactionExploreConditionOperator('Not starts with', TransactionExploreConditionOperatorType.NotStartsWith);
public static readonly EndsWith = new TransactionExploreConditionOperator('Ends with', TransactionExploreConditionOperatorType.EndsWith);
public static readonly NotEndsWith = new TransactionExploreConditionOperator('Not ends with', TransactionExploreConditionOperatorType.NotEndsWith);
public readonly name: string;
public readonly value: TransactionExploreConditionOperatorType;
private constructor(name: string, value: TransactionExploreConditionOperatorType) {
this.name = name;
this.value = value;
TransactionExploreConditionOperator.allInstances.push(this);
TransactionExploreConditionOperator.allInstancesByValue[value] = this;
}
public static values(): TransactionExploreConditionOperator[] {
return TransactionExploreConditionOperator.allInstances;
}
public static valueOf(type: string): TransactionExploreConditionOperator | undefined {
return TransactionExploreConditionOperator.allInstancesByValue[type];
}
}
export const DEFAULT_TRANSACTION_EXPLORE_DATE_RANGE: DateRange = DateRange.ThisMonth;
+10
View File
@@ -10,6 +10,7 @@ import {
DEFAULT_TREND_CHART_DATA_RANGE,
DEFAULT_ASSET_TRENDS_CHART_DATA_RANGE
} from './statistics.ts';
import { DEFAULT_TRANSACTION_EXPLORE_DATE_RANGE } from './explore.ts';
import { DEFAULT_CURRENCY_CODE } from '@/consts/currency.ts';
export type ApplicationSettingKey = string;
@@ -49,6 +50,9 @@ export interface ApplicationSettings extends BaseApplicationSetting {
autoSaveTransactionDraft: string;
autoGetCurrentGeoLocation: boolean;
alwaysShowTransactionPicturesInMobileTransactionEditPage: boolean;
// Insights & Explore Page
insightsExploreDefaultDateRangeType: number;
timezoneUsedForInsightsExplorePage: number;
// Account List Page
totalAmountExcludeAccountIds: Record<string, boolean>;
// Exchange Rates Data Page
@@ -111,6 +115,9 @@ export const ALL_ALLOWED_CLOUD_SYNC_APP_SETTING_KEY_TYPES: Record<string, UserAp
'autoSaveTransactionDraft': UserApplicationCloudSettingType.String,
'autoGetCurrentGeoLocation': UserApplicationCloudSettingType.Boolean,
'alwaysShowTransactionPicturesInMobileTransactionEditPage': UserApplicationCloudSettingType.Boolean,
// Insights & Explore Page
'insightsExploreDefaultDateRangeType': UserApplicationCloudSettingType.Number,
'timezoneUsedForInsightsExplorePage': UserApplicationCloudSettingType.Number,
// Account List Page
'totalAmountExcludeAccountIds': UserApplicationCloudSettingType.StringBooleanMap,
// Exchange Rates Data Page
@@ -158,6 +165,9 @@ export const DEFAULT_APPLICATION_SETTINGS: ApplicationSettings = {
autoSaveTransactionDraft: 'disabled',
autoGetCurrentGeoLocation: false,
alwaysShowTransactionPicturesInMobileTransactionEditPage: false,
// Insights & Explore Page
insightsExploreDefaultDateRangeType: DEFAULT_TRANSACTION_EXPLORE_DATE_RANGE.type,
timezoneUsedForInsightsExplorePage: TimezoneTypeForStatistics.Default.type,
// Account List Page
totalAmountExcludeAccountIds: {},
// Exchange Rates Data Page