move the "Timezone Used for Date Range" option from insights explorer settings into each exploration

This commit is contained in:
MaysWind
2026-01-03 20:42:16 +08:00
parent cc0996e0d2
commit 526d7e50ec
9 changed files with 130 additions and 102 deletions
-1
View File
@@ -31,7 +31,6 @@ var ALL_ALLOWED_CLOUD_SYNC_APP_SETTING_KEY_TYPES = map[string]UserApplicationClo
"alwaysShowTransactionPicturesInMobileTransactionEditPage": USER_APPLICATION_CLOUD_SETTING_TYPE_BOOLEAN, "alwaysShowTransactionPicturesInMobileTransactionEditPage": USER_APPLICATION_CLOUD_SETTING_TYPE_BOOLEAN,
// Insights Explorer Page // Insights Explorer Page
"insightsExplorerDefaultDateRangeType": USER_APPLICATION_CLOUD_SETTING_TYPE_NUMBER, "insightsExplorerDefaultDateRangeType": USER_APPLICATION_CLOUD_SETTING_TYPE_NUMBER,
"timezoneUsedForInsightsExplorerPage": USER_APPLICATION_CLOUD_SETTING_TYPE_NUMBER,
// Account List Page // Account List Page
"totalAmountExcludeAccountIds": USER_APPLICATION_CLOUD_SETTING_TYPE_STRING_BOOLEAN_MAP, "totalAmountExcludeAccountIds": USER_APPLICATION_CLOUD_SETTING_TYPE_STRING_BOOLEAN_MAP,
// Exchange Rates Data Page // Exchange Rates Data Page
-3
View File
@@ -52,7 +52,6 @@ export interface ApplicationSettings extends BaseApplicationSetting {
alwaysShowTransactionPicturesInMobileTransactionEditPage: boolean; alwaysShowTransactionPicturesInMobileTransactionEditPage: boolean;
// Insights Explorer Page // Insights Explorer Page
insightsExplorerDefaultDateRangeType: number; insightsExplorerDefaultDateRangeType: number;
timezoneUsedForInsightsExplorerPage: number;
// Account List Page // Account List Page
totalAmountExcludeAccountIds: Record<string, boolean>; totalAmountExcludeAccountIds: Record<string, boolean>;
// Exchange Rates Data Page // Exchange Rates Data Page
@@ -117,7 +116,6 @@ export const ALL_ALLOWED_CLOUD_SYNC_APP_SETTING_KEY_TYPES: Record<string, UserAp
'alwaysShowTransactionPicturesInMobileTransactionEditPage': UserApplicationCloudSettingType.Boolean, 'alwaysShowTransactionPicturesInMobileTransactionEditPage': UserApplicationCloudSettingType.Boolean,
// Insights Explorer Page // Insights Explorer Page
'insightsExplorerDefaultDateRangeType': UserApplicationCloudSettingType.Number, 'insightsExplorerDefaultDateRangeType': UserApplicationCloudSettingType.Number,
'timezoneUsedForInsightsExplorerPage': UserApplicationCloudSettingType.Number,
// Account List Page // Account List Page
'totalAmountExcludeAccountIds': UserApplicationCloudSettingType.StringBooleanMap, 'totalAmountExcludeAccountIds': UserApplicationCloudSettingType.StringBooleanMap,
// Exchange Rates Data Page // Exchange Rates Data Page
@@ -167,7 +165,6 @@ export const DEFAULT_APPLICATION_SETTINGS: ApplicationSettings = {
alwaysShowTransactionPicturesInMobileTransactionEditPage: false, alwaysShowTransactionPicturesInMobileTransactionEditPage: false,
// Insights Explorer Page // Insights Explorer Page
insightsExplorerDefaultDateRangeType: DEFAULT_TRANSACTION_EXPLORER_DATE_RANGE.type, insightsExplorerDefaultDateRangeType: DEFAULT_TRANSACTION_EXPLORER_DATE_RANGE.type,
timezoneUsedForInsightsExplorerPage: TimezoneTypeForStatistics.Default.type,
// Account List Page // Account List Page
totalAmountExcludeAccountIds: {}, totalAmountExcludeAccountIds: {},
// Exchange Rates Data Page // Exchange Rates Data Page
+53 -38
View File
@@ -44,17 +44,18 @@ import {
import { import {
parseDateTimeFromUnixTime, parseDateTimeFromUnixTime,
parseDateTimeFromUnixTimeWithTimezoneOffset, parseDateTimeFromUnixTimeWithTimezoneOffset,
getYearFirstUnixTimeBySpecifiedUnixTime,
getQuarterFirstUnixTimeBySpecifiedUnixTime,
getMonthFirstUnixTimeBySpecifiedUnixTime,
getDayFirstUnixTimeBySpecifiedUnixTime,
getDateRangeByDateType, getDateRangeByDateType,
getFiscalYearStartUnixTime getFiscalYearFromUnixTime
} from '@/lib/datetime.ts'; } from '@/lib/datetime.ts';
import services from '@/lib/services.ts'; import services from '@/lib/services.ts';
import logger from '@/lib/logger.ts'; import logger from '@/lib/logger.ts';
export enum TransactionExplorerDimensionType { export enum TransactionExplorerDimensionType {
DateTime = 'YYYY-MM-DD HH:mm:ss',
YearMonthDay = 'YYYY-MM-DD',
YearMonth = 'YYYY-MM',
YearQuarter = 'YYYY-Q',
Year = 'YYYY',
TransactionType = 'transactionType', TransactionType = 'transactionType',
Category = 'category', Category = 'category',
Account = 'account', Account = 'account',
@@ -67,6 +68,7 @@ export interface TransactionExplorerPartialFilter {
startTime?: number; startTime?: number;
endTime?: number; endTime?: number;
queryId?: string; queryId?: string;
timezoneUsedForDateRange?: number;
chartType?: TransactionExplorerChartTypeValue; chartType?: TransactionExplorerChartTypeValue;
categoryDimension?: TransactionExplorerDataDimensionType; categoryDimension?: TransactionExplorerDataDimensionType;
seriesDimension?: TransactionExplorerDataDimensionType; seriesDimension?: TransactionExplorerDataDimensionType;
@@ -78,6 +80,7 @@ export interface TransactionExplorerFilter extends TransactionExplorerPartialFil
startTime: number; startTime: number;
endTime: number; endTime: number;
query: TransactionExplorerQuery[]; query: TransactionExplorerQuery[];
timezoneUsedForDateRange: number;
chartType: TransactionExplorerChartTypeValue; chartType: TransactionExplorerChartTypeValue;
categoryDimension: TransactionExplorerDataDimensionType; categoryDimension: TransactionExplorerDataDimensionType;
seriesDimension: TransactionExplorerDataDimensionType; seriesDimension: TransactionExplorerDataDimensionType;
@@ -124,10 +127,10 @@ export const useExplorersStore = defineStore('explorers', () => {
const transactionTagsStore = useTransactionTagsStore(); const transactionTagsStore = useTransactionTagsStore();
const exchangeRatesStore = useExchangeRatesStore(); const exchangeRatesStore = useExchangeRatesStore();
function getDataCategoryInfo(dimension: TransactionExplorerDataDimension, queryName: string, queryIndex: number, transaction: TransactionInsightDataItem): CategoriedInfo { function getDataCategoryInfo(timezoneUsedForDateRange: number, dimension: TransactionExplorerDataDimension, queryName: string, queryIndex: number, transaction: TransactionInsightDataItem): CategoriedInfo {
let transactionTimeUtfOffset: number | undefined = undefined; let transactionTimeUtfOffset: number | undefined = undefined;
if (settingsStore.appSettings.timezoneUsedForInsightsExplorerPage === TimezoneTypeForStatistics.TransactionTimezone.type) { if (timezoneUsedForDateRange === TimezoneTypeForStatistics.TransactionTimezone.type) {
transactionTimeUtfOffset = transaction.utcOffset; transactionTimeUtfOffset = transaction.utcOffset;
} }
@@ -158,52 +161,56 @@ export const useExplorersStore = defineStore('explorers', () => {
}; };
} }
} else if (dimension === TransactionExplorerDataDimension.DateTime) { } else if (dimension === TransactionExplorerDataDimension.DateTime) {
const unixTime = transaction.time.toString(10); const dateTime = isDefined(transactionTimeUtfOffset) ? parseDateTimeFromUnixTimeWithTimezoneOffset(transaction.time, transactionTimeUtfOffset) : parseDateTimeFromUnixTime(transaction.time);
const textualDateTime = `${dateTime.getGregorianCalendarYearDashMonthDashDay()} ${dateTime.getHour().toString(10).padStart(2, '0')}:${dateTime.getMinute().toString(10).padStart(2, '0')}:${dateTime.getSecond().toString(10).padStart(2, '0')}`;
return { return {
categoryName: unixTime, categoryName: textualDateTime,
categoryId: unixTime, categoryId: textualDateTime,
categoryIdType: TransactionExplorerDimensionType.Other categoryIdType: TransactionExplorerDimensionType.DateTime
}; };
} else if (dimension === TransactionExplorerDataDimension.DateTimeByYearMonthDay) { } else if (dimension === TransactionExplorerDataDimension.DateTimeByYearMonthDay) {
const unixTime = getDayFirstUnixTimeBySpecifiedUnixTime(transaction.time, transactionTimeUtfOffset).toString(10); const dateTime = isDefined(transactionTimeUtfOffset) ? parseDateTimeFromUnixTimeWithTimezoneOffset(transaction.time, transactionTimeUtfOffset) : parseDateTimeFromUnixTime(transaction.time);
const yearMonthDay = dateTime.getGregorianCalendarYearDashMonthDashDay();
return { return {
categoryName: unixTime, categoryName: yearMonthDay,
categoryId: unixTime, categoryId: yearMonthDay,
categoryIdType: TransactionExplorerDimensionType.Other categoryIdType: TransactionExplorerDimensionType.YearMonthDay
}; };
} else if (dimension === TransactionExplorerDataDimension.DateTimeByYearMonth) { } else if (dimension === TransactionExplorerDataDimension.DateTimeByYearMonth) {
const unixTime = getMonthFirstUnixTimeBySpecifiedUnixTime(transaction.time, transactionTimeUtfOffset).toString(10); const dateTime = isDefined(transactionTimeUtfOffset) ? parseDateTimeFromUnixTimeWithTimezoneOffset(transaction.time, transactionTimeUtfOffset) : parseDateTimeFromUnixTime(transaction.time);
const yearMonth = dateTime.getGregorianCalendarYearDashMonth();
return { return {
categoryName: unixTime, categoryName: yearMonth,
categoryId: unixTime, categoryId: yearMonth,
categoryIdType: TransactionExplorerDimensionType.Other categoryIdType: TransactionExplorerDimensionType.YearMonth
}; };
} else if (dimension === TransactionExplorerDataDimension.DateTimeByYearQuarter) { } else if (dimension === TransactionExplorerDataDimension.DateTimeByYearQuarter) {
const unixTime = getQuarterFirstUnixTimeBySpecifiedUnixTime(transaction.time, transactionTimeUtfOffset).toString(10); const dateTime = isDefined(transactionTimeUtfOffset) ? parseDateTimeFromUnixTimeWithTimezoneOffset(transaction.time, transactionTimeUtfOffset) : parseDateTimeFromUnixTime(transaction.time);
const yearQuarter = `${dateTime.getGregorianCalendarYear().toString(10)}-${dateTime.getGregorianCalendarQuarter().toString(10)}`;
return { return {
categoryName: unixTime, categoryName: yearQuarter,
categoryId: unixTime, categoryId: yearQuarter,
categoryIdType: TransactionExplorerDimensionType.Other categoryIdType: TransactionExplorerDimensionType.YearQuarter
}; };
} else if (dimension === TransactionExplorerDataDimension.DateTimeByYear) { } else if (dimension === TransactionExplorerDataDimension.DateTimeByYear) {
const unixTime = getYearFirstUnixTimeBySpecifiedUnixTime(transaction.time, transactionTimeUtfOffset).toString(10); const dateTime = isDefined(transactionTimeUtfOffset) ? parseDateTimeFromUnixTimeWithTimezoneOffset(transaction.time, transactionTimeUtfOffset) : parseDateTimeFromUnixTime(transaction.time);
return { return {
categoryName: unixTime, categoryName: dateTime.getGregorianCalendarYear().toString(10),
categoryId: unixTime, categoryId: dateTime.getGregorianCalendarYear().toString(10),
categoryIdType: TransactionExplorerDimensionType.Other categoryIdType: TransactionExplorerDimensionType.Year
}; };
} else if (dimension === TransactionExplorerDataDimension.DateTimeByFiscalYear) { } else if (dimension === TransactionExplorerDataDimension.DateTimeByFiscalYear) {
const unixTime = getFiscalYearStartUnixTime(transaction.time, userStore.currentUserFiscalYearStart, transactionTimeUtfOffset).toString(10); const fiscalYear = getFiscalYearFromUnixTime(transaction.time, userStore.currentUserFiscalYearStart, transactionTimeUtfOffset).toString(10);
return { return {
categoryName: unixTime, categoryName: fiscalYear,
categoryId: unixTime, categoryId: fiscalYear,
categoryIdType: TransactionExplorerDimensionType.Other categoryIdType: TransactionExplorerDimensionType.Year
}; };
} else if (dimension === TransactionExplorerDataDimension.DateTimeByDayOfWeek) { } else if (dimension === TransactionExplorerDataDimension.DateTimeByDayOfWeek) {
const dateTime = isDefined(transactionTimeUtfOffset) ? parseDateTimeFromUnixTimeWithTimezoneOffset(transaction.time, transactionTimeUtfOffset) : parseDateTimeFromUnixTime(transaction.time); const dateTime = isDefined(transactionTimeUtfOffset) ? parseDateTimeFromUnixTimeWithTimezoneOffset(transaction.time, transactionTimeUtfOffset) : parseDateTimeFromUnixTime(transaction.time);
@@ -336,8 +343,8 @@ export const useExplorersStore = defineStore('explorers', () => {
} }
} }
function addTransactionToCategoriedDataMap(categoriedDataMap: Record<string, CategoriedTransactions>, categoryDimension: TransactionExplorerDataDimension, seriesDemension: TransactionExplorerDataDimension, queryName: string, queryIndex: number, transaction: TransactionInsightDataItem): void { function addTransactionToCategoriedDataMap(timezoneUsedForDateRange: number, categoriedDataMap: Record<string, CategoriedTransactions>, categoryDimension: TransactionExplorerDataDimension, seriesDemension: TransactionExplorerDataDimension, queryName: string, queryIndex: number, transaction: TransactionInsightDataItem): void {
const categoriedInfo = getDataCategoryInfo(categoryDimension, queryName, queryIndex, transaction); const categoriedInfo = getDataCategoryInfo(timezoneUsedForDateRange, categoryDimension, queryName, queryIndex, transaction);
let categoriedData = categoriedDataMap[categoriedInfo.categoryId]; let categoriedData = categoriedDataMap[categoriedInfo.categoryId];
if (!categoriedData) { if (!categoriedData) {
@@ -352,7 +359,7 @@ export const useExplorersStore = defineStore('explorers', () => {
categoriedDataMap[categoriedInfo.categoryId] = categoriedData; categoriedDataMap[categoriedInfo.categoryId] = categoriedData;
} }
const seriesedInfo = getDataCategoryInfo(seriesDemension, queryName, queryIndex, transaction); const seriesedInfo = getDataCategoryInfo(timezoneUsedForDateRange, seriesDemension, queryName, queryIndex, transaction);
let seriesedData = categoriedData.trasactions[seriesedInfo.categoryId]; let seriesedData = categoriedData.trasactions[seriesedInfo.categoryId];
if (!seriesedData) { if (!seriesedData) {
@@ -375,10 +382,11 @@ export const useExplorersStore = defineStore('explorers', () => {
startTime: 0, startTime: 0,
endTime: 0, endTime: 0,
query: [], query: [],
timezoneUsedForDateRange: TimezoneTypeForStatistics.Default.type,
chartType: TransactionExplorerChartType.Default.value,
categoryDimension: TransactionExplorerDataDimension.CategoryDimensionDefault.value, categoryDimension: TransactionExplorerDataDimension.CategoryDimensionDefault.value,
seriesDimension: TransactionExplorerDataDimension.SeriesDimensionDefault.value, seriesDimension: TransactionExplorerDataDimension.SeriesDimensionDefault.value,
valueMetric: TransactionExplorerValueMetric.Default.value, valueMetric: TransactionExplorerValueMetric.Default.value
chartType: TransactionExplorerChartType.Default.value
}); });
const transactionExplorerAllData = ref<TransactionInfoResponse[]>([]); const transactionExplorerAllData = ref<TransactionInfoResponse[]>([]);
@@ -498,13 +506,13 @@ export const useExplorersStore = defineStore('explorers', () => {
for (const transaction of allTransactions.value) { for (const transaction of allTransactions.value) {
if (!transactionExplorerFilter.value.query || transactionExplorerFilter.value.query.length < 1) { if (!transactionExplorerFilter.value.query || transactionExplorerFilter.value.query.length < 1) {
addTransactionToCategoriedDataMap(categoriedDataMap, categoryDimension, seriesDimension, '', 0, transaction); addTransactionToCategoriedDataMap(transactionExplorerFilter.value.timezoneUsedForDateRange, categoriedDataMap, categoryDimension, seriesDimension, '', 0, transaction);
continue; continue;
} }
for (const [query, index] of itemAndIndex(transactionExplorerFilter.value.query)) { for (const [query, index] of itemAndIndex(transactionExplorerFilter.value.query)) {
if (query.match(transaction)) { if (query.match(transaction)) {
addTransactionToCategoriedDataMap(categoriedDataMap, categoryDimension, seriesDimension, query.name, index, transaction); addTransactionToCategoriedDataMap(transactionExplorerFilter.value.timezoneUsedForDateRange, categoriedDataMap, categoryDimension, seriesDimension, query.name, index, transaction);
if (categoryDimension !== TransactionExplorerDataDimension.Query) { if (categoryDimension !== TransactionExplorerDataDimension.Query) {
break; break;
@@ -640,6 +648,7 @@ export const useExplorersStore = defineStore('explorers', () => {
transactionExplorerFilter.value.startTime = 0; transactionExplorerFilter.value.startTime = 0;
transactionExplorerFilter.value.endTime = 0; transactionExplorerFilter.value.endTime = 0;
transactionExplorerFilter.value.query = []; transactionExplorerFilter.value.query = [];
transactionExplorerFilter.value.timezoneUsedForDateRange = TimezoneTypeForStatistics.Default.type;
transactionExplorerFilter.value.chartType = TransactionExplorerChartType.Default.value; transactionExplorerFilter.value.chartType = TransactionExplorerChartType.Default.value;
transactionExplorerFilter.value.categoryDimension = TransactionExplorerDataDimension.CategoryDimensionDefault.value; transactionExplorerFilter.value.categoryDimension = TransactionExplorerDataDimension.CategoryDimensionDefault.value;
transactionExplorerFilter.value.seriesDimension = TransactionExplorerDataDimension.SeriesDimensionDefault.value; transactionExplorerFilter.value.seriesDimension = TransactionExplorerDataDimension.SeriesDimensionDefault.value;
@@ -686,6 +695,7 @@ export const useExplorersStore = defineStore('explorers', () => {
if (resetQuery) { if (resetQuery) {
transactionExplorerFilter.value.query = []; transactionExplorerFilter.value.query = [];
transactionExplorerFilter.value.timezoneUsedForDateRange = TimezoneTypeForStatistics.Default.type;
transactionExplorerFilter.value.chartType = TransactionExplorerChartType.Default.value; transactionExplorerFilter.value.chartType = TransactionExplorerChartType.Default.value;
transactionExplorerFilter.value.categoryDimension = TransactionExplorerDataDimension.CategoryDimensionDefault.value; transactionExplorerFilter.value.categoryDimension = TransactionExplorerDataDimension.CategoryDimensionDefault.value;
transactionExplorerFilter.value.seriesDimension = TransactionExplorerDataDimension.SeriesDimensionDefault.value; transactionExplorerFilter.value.seriesDimension = TransactionExplorerDataDimension.SeriesDimensionDefault.value;
@@ -711,6 +721,11 @@ export const useExplorersStore = defineStore('explorers', () => {
changed = true; changed = true;
} }
if (filter && isDefined(filter.timezoneUsedForDateRange) && transactionExplorerFilter.value.timezoneUsedForDateRange !== filter.timezoneUsedForDateRange) {
transactionExplorerFilter.value.timezoneUsedForDateRange = filter.timezoneUsedForDateRange;
changed = true;
}
if (filter && isDefined(filter.chartType) && transactionExplorerFilter.value.chartType !== filter.chartType) { if (filter && isDefined(filter.chartType) && transactionExplorerFilter.value.chartType !== filter.chartType) {
transactionExplorerFilter.value.chartType = filter.chartType; transactionExplorerFilter.value.chartType = filter.chartType;
changed = true; changed = true;
-7
View File
@@ -252,12 +252,6 @@ export const useSettingsStore = defineStore('settings', () => {
updateUserApplicationCloudSettingValue('insightsExplorerDefaultDateRangeType', value); updateUserApplicationCloudSettingValue('insightsExplorerDefaultDateRangeType', value);
} }
function setTimezoneUsedForInsightsExplorerPage(value: number): void {
updateApplicationSettingsValue('timezoneUsedForInsightsExplorerPage', value);
appSettings.value.timezoneUsedForInsightsExplorerPage = value;
updateUserApplicationCloudSettingValue('timezoneUsedForInsightsExplorerPage', value);
}
// Account List Page // Account List Page
function setTotalAmountExcludeAccountIds(value: Record<string, boolean>): void { function setTotalAmountExcludeAccountIds(value: Record<string, boolean>): void {
updateApplicationSettingsValue('totalAmountExcludeAccountIds', value); updateApplicationSettingsValue('totalAmountExcludeAccountIds', value);
@@ -482,7 +476,6 @@ export const useSettingsStore = defineStore('settings', () => {
setAlwaysShowTransactionPicturesInMobileTransactionEditPage, setAlwaysShowTransactionPicturesInMobileTransactionEditPage,
// -- Insights Explorer Page // -- Insights Explorer Page
setInsightsExplorerDefaultDateRangeType, setInsightsExplorerDefaultDateRangeType,
setTimezoneUsedForInsightsExplorerPage,
// -- Account List Page // -- Account List Page
setTotalAmountExcludeAccountIds, setTotalAmountExcludeAccountIds,
// -- Exchange Rates Data Page // -- Exchange Rates Data Page
@@ -53,14 +53,13 @@ export const ALL_APPLICATION_CLOUD_SETTINGS: CategorizedApplicationCloudSettingI
{ {
categoryName: 'Insights Explorer Page', categoryName: 'Insights Explorer Page',
items: [ items: [
{ settingKey: 'insightsExplorerDefaultDateRangeType', settingName: 'Default Date Range', mobile: false, desktop: true }, { settingKey: 'insightsExplorerDefaultDateRangeType', settingName: 'Default Date Range', mobile: false, desktop: true }
{ settingKey: 'timezoneUsedForInsightsExplorerPage', settingName: 'Timezone Used for Date Range', mobile: false, desktop: true },
] ]
}, },
{ {
categoryName: 'Account List Page', categoryName: 'Account List Page',
items: [ items: [
{ settingKey: 'totalAmountExcludeAccountIds', settingName: 'Accounts Included in Total', mobile: true, desktop: true }, { settingKey: 'totalAmountExcludeAccountIds', settingName: 'Accounts Included in Total', mobile: true, desktop: true }
] ]
}, },
{ {
@@ -240,18 +240,6 @@
v-model="insightsExplorerDefaultDateRangeType" v-model="insightsExplorerDefaultDateRangeType"
/> />
</v-col> </v-col>
<v-col cols="12" md="6">
<v-select
item-title="displayName"
item-value="type"
persistent-placeholder
:label="tt('Timezone Used for Date Range')"
:placeholder="tt('Timezone Used for Date Range')"
:items="allTimezoneTypesUsedForStatistics"
v-model="timezoneUsedForInsightsExplorerPage"
/>
</v-col>
</v-row> </v-row>
</v-card-text> </v-card-text>
</v-form> </v-form>
@@ -339,7 +327,6 @@ import { useAppSettingPageBase } from '@/views/base/settings/AppSettingsPageBase
import { useSettingsStore } from '@/stores/setting.ts'; import { useSettingsStore } from '@/stores/setting.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 { useExplorersStore } from '@/stores/explorer.ts';
import type { LocalizedSwitchOption } from '@/core/base.ts'; import type { LocalizedSwitchOption } from '@/core/base.ts';
import { ThemeType } from '@/core/theme.ts'; import { ThemeType } from '@/core/theme.ts';
@@ -383,7 +370,6 @@ const {
const settingsStore = useSettingsStore(); const settingsStore = useSettingsStore();
const accountsStore = useAccountsStore(); const accountsStore = useAccountsStore();
const transactionCategoriesStore = useTransactionCategoriesStore(); const transactionCategoriesStore = useTransactionCategoriesStore();
const explorersStore = useExplorersStore();
const snackbar = useTemplateRef<SnackBarType>('snackbar'); const snackbar = useTemplateRef<SnackBarType>('snackbar');
@@ -419,14 +405,6 @@ const insightsExplorerDefaultDateRangeType = computed<number>({
set: (value) => settingsStore.setInsightsExplorerDefaultDateRangeType(value) set: (value) => settingsStore.setInsightsExplorerDefaultDateRangeType(value)
}); });
const timezoneUsedForInsightsExplorerPage = computed<number>({
get: () => settingsStore.appSettings.timezoneUsedForInsightsExplorerPage,
set: (value: number) => {
settingsStore.setTimezoneUsedForInsightsExplorerPage(value);
explorersStore.updateTransactionExplorerInvalidState(true);
}
});
function init(): void { function init(): void {
loadingAccounts.value = true; loadingAccounts.value = true;
+30 -4
View File
@@ -77,15 +77,25 @@
</v-btn> </v-btn>
<v-spacer/> <v-spacer/>
<v-btn density="comfortable" color="default" variant="text" class="ms-2" <v-btn density="comfortable" color="default" variant="text" class="ms-2"
:disabled="loading" :icon="true" :disabled="loading" :icon="true">
v-if="activeTab === 'table' || activeTab === 'chart'">
<v-icon :icon="mdiDotsVertical" /> <v-icon :icon="mdiDotsVertical" />
<v-menu activator="parent"> <v-menu activator="parent">
<v-list> <v-list>
<v-list-subheader :title="tt('Timezone Used for Date Range')"
v-if="activeTab === 'query'"/>
<template v-if="activeTab === 'query'">
<v-list-item :key="timezoneType.type" :value="timezoneType.type"
:prepend-icon="timezoneTypeIconMap[timezoneType.type]"
:append-icon="(query.timezoneUsedForDateRange === timezoneType.type ? mdiCheck : undefined)"
:title="timezoneType.displayName"
v-for="timezoneType in allTimezoneTypesUsedForDateRange"
@click="updateTimezoneUsedForDateRange(timezoneType.type)"></v-list-item>
</template>
<v-list-item :prepend-icon="mdiExport" <v-list-item :prepend-icon="mdiExport"
:title="tt('Export Results')" :title="tt('Export Results')"
:disabled="loading || !filteredTransactions || filteredTransactions.length < 1" :disabled="loading || !filteredTransactions || filteredTransactions.length < 1"
@click="exportResults"></v-list-item> @click="exportResults"
v-if="activeTab === 'table' || activeTab === 'chart'"></v-list-item>
</v-list> </v-list>
</v-menu> </v-menu>
</v-btn> </v-btn>
@@ -144,9 +154,10 @@ import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
import { useTransactionTagsStore } from '@/stores/transactionTag.ts'; import { useTransactionTagsStore } from '@/stores/transactionTag.ts';
import { type TransactionExplorerPartialFilter, type TransactionExplorerFilter, useExplorersStore } from '@/stores/explorer.ts'; import { type TransactionExplorerPartialFilter, type TransactionExplorerFilter, useExplorersStore } from '@/stores/explorer.ts';
import type { NameNumeralValue } from '@/core/base.ts'; import type { NameNumeralValue, TypeAndDisplayName } from '@/core/base.ts';
import type { NumeralSystem } from '@/core/numeral.ts'; import type { NumeralSystem } from '@/core/numeral.ts';
import { type WeekDayValue, type LocalizedDateRange, DateRangeScene, DateRange } from '@/core/datetime.ts'; import { type WeekDayValue, type LocalizedDateRange, DateRangeScene, DateRange } from '@/core/datetime.ts';
import { TimezoneTypeForStatistics } from '@/core/timezone.ts';
import { import {
type TransactionInsightDataItem type TransactionInsightDataItem
@@ -166,6 +177,8 @@ import {
mdiCheck, mdiCheck,
mdiRefresh, mdiRefresh,
mdiDotsVertical, mdiDotsVertical,
mdiHomeClockOutline,
mdiInvoiceTextClockOutline,
mdiExport mdiExport
} from '@mdi/js'; } from '@mdi/js';
@@ -191,6 +204,7 @@ const display = useDisplay();
const { const {
tt, tt,
getAllDateRanges, getAllDateRanges,
getAllTimezoneTypesUsedForStatistics,
getCurrentNumeralSystemType, getCurrentNumeralSystemType,
formatDateTimeToLongDateTime, formatDateTimeToLongDateTime,
formatDateRange formatDateRange
@@ -202,6 +216,11 @@ const transactionCategoriesStore = useTransactionCategoriesStore();
const transactionTagsStore = useTransactionTagsStore(); const transactionTagsStore = useTransactionTagsStore();
const explorersStore = useExplorersStore(); const explorersStore = useExplorersStore();
const timezoneTypeIconMap = {
[TimezoneTypeForStatistics.ApplicationTimezone.type]: mdiHomeClockOutline,
[TimezoneTypeForStatistics.TransactionTimezone.type]: mdiInvoiceTextClockOutline
};
const snackbar = useTemplateRef<SnackBarType>('snackbar'); const snackbar = useTemplateRef<SnackBarType>('snackbar');
const explorerDataTableTab = useTemplateRef<ExplorerDataTableTabType>('explorerDataTableTab'); const explorerDataTableTab = useTemplateRef<ExplorerDataTableTabType>('explorerDataTableTab');
const explorerChartTab = useTemplateRef<ExplorerChartTabType>('explorerChartTab'); const explorerChartTab = useTemplateRef<ExplorerChartTabType>('explorerChartTab');
@@ -224,6 +243,7 @@ const query = computed<TransactionExplorerFilter>(() => explorersStore.transacti
const filteredTransactions = computed<TransactionInsightDataItem[]>(() => explorersStore.filteredTransactions); const filteredTransactions = computed<TransactionInsightDataItem[]>(() => explorersStore.filteredTransactions);
const allDateRanges = computed<LocalizedDateRange[]>(() => getAllDateRanges(DateRangeScene.InsightsExplorer, true)); const allDateRanges = computed<LocalizedDateRange[]>(() => getAllDateRanges(DateRangeScene.InsightsExplorer, true));
const allTimezoneTypesUsedForDateRange = computed<TypeAndDisplayName[]>(() => getAllTimezoneTypesUsedForStatistics());
const canShiftDateRange = computed<boolean>(() => query.value.dateRangeType !== DateRange.All.type); const canShiftDateRange = computed<boolean>(() => query.value.dateRangeType !== DateRange.All.type);
const displayQueryDateRangeName = computed<string>(() => formatDateRange(query.value.dateRangeType, query.value.startTime, query.value.endTime)); const displayQueryDateRangeName = computed<string>(() => formatDateRange(query.value.dateRangeType, query.value.startTime, query.value.endTime));
const displayQueryStartTime = computed<string>(() => formatDateTimeToLongDateTime(parseDateTimeFromUnixTime(query.value.startTime))); const displayQueryStartTime = computed<string>(() => formatDateTimeToLongDateTime(parseDateTimeFromUnixTime(query.value.startTime)));
@@ -337,6 +357,12 @@ function reload(force: boolean): Promise<unknown> | null {
}); });
} }
function updateTimezoneUsedForDateRange(timezoneType: number): void {
explorersStore.updateTransactionExplorerFilter({
timezoneUsedForDateRange: timezoneType
});
}
function exportResults(): void { function exportResults(): void {
if (activeTab.value === 'table' && filteredTransactions.value) { if (activeTab.value === 'table' && filteredTransactions.value) {
const results = explorerDataTableTab.value?.buildExportResults(); const results = explorerDataTableTab.value?.buildExportResults();
@@ -80,7 +80,6 @@
/> />
<pie-chart <pie-chart
:items="categoryDimensionTransactionExplorerData && categoryDimensionTransactionExplorerData.length ? categoryDimensionTransactionExplorerData : []" :items="categoryDimensionTransactionExplorerData && categoryDimensionTransactionExplorerData.length ? categoryDimensionTransactionExplorerData : []"
:min-valid-percent="0.0001"
:show-value="true" :show-value="true"
:show-percent="true" :show-percent="true"
:enable-click-item="true" :enable-click-item="true"
@@ -152,7 +151,7 @@ import {
} from '@/lib/common.ts'; } from '@/lib/common.ts';
import { import {
parseDateTimeFromUnixTime parseDateTimeFromString
} from '@/lib/datetime.ts'; } from '@/lib/datetime.ts';
interface InsightsExplorerDataTableTabProps { interface InsightsExplorerDataTableTabProps {
@@ -184,7 +183,7 @@ const {
formatDateTimeToGregorianLikeShortYear, formatDateTimeToGregorianLikeShortYear,
formatDateTimeToGregorianLikeShortYearMonth, formatDateTimeToGregorianLikeShortYearMonth,
formatDateTimeToGregorianLikeYearQuarter, formatDateTimeToGregorianLikeYearQuarter,
formatDateTimeToGregorianLikeFiscalYear, formatGregorianYearToGregorianLikeFiscalYear,
formatAmountToLocalizedNumerals, formatAmountToLocalizedNumerals,
formatAmountToWesternArabicNumeralsWithoutDigitGrouping formatAmountToWesternArabicNumeralsWithoutDigitGrouping
} = useI18n(); } = useI18n();
@@ -238,18 +237,21 @@ function getCategoriedDataDisplayName(info: CategoriedInfo | SeriesedInfo): stri
let name: string = ''; let name: string = '';
let needI18n: boolean | undefined = false; let needI18n: boolean | undefined = false;
let i18nParameters: Record<string, unknown> | undefined = undefined; let i18nParameters: Record<string, unknown> | undefined = undefined;
let dimessionType: TransactionExplorerDataDimensionType = TransactionExplorerDataDimension.None.value; let dimessionType: TransactionExplorerDimensionType = TransactionExplorerDimensionType.Other;
let dimession: TransactionExplorerDataDimensionType = TransactionExplorerDataDimension.None.value;
if ('categoryName' in info) { if ('categoryName' in info) {
name = info.categoryName; name = info.categoryName;
needI18n = info.categoryNameNeedI18n; needI18n = info.categoryNameNeedI18n;
i18nParameters = info.categoryNameI18nParameters; i18nParameters = info.categoryNameI18nParameters;
dimessionType = explorersStore.transactionExplorerFilter.categoryDimension; dimessionType = info.categoryIdType;
dimession = explorersStore.transactionExplorerFilter.categoryDimension;
} else if ('seriesName' in info) { } else if ('seriesName' in info) {
name = info.seriesName; name = info.seriesName;
needI18n = info.seriesNameNeedI18n; needI18n = info.seriesNameNeedI18n;
i18nParameters = info.seriesNameI18nParameters; i18nParameters = info.seriesNameI18nParameters;
dimessionType = explorersStore.transactionExplorerFilter.seriesDimension; dimessionType = info.seriesIdType;
dimession = explorersStore.transactionExplorerFilter.seriesDimension;
} }
let displayName: string = name; let displayName: string = name;
@@ -262,32 +264,40 @@ function getCategoriedDataDisplayName(info: CategoriedInfo | SeriesedInfo): stri
} }
// convert the name to formatted date time if needed // convert the name to formatted date time if needed
if (dimessionType === TransactionExplorerDataDimension.DateTime.value) { if (dimession === TransactionExplorerDataDimension.DateTime.value) {
displayName = formatDateTimeToShortDateTime(parseDateTimeFromUnixTime(parseInt(name))); const dateTime = parseDateTimeFromString(name, dimessionType);
} else if (dimessionType === TransactionExplorerDataDimension.DateTimeByYearMonthDay.value) { displayName = dateTime ? formatDateTimeToShortDateTime(dateTime) : tt('Unknown');
displayName = formatDateTimeToShortDate(parseDateTimeFromUnixTime(parseInt(name))); } else if (dimession === TransactionExplorerDataDimension.DateTimeByYearMonthDay.value) {
} else if (dimessionType === TransactionExplorerDataDimension.DateTimeByYearMonth.value) { const dateTime = parseDateTimeFromString(name, dimessionType);
displayName = formatDateTimeToGregorianLikeShortYearMonth(parseDateTimeFromUnixTime(parseInt(name))); displayName = dateTime ? formatDateTimeToShortDate(dateTime) : tt('Unknown');
} else if (dimessionType === TransactionExplorerDataDimension.DateTimeByYearQuarter.value) { } else if (dimession === TransactionExplorerDataDimension.DateTimeByYearMonth.value) {
displayName = formatDateTimeToGregorianLikeYearQuarter(parseDateTimeFromUnixTime(parseInt(name))); const dateTime = parseDateTimeFromString(name, dimessionType);
} else if (dimessionType === TransactionExplorerDataDimension.DateTimeByYear.value) { displayName = dateTime ? formatDateTimeToGregorianLikeShortYearMonth(dateTime) : tt('Unknown');
displayName = formatDateTimeToGregorianLikeShortYear(parseDateTimeFromUnixTime(parseInt(name))); } else if (dimession === TransactionExplorerDataDimension.DateTimeByYearQuarter.value) {
} else if (dimessionType === TransactionExplorerDataDimension.DateTimeByFiscalYear.value) { const parts = name.split('-');
displayName = formatDateTimeToGregorianLikeFiscalYear(parseDateTimeFromUnixTime(parseInt(name))); const year = parts.length === 2 ? parts[0] : '';
} else if (dimessionType === TransactionExplorerDataDimension.DateTimeByDayOfWeek.value) { const quarter = parts.length === 2 ? parseInt(parts[1] as string) : 0;
const dateTime = year && quarter ? parseDateTimeFromString(`${year}-${quarter * 3}`, TransactionExplorerDimensionType.YearMonth) : undefined;
displayName = dateTime ? formatDateTimeToGregorianLikeYearQuarter(dateTime) : tt('Unknown');
} else if (dimession === TransactionExplorerDataDimension.DateTimeByYear.value) {
const dateTime = parseDateTimeFromString(name, dimessionType);
displayName = dateTime ? formatDateTimeToGregorianLikeShortYear(dateTime) : tt('Unknown');
} else if (dimession === TransactionExplorerDataDimension.DateTimeByFiscalYear.value) {
displayName = formatGregorianYearToGregorianLikeFiscalYear(parseInt(name));
} else if (dimession === TransactionExplorerDataDimension.DateTimeByDayOfWeek.value) {
const weekDay = WeekDay.parse(name); const weekDay = WeekDay.parse(name);
displayName = weekDay ? getWeekdayLongName(weekDay) : tt('Unknown'); displayName = weekDay ? getWeekdayLongName(weekDay) : tt('Unknown');
} else if (dimessionType === TransactionExplorerDataDimension.DateTimeByDayOfMonth.value) { } else if (dimession === TransactionExplorerDataDimension.DateTimeByDayOfMonth.value) {
displayName = getMonthdayShortName(parseInt(name)); displayName = getMonthdayShortName(parseInt(name));
} else if (dimessionType === TransactionExplorerDataDimension.DateTimeByMonthOfYear.value) { } else if (dimession === TransactionExplorerDataDimension.DateTimeByMonthOfYear.value) {
const month = Month.valueOf(parseInt(name)); const month = Month.valueOf(parseInt(name));
displayName = month ? getMonthLongName(month.name) : tt('Unknown'); displayName = month ? getMonthLongName(month.name) : tt('Unknown');
} else if (dimessionType === TransactionExplorerDataDimension.DateTimeByQuarterOfYear.value) { } else if (dimession === TransactionExplorerDataDimension.DateTimeByQuarterOfYear.value) {
displayName = getQuarterName(parseInt(name)); displayName = getQuarterName(parseInt(name));
} }
if (dimessionType === TransactionExplorerDataDimension.SourceAmount.value if (dimession === TransactionExplorerDataDimension.SourceAmount.value
|| dimessionType === TransactionExplorerDataDimension.DestinationAmount.value) { || dimession === TransactionExplorerDataDimension.DestinationAmount.value) {
if (name !== '' && name !== 'none' && Number.isFinite(parseInt(name))) { if (name !== '' && name !== 'none' && Number.isFinite(parseInt(name))) {
displayName = formatAmountToLocalizedNumerals(parseInt(name)); displayName = formatAmountToLocalizedNumerals(parseInt(name));
} }
@@ -15,6 +15,7 @@
<span>{{ getDisplayDateTime(item) }}</span> <span>{{ getDisplayDateTime(item) }}</span>
<v-chip class="ms-1" variant="flat" color="secondary" size="x-small" <v-chip class="ms-1" variant="flat" color="secondary" size="x-small"
v-if="!isSameAsDefaultTimezoneOffsetMinutes(item)">{{ getDisplayTimezone(item) }}</v-chip> v-if="!isSameAsDefaultTimezoneOffsetMinutes(item)">{{ getDisplayTimezone(item) }}</v-chip>
<v-tooltip activator="parent" v-if="!isSameAsDefaultTimezoneOffsetMinutes(item)">{{ getDisplayTimeInDefaultTimezone(item) }}</v-tooltip>
</template> </template>
<template #item.type="{ item }"> <template #item.type="{ item }">
<v-chip label variant="outlined" size="x-small" <v-chip label variant="outlined" size="x-small"
@@ -79,6 +80,7 @@ import { useI18n } from '@/locales/helpers.ts';
import { useUserStore } from '@/stores/user.ts'; import { useUserStore } from '@/stores/user.ts';
import { useExplorersStore } from '@/stores/explorer.ts'; import { useExplorersStore } from '@/stores/explorer.ts';
import type { NumeralSystem } from '@/core/numeral.ts';
import { TransactionType } from '@/core/transaction.ts'; import { TransactionType } from '@/core/transaction.ts';
import { import {
@@ -108,6 +110,7 @@ const emit = defineEmits<{
const { const {
tt, tt,
getCurrentNumeralSystemType,
formatDateTimeToLongDateTime, formatDateTimeToLongDateTime,
formatDateTimeToGregorianDefaultDateTime, formatDateTimeToGregorianDefaultDateTime,
formatAmountToWesternArabicNumeralsWithoutDigitGrouping, formatAmountToWesternArabicNumeralsWithoutDigitGrouping,
@@ -119,6 +122,7 @@ const explorersStore = useExplorersStore();
const currentPage = ref<number>(1); const currentPage = ref<number>(1);
const numeralSystem = computed<NumeralSystem>(() => getCurrentNumeralSystemType());
const defaultCurrency = computed<string>(() => userStore.currentUserDefaultCurrency); const defaultCurrency = computed<string>(() => userStore.currentUserDefaultCurrency);
const filteredTransactions = computed<TransactionInsightDataItem[]>(() => explorersStore.filteredTransactions); const filteredTransactions = computed<TransactionInsightDataItem[]>(() => explorersStore.filteredTransactions);
@@ -172,6 +176,13 @@ function getDisplayTimezone(transaction: TransactionInsightDataItem): string {
return `UTC${getUtcOffsetByUtcOffsetMinutes(transaction.utcOffset)}`; return `UTC${getUtcOffsetByUtcOffsetMinutes(transaction.utcOffset)}`;
} }
function getDisplayTimeInDefaultTimezone(transaction: TransactionInsightDataItem): string {
const timezoneOffsetMinutes = getTimezoneOffsetMinutes(transaction.time);
const dateTime = parseDateTimeFromUnixTimeWithTimezoneOffset(transaction.time, timezoneOffsetMinutes);
const utcOffset = numeralSystem.value.replaceWesternArabicDigitsToLocalizedDigits(getUtcOffsetByUtcOffsetMinutes(timezoneOffsetMinutes));
return `${formatDateTimeToLongDateTime(dateTime)} (UTC${utcOffset})`;
}
function getDisplayTransactionType(transaction: TransactionInsightDataItem): string { function getDisplayTransactionType(transaction: TransactionInsightDataItem): string {
if (transaction.type === TransactionType.ModifyBalance) { if (transaction.type === TransactionType.ModifyBalance) {
return tt('Modify Balance'); return tt('Modify Balance');