diff --git a/eslint.config.mjs b/eslint.config.mjs index 1d07b048..52f19c5b 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -23,17 +23,9 @@ export default [ '**/*.{vue,ts,tsx,mts,js,jsx,cjs,mjs}' ], rules: { - '@typescript-eslint/no-this-alias': ['error', { - allowedNames: ['self'] - }], 'vue/valid-v-slot': ['error', { allowModifiers: true - }], - 'vue/block-lang': ['error', { - script: { - lang: ['ts', 'js'] - } - }], + }] } }, ]; diff --git a/src/core/datetime.ts b/src/core/datetime.ts index f0c96161..987491a4 100644 --- a/src/core/datetime.ts +++ b/src/core/datetime.ts @@ -74,7 +74,7 @@ export interface LocalizedDateRange extends TypeAndDisplayName { readonly isBillingCycle?: boolean; } -export interface LocalizedRecentMonthDateRange { +export interface LocalizedRecentMonthDateRange extends TimeRangeAndDateType { readonly dateType: number; readonly minTime: number; readonly maxTime: number; @@ -420,60 +420,48 @@ export enum DateRangeScene { TrendAnalysis = 1 } -export type DateRangeTypeName = 'All' | - 'Today' | 'Yesterday' | - 'LastSevenDays' | 'LastThirtyDays' | - 'ThisWeek' | 'LastWeek' | - 'ThisMonth' | 'LastMonth' | - 'ThisYear' | 'LastYear' | - 'PreviousBillingCycle' | 'CurrentBillingCycle' | - 'RecentTwelveMonths' | 'RecentTwentyFourMonths' | 'RecentThirtySixMonths' | - 'RecentTwoYears' | 'RecentThreeYears' | 'RecentFiveYears' | - 'Custom'; - export class DateRange implements TypeAndName { private static readonly allInstances: DateRange[] = []; private static readonly allInstancesByType: Record = {}; - private static readonly allInstancesByTypeName: Record = {}; // All date range - public static readonly All = new DateRange(0, 'All', 'All', false, DateRangeScene.Normal, DateRangeScene.TrendAnalysis); + public static readonly All = new DateRange(0, 'All', false, DateRangeScene.Normal, DateRangeScene.TrendAnalysis); // Date ranges for normal scene only - public static readonly Today = new DateRange(1, 'Today', 'Today', false, DateRangeScene.Normal); - public static readonly Yesterday = new DateRange(2, 'Yesterday', 'Yesterday', false, DateRangeScene.Normal); - public static readonly LastSevenDays = new DateRange(3, 'LastSevenDays', 'Recent 7 days', false, DateRangeScene.Normal); - public static readonly LastThirtyDays = new DateRange(4, 'LastThirtyDays', 'Recent 30 days', false, DateRangeScene.Normal); - public static readonly ThisWeek = new DateRange(5, 'ThisWeek', 'This week', false, DateRangeScene.Normal); - public static readonly LastWeek = new DateRange(6, 'LastWeek', 'Last week', false, DateRangeScene.Normal); - public static readonly ThisMonth = new DateRange(7, 'ThisMonth', 'This month', false, DateRangeScene.Normal); - public static readonly LastMonth = new DateRange(8, 'LastMonth', 'Last month', false, DateRangeScene.Normal); + public static readonly Today = new DateRange(1, 'Today', false, DateRangeScene.Normal); + public static readonly Yesterday = new DateRange(2, 'Yesterday', false, DateRangeScene.Normal); + public static readonly LastSevenDays = new DateRange(3, 'Recent 7 days', false, DateRangeScene.Normal); + public static readonly LastThirtyDays = new DateRange(4, 'Recent 30 days', false, DateRangeScene.Normal); + public static readonly ThisWeek = new DateRange(5, 'This week', false, DateRangeScene.Normal); + public static readonly LastWeek = new DateRange(6, 'Last week', false, DateRangeScene.Normal); + public static readonly ThisMonth = new DateRange(7, 'This month', false, DateRangeScene.Normal); + public static readonly LastMonth = new DateRange(8, 'Last month', false, DateRangeScene.Normal); // Date ranges for normal and trend analysis scene - public static readonly ThisYear = new DateRange(9, 'ThisYear', 'This year', false, DateRangeScene.Normal, DateRangeScene.TrendAnalysis); - public static readonly LastYear = new DateRange(10, 'LastYear', 'Last year', false, DateRangeScene.Normal, DateRangeScene.TrendAnalysis); + public static readonly ThisYear = new DateRange(9, 'This year', false, DateRangeScene.Normal, DateRangeScene.TrendAnalysis); + public static readonly LastYear = new DateRange(10, 'Last year', false, DateRangeScene.Normal, DateRangeScene.TrendAnalysis); // Billing cycle date ranges for normal scene only - public static readonly PreviousBillingCycle = new DateRange(51, 'PreviousBillingCycle', 'Previous Billing Cycle', true, DateRangeScene.Normal); - public static readonly CurrentBillingCycle = new DateRange(52, 'CurrentBillingCycle', 'Current Billing Cycle', true, DateRangeScene.Normal); + public static readonly PreviousBillingCycle = new DateRange(51, 'Previous Billing Cycle', true, DateRangeScene.Normal); + public static readonly CurrentBillingCycle = new DateRange(52, 'Current Billing Cycle', true, DateRangeScene.Normal); // Date ranges for trend analysis scene only - public static readonly RecentTwelveMonths = new DateRange(101, 'RecentTwelveMonths', 'Recent 12 months', false, DateRangeScene.TrendAnalysis); - public static readonly RecentTwentyFourMonths = new DateRange(102, 'RecentTwentyFourMonths', 'Recent 24 months', false, DateRangeScene.TrendAnalysis); - public static readonly RecentThirtySixMonths = new DateRange(103, 'RecentThirtySixMonths', 'Recent 36 months', false, DateRangeScene.TrendAnalysis); - public static readonly RecentTwoYears = new DateRange(104, 'RecentTwoYears', 'Recent 2 years', false, DateRangeScene.TrendAnalysis); - public static readonly RecentThreeYears = new DateRange(105, 'RecentThreeYears', 'Recent 3 years', false, DateRangeScene.TrendAnalysis); - public static readonly RecentFiveYears = new DateRange(106, 'RecentFiveYears', 'Recent 5 years', false, DateRangeScene.TrendAnalysis); + public static readonly RecentTwelveMonths = new DateRange(101, 'Recent 12 months', false, DateRangeScene.TrendAnalysis); + public static readonly RecentTwentyFourMonths = new DateRange(102, 'Recent 24 months', false, DateRangeScene.TrendAnalysis); + public static readonly RecentThirtySixMonths = new DateRange(103, 'Recent 36 months', false, DateRangeScene.TrendAnalysis); + public static readonly RecentTwoYears = new DateRange(104, 'Recent 2 years', false, DateRangeScene.TrendAnalysis); + public static readonly RecentThreeYears = new DateRange(105, 'Recent 3 years', false, DateRangeScene.TrendAnalysis); + public static readonly RecentFiveYears = new DateRange(106, 'Recent 5 years', false, DateRangeScene.TrendAnalysis); // Custom date range - public static readonly Custom = new DateRange(255, 'Custom', 'Custom Date', false, DateRangeScene.Normal, DateRangeScene.TrendAnalysis); + public static readonly Custom = new DateRange(255, 'Custom Date', false, DateRangeScene.Normal, DateRangeScene.TrendAnalysis); public readonly type: number; public readonly name: string; public readonly isBillingCycle: boolean; private readonly availableScenes: Record; - private constructor(type: number, typeName: DateRangeTypeName, name: string, isBillingCycle: boolean, ...availableScenes: DateRangeScene[]) { + private constructor(type: number, name: string, isBillingCycle: boolean, ...availableScenes: DateRangeScene[]) { this.type = type; this.name = name; this.isBillingCycle = isBillingCycle; @@ -487,7 +475,6 @@ export class DateRange implements TypeAndName { DateRange.allInstances.push(this); DateRange.allInstancesByType[type] = this; - DateRange.allInstancesByTypeName[typeName] = this; } public isAvailableForScene(scene: DateRangeScene): boolean { @@ -498,10 +485,6 @@ export class DateRange implements TypeAndName { return DateRange.allInstances; } - public static all(): Record { - return DateRange.allInstancesByTypeName; - } - public static valueOf(type: number): DateRange | undefined { return DateRange.allInstancesByType[type]; } diff --git a/src/desktop-main.ts b/src/desktop-main.ts index 29394cc1..d201474e 100644 --- a/src/desktop-main.ts +++ b/src/desktop-main.ts @@ -71,8 +71,6 @@ import draggable from 'vuedraggable'; import router from '@/router/desktop.ts'; import { getI18nOptions } from '@/locales/helpers.ts'; -// @ts-expect-error the above file is migrating to ts -import { i18nFunctions } from '@/locales/helper.js'; import PinCodeInput from '@/components/common/PinCodeInput.vue'; import MapView from '@/components/common/MapView.vue'; @@ -465,6 +463,4 @@ app.component('DateRangeSelectionDialog', DateRangeSelectionDialog); app.component('MonthRangeSelectionDialog', MonthRangeSelectionDialog); app.component('SwitchToMobileDialog', SwitchToMobileDialog); -app.config.globalProperties['$locale'] = i18nFunctions(i18n.global); - app.mount('#app'); diff --git a/src/lib/datetime.ts b/src/lib/datetime.ts index d1060c31..c74192bc 100644 --- a/src/lib/datetime.ts +++ b/src/lib/datetime.ts @@ -545,7 +545,7 @@ export function getShiftedDateRangeAndDateType(minTime: number, maxTime: number, }; } -export function getShiftedDateRangeAndDateTypeForBillingCycle(minTime: number, maxTime: number, scale: number, firstDayOfWeek: number, scene: number, statementDate: number): TimeRangeAndDateType | null { +export function getShiftedDateRangeAndDateTypeForBillingCycle(minTime: number, maxTime: number, scale: number, firstDayOfWeek: number, scene: number, statementDate: number | undefined | null): TimeRangeAndDateType | null { if (!statementDate || !DateRange.PreviousBillingCycle.isAvailableForScene(scene) || !DateRange.CurrentBillingCycle.isAvailableForScene(scene)) { return null; } @@ -588,7 +588,7 @@ export function getDateTypeByDateRange(minTime: number, maxTime: number, firstDa return newDateType; } -export function getDateTypeByBillingCycleDateRange(minTime: number, maxTime: number, firstDayOfWeek: number, scene: DateRangeScene, statementDate: number): number | null { +export function getDateTypeByBillingCycleDateRange(minTime: number, maxTime: number, firstDayOfWeek: number, scene: DateRangeScene, statementDate: number | undefined | null): number | null { if (!statementDate || !DateRange.PreviousBillingCycle.isAvailableForScene(scene) || !DateRange.CurrentBillingCycle.isAvailableForScene(scene)) { return null; } @@ -605,7 +605,7 @@ export function getDateTypeByBillingCycleDateRange(minTime: number, maxTime: num return null; } -export function getDateRangeByDateType(dateType: number, firstDayOfWeek: number): TimeRangeAndDateType | null { +export function getDateRangeByDateType(dateType: number | undefined, firstDayOfWeek: number): TimeRangeAndDateType | null { let maxTime = 0; let minTime = 0; @@ -671,7 +671,7 @@ export function getDateRangeByDateType(dateType: number, firstDayOfWeek: number) }; } -export function getDateRangeByBillingCycleDateType(dateType: number, firstDayOfWeek: number, statementDate: number): TimeRangeAndDateType | null { +export function getDateRangeByBillingCycleDateType(dateType: number, firstDayOfWeek: number, statementDate: number | undefined | null): TimeRangeAndDateType | null { let maxTime = 0; let minTime = 0; diff --git a/src/lib/transaction.ts b/src/lib/transaction.ts index e04d1cb1..5dccc7e2 100644 --- a/src/lib/transaction.ts +++ b/src/lib/transaction.ts @@ -3,7 +3,7 @@ import { TransactionType } from '@/core/transaction.ts'; import { Account } from '@/models/account.ts'; import { TransactionCategory } from '@/models/transaction_category.ts'; import { TransactionTag } from '@/models/transaction_tag.ts'; -import {Transaction, TransactionPicture} from '@/models/transaction.ts'; +import { Transaction, TransactionPicture } from '@/models/transaction.ts'; import { isNumber @@ -30,14 +30,6 @@ export interface SetTransactionOptions { comment?: string; } -function getDisplayAmount(amount: number, currency: string, hideAmount: boolean, formatAmountWithCurrencyFunc: (value: number | string, currencyCode?: string) => string): string { - if (hideAmount) { - return formatAmountWithCurrencyFunc('***', currency); - } - - return formatAmountWithCurrencyFunc(amount, currency); -} - export function setTransactionModelByTransaction(transaction: Transaction, transaction2: Transaction | null | undefined, allCategories: Record, allCategoriesMap: Record, allVisibleAccounts: Account[], allAccountsMap: Record, allTagsMap: Record, defaultAccountId: string, options: SetTransactionOptions, setContextData: boolean, convertContextTime: boolean): void { if (!options.type && options.categoryId && options.categoryId !== '0' && allCategoriesMap[options.categoryId]) { const category = allCategoriesMap[options.categoryId]; @@ -190,33 +182,3 @@ export function setTransactionModelByTransaction(transaction: Transaction, trans } } } - -export function getTransactionDisplayAmount(transaction: Transaction, allFilterAccountIdsCount: number, allFilterAccountIds: Record, formatAmountWithCurrencyFunc: (value: number | string, currencyCode?: string) => string): string { - if (allFilterAccountIdsCount < 1) { - if (transaction.sourceAccount) { - return getDisplayAmount(transaction.sourceAmount, transaction.sourceAccount.currency, transaction.hideAmount, formatAmountWithCurrencyFunc); - } - } else if (allFilterAccountIdsCount === 1) { - if (transaction.sourceAccount && (allFilterAccountIds[transaction.sourceAccount.id] || allFilterAccountIds[transaction.sourceAccount.parentId])) { - return getDisplayAmount(transaction.sourceAmount, transaction.sourceAccount.currency, transaction.hideAmount , formatAmountWithCurrencyFunc); - } else if (transaction.destinationAccount && (allFilterAccountIds[transaction.destinationAccount.id] || allFilterAccountIds[transaction.destinationAccount.parentId])) { - return getDisplayAmount(transaction.destinationAmount, transaction.destinationAccount.currency, transaction.hideAmount , formatAmountWithCurrencyFunc); - } - } else { // allFilterAccountIdsCount > 1 - if (transaction.sourceAccount && transaction.destinationAccount) { - if ((allFilterAccountIds[transaction.sourceAccount.id] || allFilterAccountIds[transaction.sourceAccount.parentId]) - && !allFilterAccountIds[transaction.destinationAccount.id] && !allFilterAccountIds[transaction.destinationAccount.parentId]) { - return getDisplayAmount(transaction.sourceAmount, transaction.sourceAccount.currency, transaction.hideAmount , formatAmountWithCurrencyFunc); - } else if ((allFilterAccountIds[transaction.destinationAccount.id] || allFilterAccountIds[transaction.destinationAccount.parentId]) - && !allFilterAccountIds[transaction.sourceAccount.id] && !allFilterAccountIds[transaction.sourceAccount.parentId]) { - return getDisplayAmount(transaction.destinationAmount, transaction.destinationAccount.currency, transaction.hideAmount , formatAmountWithCurrencyFunc); - } - } - } - - if (transaction.sourceAccount) { - return getDisplayAmount(transaction.sourceAmount, transaction.sourceAccount.currency, transaction.hideAmount, formatAmountWithCurrencyFunc); - } - - return ''; -} diff --git a/src/lib/ui/desktop.ts b/src/lib/ui/desktop.ts index d2681966..dc8e83dc 100644 --- a/src/lib/ui/desktop.ts +++ b/src/lib/ui/desktop.ts @@ -45,7 +45,7 @@ export function getCssValue(element: HTMLElement | null, name: string): string { return computedStyle.getPropertyValue(name); } -export function scrollToSelectedItem(parentEl: HTMLElement | null, containerSelector: string | null, selectedItemSelector: string): void { +export function scrollToSelectedItem(parentEl: HTMLElement | null | undefined, containerSelector: string | null, selectedItemSelector: string): void { if (!parentEl) { return; } diff --git a/src/lib/ui/mobile.ts b/src/lib/ui/mobile.ts index 6aaadc46..a651a547 100644 --- a/src/lib/ui/mobile.ts +++ b/src/lib/ui/mobile.ts @@ -1,13 +1,12 @@ import { type Ref, watch } from 'vue'; -import { useI18n as useVueI18n } from 'vue-i18n'; import { f7, f7ready } from 'framework7-vue'; import type { Dialog, Picker, Router } from 'framework7/types'; +import { useI18n } from '@/locales/helpers.ts'; + import { FontSize, FONT_SIZE_PREVIEW_CLASSNAME_PREFIX } from '@/core/font.ts'; import { getNumberValue } from '../common.ts'; import { isEnableAnimate } from '../settings.ts'; -// @ts-expect-error the above file is migrating to ts -import { translateError } from '@/locales/helper.js'; export interface Framework7Dom { length: number; @@ -19,34 +18,6 @@ export interface Framework7Dom { css(property: string): string | number; } -type TranslateFunction = (message: string) => string; - -export function showAlert(message: string, confirmCallback: ((dialog: Dialog.Dialog, e: Event) => void) | undefined, translateFn: TranslateFunction): void { - f7ready((f7) => { - f7.dialog.create({ - title: translateFn('global.app.title'), - text: translateError(message, translateFn), - animate: isEnableAnimate(), - buttons: [ - { - text: translateFn('OK'), - onClick: confirmCallback - } - ] - }).open(); - }); -} - -export function showToast(message: string, timeout: number | undefined, translateFn: TranslateFunction): void { - f7ready((f7) => { - f7.toast.create({ - text: translateError(message, translateFn), - position: 'center', - closeTimeout: timeout || 1500 - }).open(); - }); -} - export function showLoading(delayConditionFunc?: () => boolean, delayMills?: number): void { if (!delayConditionFunc) { f7ready((f7) => { @@ -82,26 +53,6 @@ export function createInlinePicker(containerEl: string, inputEl: string, cols: P }); } -export function routeBackOnError(f7router: Router.Router, errorPropertyName: string): void { - // @ts-expect-error vue SFC would be migrated to composition API and this function would be removed in the future - const self = this; - const router = f7router; - - const unwatch = self.$watch(errorPropertyName, () => { - if (self[errorPropertyName]) { - setTimeout(() => { - if (unwatch) { - unwatch(); - } - - router.back(); - }, 200); - } - }, { - immediate: true - }); -} - export function isModalShowing(): number { return f7.$('.modal-in').length; } @@ -190,7 +141,7 @@ export function scrollToSelectedItem(parentEl: Framework7Dom, containerSelector: } export function useI18nUIComponents() { - const { t } = useVueI18n(); + const { tt, te } = useI18n(); function routeBackOnError(f7router: Router.Router, errorRef: Ref): void { const unwatch = watch(errorRef, (newValue) => { @@ -208,19 +159,15 @@ export function useI18nUIComponents() { }); } - function showConfirm(message: string, confirmCallback?: (dialog: Dialog.Dialog, e: Event) => void, cancelCallback?: ((dialog: Dialog.Dialog, e: Event) => void) | undefined): void { + function showAlert(message: string, confirmCallback?: (dialog: Dialog.Dialog, e: Event) => void): void { f7ready((f7) => { f7.dialog.create({ - title: t('global.app.title'), - text: t(message), + title: tt('global.app.title'), + text: te(message), animate: isEnableAnimate(), buttons: [ { - text: t('Cancel'), - onClick: cancelCallback - }, - { - text: t('OK'), + text: tt('OK'), onClick: confirmCallback } ] @@ -228,10 +175,40 @@ export function useI18nUIComponents() { }); } + function showConfirm(message: string, confirmCallback?: (dialog: Dialog.Dialog, e: Event) => void, cancelCallback?: (dialog: Dialog.Dialog, e: Event) => void): void { + f7ready((f7) => { + f7.dialog.create({ + title: tt('global.app.title'), + text: tt(message), + animate: isEnableAnimate(), + buttons: [ + { + text: tt('Cancel'), + onClick: cancelCallback + }, + { + text: tt('OK'), + onClick: confirmCallback + } + ] + }).open(); + }); + } + + function showToast(message: string, timeout?: number): void { + f7ready((f7) => { + f7.toast.create({ + text: te(message), + position: 'center', + closeTimeout: timeout || 1500 + }).open(); + }); + } + return { - showAlert: (message: string, confirmCallback?: (dialog: Dialog.Dialog, e: Event) => void) => showAlert(message, confirmCallback, t), + showAlert: showAlert, showConfirm: showConfirm, - showToast: (message: string, timeout?: number): void => showToast(message, timeout, t), + showToast: showToast, routeBackOnError } } diff --git a/src/locales/helper.js b/src/locales/helper.js deleted file mode 100644 index 993121d7..00000000 --- a/src/locales/helper.js +++ /dev/null @@ -1,442 +0,0 @@ -import { LongDateFormat, ShortDateFormat, LongTimeFormat, ShortTimeFormat, DateRange } from '@/core/datetime.ts'; -import { DecimalSeparator, DigitGroupingSymbol, DigitGroupingType } from '@/core/numeral.ts'; -import { CurrencyDisplayType } from '@/core/currency.ts' -import { TransactionTagFilterType } from '@/core/transaction.ts'; - -import { ALL_CURRENCIES } from '@/consts/currency.ts'; -import { KnownErrorCode, SPECIFIED_API_NOT_FOUND_ERRORS, PARAMETERIZED_ERRORS } from '@/consts/api.ts'; - -import { - isString, - isNumber, - isBoolean -} from '@/lib/common.ts'; - -import { - parseDateFromUnixTime, - formatUnixTime, - getYear, - getDateTimeFormatType, - getRecentMonthDateRanges, - isDateRangeMatchFullYears, - isDateRangeMatchFullMonths -} from '@/lib/datetime.ts'; - -import { - formatAmount -} from '@/lib/numeral.ts'; - -import { - getCurrencyFraction, - appendCurrencySymbol -} from '@/lib/currency.ts'; - -function getLocalizedDisplayNameAndType(typeAndNames, translateFn) { - const ret = []; - - for (let i = 0; i < typeAndNames.length; i++) { - const nameAndType = typeAndNames[i]; - - ret.push({ - type: nameAndType.type, - displayName: translateFn(nameAndType.name) - }); - } - - return ret; -} - -function getCurrencyName(currencyCode, translateFn) { - return translateFn(`currency.name.${currencyCode}`); -} - -function getCurrencyUnitName(currencyCode, isPlural, translateFn) { - const currencyInfo = ALL_CURRENCIES[currencyCode]; - - if (currencyInfo && currencyInfo.unit) { - if (isPlural) { - return translateFn(`currency.unit.${currencyInfo.unit}.plural`); - } else { - return translateFn(`currency.unit.${currencyInfo.unit}.normal`); - } - } - - return ''; -} - -function getWeekdayShortName(weekDayName, translateFn) { - return translateFn(`datetime.${weekDayName}.short`); -} - -function getWeekdayLongName(weekDayName, translateFn) { - return translateFn(`datetime.${weekDayName}.long`); -} - -function getI18nLongDateFormat(translateFn, formatTypeValue) { - const defaultLongDateFormatTypeName = translateFn('default.longDateFormat'); - return getDateTimeFormat(translateFn, LongDateFormat.all(), LongDateFormat.values(), 'format.longDate', defaultLongDateFormatTypeName, LongDateFormat.Default, formatTypeValue); -} - -function getI18nShortDateFormat(translateFn, formatTypeValue) { - const defaultShortDateFormatTypeName = translateFn('default.shortDateFormat'); - return getDateTimeFormat(translateFn, ShortDateFormat.all(), ShortDateFormat.values(), 'format.shortDate', defaultShortDateFormatTypeName, ShortDateFormat.Default, formatTypeValue); -} - -function getI18nLongYearFormat(translateFn, formatTypeValue) { - const defaultLongDateFormatTypeName = translateFn('default.longDateFormat'); - return getDateTimeFormat(translateFn, LongDateFormat.all(), LongDateFormat.values(), 'format.longYear', defaultLongDateFormatTypeName, LongDateFormat.Default, formatTypeValue); -} - -function getI18nShortYearFormat(translateFn, formatTypeValue) { - const defaultShortDateFormatTypeName = translateFn('default.shortDateFormat'); - return getDateTimeFormat(translateFn, ShortDateFormat.all(), ShortDateFormat.values(), 'format.shortYear', defaultShortDateFormatTypeName, ShortDateFormat.Default, formatTypeValue); -} - -function getI18nLongYearMonthFormat(translateFn, formatTypeValue) { - const defaultLongDateFormatTypeName = translateFn('default.longDateFormat'); - return getDateTimeFormat(translateFn, LongDateFormat.all(), LongDateFormat.values(), 'format.longYearMonth', defaultLongDateFormatTypeName, LongDateFormat.Default, formatTypeValue); -} - -function getI18nShortYearMonthFormat(translateFn, formatTypeValue) { - const defaultShortDateFormatTypeName = translateFn('default.shortDateFormat'); - return getDateTimeFormat(translateFn, ShortDateFormat.all(), ShortDateFormat.values(), 'format.shortYearMonth', defaultShortDateFormatTypeName, ShortDateFormat.Default, formatTypeValue); -} - -function getI18nShortMonthDayFormat(translateFn, formatTypeValue) { - const defaultShortDateFormatTypeName = translateFn('default.shortDateFormat'); - return getDateTimeFormat(translateFn, ShortDateFormat.all(), ShortDateFormat.values(), 'format.shortMonthDay', defaultShortDateFormatTypeName, ShortDateFormat.Default, formatTypeValue); -} - -function getI18nLongTimeFormat(translateFn, formatTypeValue) { - const defaultLongTimeFormatTypeName = translateFn('default.longTimeFormat'); - return getDateTimeFormat(translateFn, LongTimeFormat.all(), LongTimeFormat.values(), 'format.longTime', defaultLongTimeFormatTypeName, LongTimeFormat.Default, formatTypeValue); -} - -function getI18nShortTimeFormat(translateFn, formatTypeValue) { - const defaultShortTimeFormatTypeName = translateFn('default.shortTimeFormat'); - return getDateTimeFormat(translateFn, ShortTimeFormat.all(), ShortTimeFormat.values(), 'format.shortTime', defaultShortTimeFormatTypeName, ShortTimeFormat.Default, formatTypeValue); -} - -function getDateTimeFormat(translateFn, allFormatMap, allFormatArray, localeFormatPathPrefix, localeDefaultFormatTypeName, systemDefaultFormatType, formatTypeValue) { - const type = getDateTimeFormatType(allFormatMap, allFormatArray, formatTypeValue, localeDefaultFormatTypeName, systemDefaultFormatType); - return translateFn(`${localeFormatPathPrefix}.${type.key}`); -} - -function getAllDateRanges(scene, includeCustom, includeBillingCycle, translateFn) { - const ret = []; - const allDateRanges = DateRange.values(); - - for (let i = 0; i < allDateRanges.length; i++) { - const dateRange = allDateRanges[i]; - - if (!dateRange.isAvailableForScene(scene)) { - continue; - } - - if (dateRange.isBillingCycle) { - if (includeBillingCycle) { - ret.push({ - type: dateRange.type, - displayName: translateFn(dateRange.name), - isBillingCycle: dateRange.isBillingCycle - }); - } - - continue; - } - - if (includeCustom || dateRange.type !== DateRange.Custom.type) { - ret.push({ - type: dateRange.type, - displayName: translateFn(dateRange.name) - }); - } - } - - return ret; -} - -function getAllRecentMonthDateRanges(userStore, includeAll, includeCustom, translateFn) { - const allRecentMonthDateRanges = []; - const recentDateRanges = getRecentMonthDateRanges(12); - - if (includeAll) { - allRecentMonthDateRanges.push({ - dateType: DateRange.All.type, - minTime: 0, - maxTime: 0, - displayName: translateFn('All') - }); - } - - for (let i = 0; i < recentDateRanges.length; i++) { - const recentDateRange = recentDateRanges[i]; - - allRecentMonthDateRanges.push({ - dateType: recentDateRange.dateType, - minTime: recentDateRange.minTime, - maxTime: recentDateRange.maxTime, - year: recentDateRange.year, - month: recentDateRange.month, - isPreset: true, - displayName: formatUnixTime(recentDateRange.minTime, getI18nLongYearMonthFormat(translateFn, userStore.currentUserLongDateFormat)) - }); - } - - if (includeCustom) { - allRecentMonthDateRanges.push({ - dateType: DateRange.Custom.type, - minTime: 0, - maxTime: 0, - displayName: translateFn('Custom Date') - }); - } - - return allRecentMonthDateRanges; -} - -function getDateRangeDisplayName(userStore, dateType, startTime, endTime, translateFn) { - if (dateType === DateRange.All.type) { - return translateFn(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 translateFn(dateRange.name); - } - } - - if (isDateRangeMatchFullYears(startTime, endTime)) { - const displayStartTime = formatUnixTime(startTime, getI18nShortYearFormat(translateFn, userStore.currentUserShortDateFormat)); - const displayEndTime = formatUnixTime(endTime, getI18nShortYearFormat(translateFn, userStore.currentUserShortDateFormat)); - - return displayStartTime !== displayEndTime ? `${displayStartTime} ~ ${displayEndTime}` : displayStartTime; - } - - if (isDateRangeMatchFullMonths(startTime, endTime)) { - const displayStartTime = formatUnixTime(startTime, getI18nShortYearMonthFormat(translateFn, userStore.currentUserShortDateFormat)); - const displayEndTime = formatUnixTime(endTime, getI18nShortYearMonthFormat(translateFn, userStore.currentUserShortDateFormat)); - - return displayStartTime !== displayEndTime ? `${displayStartTime} ~ ${displayEndTime}` : displayStartTime; - } - - const startTimeYear = getYear(parseDateFromUnixTime(startTime)); - const endTimeYear = getYear(parseDateFromUnixTime(endTime)); - - const displayStartTime = formatUnixTime(startTime, getI18nShortDateFormat(translateFn, userStore.currentUserShortDateFormat)); - const displayEndTime = formatUnixTime(endTime, getI18nShortDateFormat(translateFn, userStore.currentUserShortDateFormat)); - - if (displayStartTime === displayEndTime) { - return displayStartTime; - } else if (startTimeYear === endTimeYear) { - const displayShortEndTime = formatUnixTime(endTime, getI18nShortMonthDayFormat(translateFn, userStore.currentUserShortDateFormat)); - return `${displayStartTime} ~ ${displayShortEndTime}`; - } - - return `${displayStartTime} ~ ${displayEndTime}`; -} - -function getCurrentDecimalSeparator(translateFn, decimalSeparator) { - let decimalSeparatorType = DecimalSeparator.valueOf(decimalSeparator); - - if (!decimalSeparatorType) { - const defaultDecimalSeparatorTypeName = translateFn('default.decimalSeparator'); - decimalSeparatorType = DecimalSeparator.parse(defaultDecimalSeparatorTypeName); - - if (!decimalSeparatorType) { - decimalSeparatorType = DecimalSeparator.Default; - } - } - - return decimalSeparatorType.symbol; -} - -function getCurrentDigitGroupingSymbol(translateFn, digitGroupingSymbol) { - let digitGroupingSymbolType = DigitGroupingSymbol.valueOf(digitGroupingSymbol); - - if (!digitGroupingSymbolType) { - const defaultDigitGroupingSymbolTypeName = translateFn('default.digitGroupingSymbol'); - digitGroupingSymbolType = DigitGroupingSymbol.parse(defaultDigitGroupingSymbolTypeName); - - if (!digitGroupingSymbolType) { - digitGroupingSymbolType = DigitGroupingSymbol.Default; - } - } - - return digitGroupingSymbolType.symbol; -} - -function getCurrentDigitGroupingType(translateFn, digitGrouping) { - let digitGroupingType = DigitGroupingType.valueOf(digitGrouping); - - if (!digitGroupingType) { - const defaultDigitGroupingTypeName = translateFn('default.digitGrouping'); - digitGroupingType = DigitGroupingType.parse(defaultDigitGroupingTypeName); - - if (!digitGroupingType) { - digitGroupingType = DigitGroupingType.Default; - } - } - - return digitGroupingType.type; -} - -function getNumberFormatOptions(translateFn, userStore, currencyCode) { - return { - decimalSeparator: getCurrentDecimalSeparator(translateFn, userStore.currentUserDecimalSeparator), - decimalNumberCount: getCurrencyFraction(currencyCode), - digitGroupingSymbol: getCurrentDigitGroupingSymbol(translateFn, userStore.currentUserDigitGroupingSymbol), - digitGrouping: getCurrentDigitGroupingType(translateFn, userStore.currentUserDigitGrouping), - }; -} - -function getCurrentCurrencyDisplayType(translateFn, userStore) { - let currencyDisplayType = CurrencyDisplayType.valueOf(userStore.currentUserCurrencyDisplayType); - - if (!currencyDisplayType) { - const defaultCurrencyDisplayTypeName = translateFn('default.currencyDisplayType'); - currencyDisplayType = CurrencyDisplayType.parse(defaultCurrencyDisplayTypeName); - } - - if (!currencyDisplayType) { - currencyDisplayType = CurrencyDisplayType.Default; - } - - return currencyDisplayType; -} - -function getFormattedAmountWithCurrency(value, currencyCode, translateFn, userStore, settingsStore, notConvertValue, currencyDisplayType) { - if (!isNumber(value) && !isString(value)) { - return value; - } - - if (isNumber(value)) { - value = value.toString(); - } - - const isPlural = value !== '100' && value !== '-100'; - - if (!notConvertValue) { - const numberFormatOptions = getNumberFormatOptions(translateFn, userStore, currencyCode); - const hasIncompleteFlag = isString(value) && value.charAt(value.length - 1) === '+'; - - if (hasIncompleteFlag) { - value = value.substring(0, value.length - 1); - } - - value = formatAmount(value, numberFormatOptions); - - if (hasIncompleteFlag) { - value = value + '+'; - } - } - - if (!isBoolean(currencyCode) && !currencyCode) { - currencyCode = userStore.currentUserDefaultCurrency; - } else if (isBoolean(currencyCode) && !currencyCode) { - currencyCode = ''; - } - - if (!currencyCode) { - return value; - } - - if (!currencyDisplayType) { - currencyDisplayType = getCurrentCurrencyDisplayType(translateFn, userStore); - } - - const currencyUnit = getCurrencyUnitName(currencyCode, isPlural, translateFn); - const currencyName = getCurrencyName(currencyCode, translateFn); - return appendCurrencySymbol(value, currencyDisplayType, currencyCode, currencyUnit, currencyName, isPlural); -} - -function getAllTransactionTagFilterTypes(translateFn) { - return getLocalizedDisplayNameAndType(TransactionTagFilterType.values(), translateFn); -} - -function getLocalizedError(error) { - if (error.errorCode === KnownErrorCode.ApiNotFound && SPECIFIED_API_NOT_FOUND_ERRORS[error.path]) { - return { - message: `${SPECIFIED_API_NOT_FOUND_ERRORS[error.path].message}` - }; - } - - if (error.errorCode !== KnownErrorCode.ValidatorError) { - return { - message: `error.${error.errorMessage}` - }; - } - - for (let i = 0; i < PARAMETERIZED_ERRORS.length; i++) { - const errorInfo = PARAMETERIZED_ERRORS[i]; - const matches = error.errorMessage.match(errorInfo.regex); - - if (matches && matches.length === errorInfo.parameters.length + 1) { - return { - message: `parameterizedError.${errorInfo.localeKey}`, - parameters: errorInfo.parameters.map((param, index) => { - return { - key: param.field, - localized: param.localized, - value: matches[index + 1] - } - }) - }; - } - } - - return { - message: `error.${error.errorMessage}` - }; -} - -function getLocalizedErrorParameters(parameters, i18nFunc) { - let localizedParameters = {}; - - if (parameters) { - for (let i = 0; i < parameters.length; i++) { - const parameter = parameters[i]; - - if (parameter.localized) { - localizedParameters[parameter.key] = i18nFunc(`parameter.${parameter.value}`); - } else { - localizedParameters[parameter.key] = parameter.value; - } - } - } - - return localizedParameters; -} - -export function translateError(message, translateFn) { - let parameters = {}; - - if (message && message.error) { - const localizedError = getLocalizedError(message.error); - message = localizedError.message; - parameters = getLocalizedErrorParameters(localizedError.parameters, translateFn); - } - - return translateFn(message, parameters); -} - -export function i18nFunctions(i18nGlobal) { - return { - getWeekdayShortName: (weekDay) => getWeekdayShortName(weekDay, i18nGlobal.t), - getWeekdayLongName: (weekDay) => getWeekdayLongName(weekDay, i18nGlobal.t), - formatUnixTimeToLongDateTime: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nLongDateFormat(i18nGlobal.t, userStore.currentUserLongDateFormat) + ' ' + getI18nLongTimeFormat(i18nGlobal.t, userStore.currentUserLongTimeFormat), utcOffset, currentUtcOffset), - formatUnixTimeToLongDate: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nLongDateFormat(i18nGlobal.t, userStore.currentUserLongDateFormat), utcOffset, currentUtcOffset), - formatUnixTimeToLongYear: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nLongYearFormat(i18nGlobal.t, userStore.currentUserLongDateFormat), utcOffset, currentUtcOffset), - formatUnixTimeToLongYearMonth: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nLongYearMonthFormat(i18nGlobal.t, userStore.currentUserLongDateFormat), utcOffset, currentUtcOffset), - formatUnixTimeToShortTime: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nShortTimeFormat(i18nGlobal.t, userStore.currentUserShortTimeFormat), utcOffset, currentUtcOffset), - getAllDateRanges: (scene, includeCustom, includeBillingCycle) => getAllDateRanges(scene, includeCustom, includeBillingCycle, i18nGlobal.t), - getAllRecentMonthDateRanges: (userStore, includeAll, includeCustom) => getAllRecentMonthDateRanges(userStore, includeAll, includeCustom, i18nGlobal.t), - getDateRangeDisplayName: (userStore, dateType, startTime, endTime) => getDateRangeDisplayName(userStore, dateType, startTime, endTime, i18nGlobal.t), - formatAmountWithCurrency: (settingsStore, userStore, value, currencyCode) => getFormattedAmountWithCurrency(value, currencyCode, i18nGlobal.t, userStore, settingsStore), - getAllTransactionTagFilterTypes: () => getAllTransactionTagFilterTypes(i18nGlobal.t) - }; -} diff --git a/src/mobile-main.ts b/src/mobile-main.ts index f52f59df..5b7b5636 100644 --- a/src/mobile-main.ts +++ b/src/mobile-main.ts @@ -38,7 +38,6 @@ import Framework7Swiper from 'framework7/components/swiper'; import Framework7PhotoBrowser from 'framework7/components/photo-browser'; // @ts-expect-error there is a function called "registerComponents" in the framework7-vue package, but it is not declared in the type definition file import Framework7Vue, { registerComponents } from 'framework7-vue/bundle'; -import type { Dialog } from 'framework7/types'; import 'framework7/css'; import 'framework7/components/dialog/css'; @@ -82,15 +81,6 @@ import VueDatePicker from '@vuepic/vue-datepicker'; import '@vuepic/vue-datepicker/dist/main.css'; import { getI18nOptions } from '@/locales/helpers.ts'; -// @ts-expect-error the above file is migrating to ts -import { i18nFunctions } from '@/locales/helper.js'; -import { - showAlert, - showToast, - showLoading, - hideLoading, - routeBackOnError -} from '@/lib/ui/mobile.ts'; import PinCodeInput from '@/components/common/PinCodeInput.vue'; import MapView from '@/components/common/MapView.vue'; @@ -200,12 +190,4 @@ app.component('ScheduleFrequencySheet', ScheduleFrequencySheet); app.directive('TextareaAutoSize', TextareaAutoSize); -app.config.globalProperties['$locale'] = i18nFunctions(i18n.global); - -app.config.globalProperties['$alert'] = (message: string, confirmCallback: ((dialog: Dialog.Dialog, e: Event) => void) | undefined) => showAlert(message, confirmCallback, i18n.global.t); -app.config.globalProperties['$toast'] = (message: string, timeout: number | undefined) => showToast(message, timeout, i18n.global.t); -app.config.globalProperties['$showLoading'] = showLoading; -app.config.globalProperties['$hideLoading'] = hideLoading; -app.config.globalProperties['$routeBackOnError'] = routeBackOnError; - app.mount('#app'); diff --git a/src/views/base/transactions/TransactionListPageBase.ts b/src/views/base/transactions/TransactionListPageBase.ts new file mode 100644 index 00000000..0a94d68e --- /dev/null +++ b/src/views/base/transactions/TransactionListPageBase.ts @@ -0,0 +1,312 @@ +import { ref, computed } from 'vue'; + +import { useI18n } from '@/locales/helpers.ts'; + +import { useSettingsStore } from '@/stores/setting.ts'; +import { useUserStore } from '@/stores/user.ts'; +import { useAccountsStore } from '@/stores/account.ts'; +import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts'; +import { useTransactionTagsStore } from '@/stores/transactionTag.ts'; +import { type TransactionListFilter, type TransactionMonthList, useTransactionsStore } from '@/stores/transaction.ts'; + +import { type LocalizedDateRange, DateRange, DateRangeScene } from '@/core/datetime.ts'; +import { AccountType } from '@/core/account.ts'; +import { TransactionType } from '@/core/transaction.ts'; + +import type { Account } from '@/models/account.ts'; +import type { TransactionCategory } from '@/models/transaction_category.ts'; +import type { TransactionTag } from '@/models/transaction_tag.ts'; +import type { Transaction } from '@/models/transaction.ts'; + +import { + getUtcOffsetByUtcOffsetMinutes, + getTimezoneOffset, + getTimezoneOffsetMinutes, + parseDateFromUnixTime, + getUnixTime, + getYearMonthFirstUnixTime +} from '@/lib/datetime.ts'; + +import { + getUnifiedSelectedAccountsCurrencyOrDefaultCurrency +} from '@/lib/account.ts'; + +import { + categoryTypeToTransactionType +} from '@/lib/category.ts'; + +export function useTransactionListPageBase() { + const { + tt, + getAllDateRanges, + formatUnixTimeToLongDateTime, + formatUnixTimeToLongDate, + formatUnixTimeToLongYearMonth, + formatUnixTimeToShortTime, + formatDateRange, + formatAmountWithCurrency + } = useI18n(); + + const settingsStore = useSettingsStore(); + const userStore = useUserStore(); + const accountsStore = useAccountsStore(); + const transactionCategoriesStore = useTransactionCategoriesStore(); + const transactionTagsStore = useTransactionTagsStore(); + const transactionsStore = useTransactionsStore(); + + const loading = ref(true); + const customMinDatetime = ref(0); + const customMaxDatetime = ref(0); + + const currentTimezoneOffsetMinutes = computed(() => getTimezoneOffsetMinutes(settingsStore.appSettings.timeZone)); + const firstDayOfWeek = computed(() => userStore.currentUserFirstDayOfWeek); + const defaultCurrency = computed(() => getUnifiedSelectedAccountsCurrencyOrDefaultCurrency(allAccounts.value, queryAllFilterAccountIds.value, userStore.currentUserDefaultCurrency)); + const showTotalAmountInTransactionListPage = computed(() => settingsStore.appSettings.showTotalAmountInTransactionListPage); + const showTagInTransactionListPage = computed(() => settingsStore.appSettings.showTagInTransactionListPage); + + const allDateRanges = computed(() => getAllDateRanges(DateRangeScene.Normal, true, !!accountsStore.getAccountStatementDate(query.value.accountIds))); + + const allAccounts = computed>(() => accountsStore.allAccountsMap); + const allAvailableAccountsCount = computed(() => accountsStore.allAvailableAccountsCount); + const allPrimaryCategories = computed>(() => { + const primaryCategories: Record = {}; + + for (const categoryType in transactionCategoriesStore.allTransactionCategories) { + if (!Object.prototype.hasOwnProperty.call(transactionCategoriesStore.allTransactionCategories, categoryType)) { + continue; + } + + if (query.value.type && categoryTypeToTransactionType(parseInt(categoryType)) !== query.value.type) { + continue; + } + + primaryCategories[categoryType] = transactionCategoriesStore.allTransactionCategories[categoryType]; + } + + return primaryCategories; + }); + const allCategories = computed>(() => transactionCategoriesStore.allTransactionCategoriesMap); + const allAvailableCategoriesCount = computed(() => { + let totalCount = 0; + + for (const categoryType in transactionCategoriesStore.allTransactionCategories) { + if (!Object.prototype.hasOwnProperty.call(transactionCategoriesStore.allTransactionCategories, categoryType)) { + continue; + } + + if (query.value.type && categoryTypeToTransactionType(parseInt(categoryType)) !== query.value.type) { + continue; + } + + if (transactionCategoriesStore.allTransactionCategories[categoryType]) { + totalCount += transactionCategoriesStore.allTransactionCategories[categoryType].length; + } + } + + return totalCount; + + }); + const allTransactionTags = computed>(() => transactionTagsStore.allTransactionTagsMap); + const allAvailableTagsCount = computed(() => transactionTagsStore.allAvailableTagsCount); + + const query = computed(() => transactionsStore.transactionsFilter); + const queryDateRangeName = computed(() => { + if (query.value.dateType === DateRange.All.type) { + return tt('Date'); + } + + return formatDateRange(query.value.dateType, query.value.minTime, query.value.maxTime); + }); + const queryMinTime = computed(() => formatUnixTimeToLongDateTime(query.value.minTime)); + const queryMaxTime = computed(() => formatUnixTimeToLongDateTime(query.value.maxTime)); + const queryAllFilterCategoryIds = computed>(() => transactionsStore.allFilterCategoryIds); + const queryAllFilterAccountIds = computed>(() => transactionsStore.allFilterAccountIds); + const queryAllFilterTagIds = computed>(() => transactionsStore.allFilterTagIds); + const queryAllFilterCategoryIdsCount = computed(() => transactionsStore.allFilterCategoryIdsCount); + const queryAllFilterAccountIdsCount = computed(() => transactionsStore.allFilterAccountIdsCount); + const queryAllFilterTagIdsCount = computed(() => transactionsStore.allFilterTagIdsCount); + + const queryAccountName = computed(() => { + if (queryAllFilterAccountIdsCount.value > 1) { + return tt('Multiple Accounts'); + } + + return allAccounts.value[query.value.accountIds]?.name || tt('Account'); + }); + + const queryCategoryName = computed(() => { + if (queryAllFilterCategoryIdsCount.value > 1) { + return tt('Multiple Categories'); + } + + return allCategories.value[query.value.categoryIds]?.name || tt('Category'); + }); + + const queryTagName = computed(() => { + if (query.value.tagIds === 'none') { + return tt('Without Tags'); + } + + if (queryAllFilterTagIdsCount.value > 1) { + return tt('Multiple Tags'); + } + + return allTransactionTags.value[query.value.tagIds]?.name || tt('Tags'); + }); + + const queryAmount = computed(() => { + if (!query.value.amountFilter) { + return ''; + } + + const amountFilterItems = query.value.amountFilter.split(':'); + + if (amountFilterItems.length < 2) { + return ''; + } + + const displayAmount: string[] = []; + + for (let i = 1; i < amountFilterItems.length; i++) { + displayAmount.push(formatAmountWithCurrency(amountFilterItems[i], false)); + } + + return displayAmount.join(' ~ '); + }); + + const canAddTransaction = computed(() => { + if (query.value.accountIds && queryAllFilterAccountIdsCount.value === 1) { + const account = allAccounts.value[query.value.accountIds]; + + if (account && account.type === AccountType.MultiSubAccounts.type) { + return false; + } + } + + return true; + }); + + function formatAmount(amount: number, hideAmount: boolean, currencyCode: string): string { + if (hideAmount) { + return formatAmountWithCurrency('***', currencyCode); + } + + return formatAmountWithCurrency(amount, currencyCode); + } + + function getDisplayTime(transaction: Transaction): string { + return formatUnixTimeToShortTime(transaction.time, transaction.utcOffset, currentTimezoneOffsetMinutes.value); + } + + function getDisplayLongDate(transaction: Transaction): string { + const transactionTime = getUnixTime(parseDateFromUnixTime(transaction.time, transaction.utcOffset, currentTimezoneOffsetMinutes.value)); + return formatUnixTimeToLongDate(transactionTime); + } + + function getDisplayLongYearMonth(transactionMonthList: TransactionMonthList): string { + return formatUnixTimeToLongYearMonth(getYearMonthFirstUnixTime(transactionMonthList.yearMonth)); + } + + function getDisplayTimezone(transaction: Transaction): string { + return `UTC${getUtcOffsetByUtcOffsetMinutes(transaction.utcOffset)}`; + } + + function getDisplayTimeInDefaultTimezone(transaction: Transaction): string { + return `${formatUnixTimeToLongDateTime(transaction.time)} (UTC${getTimezoneOffset(settingsStore.appSettings.timeZone)})`; + } + + function getDisplayAmount(transaction: Transaction): string { + if (queryAllFilterAccountIdsCount.value < 1) { + if (transaction.sourceAccount) { + return formatAmount(transaction.sourceAmount, transaction.hideAmount, transaction.sourceAccount.currency); + } + } else if (queryAllFilterAccountIdsCount.value === 1) { + if (transaction.sourceAccount && (queryAllFilterAccountIds.value[transaction.sourceAccount.id] || queryAllFilterAccountIds.value[transaction.sourceAccount.parentId])) { + return formatAmount(transaction.sourceAmount, transaction.hideAmount, transaction.sourceAccount.currency); + } else if (transaction.destinationAccount && (queryAllFilterAccountIds.value[transaction.destinationAccount.id] || queryAllFilterAccountIds.value[transaction.destinationAccount.parentId])) { + return formatAmount(transaction.destinationAmount, transaction.hideAmount, transaction.destinationAccount.currency); + } + } else { // queryAllFilterAccountIdsCount.value > 1 + if (transaction.sourceAccount && transaction.destinationAccount) { + if ((queryAllFilterAccountIds.value[transaction.sourceAccount.id] || queryAllFilterAccountIds.value[transaction.sourceAccount.parentId]) + && !queryAllFilterAccountIds.value[transaction.destinationAccount.id] && !queryAllFilterAccountIds.value[transaction.destinationAccount.parentId]) { + return formatAmount(transaction.sourceAmount, transaction.hideAmount, transaction.sourceAccount.currency); + } else if ((queryAllFilterAccountIds.value[transaction.destinationAccount.id] || queryAllFilterAccountIds.value[transaction.destinationAccount.parentId]) + && !queryAllFilterAccountIds.value[transaction.sourceAccount.id] && !queryAllFilterAccountIds.value[transaction.sourceAccount.parentId]) { + return formatAmount(transaction.destinationAmount, transaction.hideAmount, transaction.destinationAccount.currency); + } + } + } + + if (transaction.sourceAccount) { + return formatAmount(transaction.sourceAmount, transaction.hideAmount, transaction.sourceAccount.currency); + } + + return ''; + } + + function getDisplayMonthTotalAmount(amount: number, currency: string, symbol: string, incomplete: boolean): string { + const displayAmount = formatAmountWithCurrency(amount, currency); + return symbol + displayAmount + (incomplete ? '+' : ''); + } + + function getTransactionTypeName(type: number | null, defaultName: string): string { + switch (type){ + case TransactionType.ModifyBalance: + return tt('Modify Balance'); + case TransactionType.Income: + return tt('Income'); + case TransactionType.Expense: + return tt('Expense'); + case TransactionType.Transfer: + return tt('Transfer'); + default: + return tt(defaultName); + } + } + + return { + // states + loading, + customMinDatetime, + customMaxDatetime, + // computed states + currentTimezoneOffsetMinutes, + firstDayOfWeek, + defaultCurrency, + showTotalAmountInTransactionListPage, + showTagInTransactionListPage, + allDateRanges, + allAccounts, + allAvailableAccountsCount, + allCategories, + allPrimaryCategories, + allAvailableCategoriesCount, + allTransactionTags, + allAvailableTagsCount, + query, + queryDateRangeName, + queryMinTime, + queryMaxTime, + queryAllFilterCategoryIds, + queryAllFilterAccountIds, + queryAllFilterTagIds, + queryAllFilterCategoryIdsCount, + queryAllFilterAccountIdsCount, + queryAllFilterTagIdsCount, + queryAccountName, + queryCategoryName, + queryTagName, + queryAmount, + canAddTransaction, + // functions + getDisplayTime, + getDisplayLongDate, + getDisplayLongYearMonth, + getDisplayTimezone, + getDisplayTimeInDefaultTimezone, + getDisplayAmount, + getDisplayMonthTotalAmount, + getTransactionTypeName, + }; +} diff --git a/src/views/desktop/transactions/ListPage.vue b/src/views/desktop/transactions/ListPage.vue index ca44a890..bade8a7d 100644 --- a/src/views/desktop/transactions/ListPage.vue +++ b/src/views/desktop/transactions/ListPage.vue @@ -6,16 +6,16 @@
- {{ $t('Transactions Per Page') }} + {{ tt('Transactions Per Page') }} - + - {{ $t('Transaction List') }} + {{ tt('Transaction List') }} - {{ $t('Add') }} + :disabled="loading || !canAddTransaction" @click="add()"> + {{ tt('Add') }} @@ -55,23 +55,23 @@ - {{ $t('Import') }} + v-if="isDataImportingEnabled()"> + {{ tt('Import') }} - - {{ $t('Refresh') }} + + {{ tt('Refresh') }}
- {{ $t('Date Range') }} + {{ tt('Date Range') }} - {{ $t('All') }} + {{ tt('All') }} {{ `${queryMinTime} - ${queryMaxTime}` }}
- {{ $t('Total Income') }} + {{ tt('Total Income') }} {{ currentMonthTotalAmount.income }} - {{ $t('Total Expense') }} + {{ tt('Total Expense') }} @@ -128,25 +128,25 @@ @update:model-value="scrollTimeMenuToSelectedItem"> + :append-icon="(!query.categoryIds ? mdiCheck : undefined)">
- - {{ $t('All') }} + + {{ tt('All') }}
- - {{ $t('Multiple Categories') }} + + {{ tt('Multiple Categories') }}
@@ -200,7 +200,7 @@ v-for="(categories, categoryType) in allPrimaryCategories"> - {{ getTransactionTypeName(getTransactionTypeFromCategoryType(categoryType), 'Type') }} + {{ getTransactionTypeName(categoryTypeToTransactionType(categoryType), 'Type') }} @@ -223,23 +223,23 @@ + :append-icon="(query.categoryIds === category.id ? mdiCheck : undefined)">
- - {{ $t('All') }} + + {{ tt('All') }}
+ :append-icon="(!query.tagIds ? mdiCheck : undefined)">
- - {{ $t('All') }} + + {{ tt('All') }}
+ :append-icon="(query.tagIds === 'none' ? mdiCheck : undefined)">
- - {{ $t('Without Tags') }} + + {{ tt('Without Tags') }}
- - {{ $t('Multiple Tags') }} + + {{ tt('Multiple Tags') }}
@@ -417,7 +417,7 @@ @@ -435,12 +435,12 @@
- + {{ transactionTag.name }}
@@ -449,7 +449,7 @@
- {{ $t('Description') }} + {{ tt('Description') }} @@ -463,7 +463,7 @@ - {{ $t('No transaction data') }} + {{ tt('No transaction data') }} @@ -474,15 +474,16 @@ v-if="idx === 0 || (idx > 0 && (transaction.date !== transactions[idx - 1].date))">
- {{ getLongDate(transaction) }} - - {{ getWeekdayLongName(transaction) }} + {{ getDisplayLongDate(transaction) }} + + {{ getWeekdayLongName(transaction.dayOfWeek) }}
@@ -497,37 +498,37 @@ :icon-id="transaction.category.icon" :color="transaction.category.color" v-if="transaction.category && transaction.category.color"> - - - {{ $t('Modify Balance') }} + + + {{ tt('Modify Balance') }} - + {{ transaction.category.name }} - + {{ getTransactionTypeName(transaction.type, 'Transaction') }}
- +
- {{ getTransactionDisplayAmount(transaction) }} + {{ getDisplayAmount(transaction) }}
{{ transaction.sourceAccount.name }} - - {{ transaction.destinationAccount.name }} + + {{ transaction.destinationAccount.name }}
- @@ -550,13 +551,13 @@ - - + @error="onShowDateRangeError" /> + @@ -578,41 +579,56 @@ -