migrate transaction list page to composition API and typescript
This commit is contained in:
@@ -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']
|
||||
}
|
||||
}],
|
||||
}]
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
@@ -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<number, DateRange> = {};
|
||||
private static readonly allInstancesByTypeName: Record<string, DateRange> = {};
|
||||
|
||||
// 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<number, boolean>;
|
||||
|
||||
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<DateRangeTypeName, DateRange> {
|
||||
return DateRange.allInstancesByTypeName;
|
||||
}
|
||||
|
||||
public static valueOf(type: number): DateRange | undefined {
|
||||
return DateRange.allInstancesByType[type];
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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<number, TransactionCategory[]>, allCategoriesMap: Record<string, TransactionCategory>, allVisibleAccounts: Account[], allAccountsMap: Record<string, Account>, allTagsMap: Record<string, TransactionTag>, 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<string, boolean>, 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 '';
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<T>(f7router: Router.Router, errorRef: Ref<T>): 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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
};
|
||||
}
|
||||
@@ -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');
|
||||
|
||||
312
src/views/base/transactions/TransactionListPageBase.ts
Normal file
312
src/views/base/transactions/TransactionListPageBase.ts
Normal file
@@ -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<boolean>(true);
|
||||
const customMinDatetime = ref<number>(0);
|
||||
const customMaxDatetime = ref<number>(0);
|
||||
|
||||
const currentTimezoneOffsetMinutes = computed<number>(() => getTimezoneOffsetMinutes(settingsStore.appSettings.timeZone));
|
||||
const firstDayOfWeek = computed<number>(() => userStore.currentUserFirstDayOfWeek);
|
||||
const defaultCurrency = computed<string>(() => getUnifiedSelectedAccountsCurrencyOrDefaultCurrency(allAccounts.value, queryAllFilterAccountIds.value, userStore.currentUserDefaultCurrency));
|
||||
const showTotalAmountInTransactionListPage = computed<boolean>(() => settingsStore.appSettings.showTotalAmountInTransactionListPage);
|
||||
const showTagInTransactionListPage = computed<boolean>(() => settingsStore.appSettings.showTagInTransactionListPage);
|
||||
|
||||
const allDateRanges = computed<LocalizedDateRange[]>(() => getAllDateRanges(DateRangeScene.Normal, true, !!accountsStore.getAccountStatementDate(query.value.accountIds)));
|
||||
|
||||
const allAccounts = computed<Record<string, Account>>(() => accountsStore.allAccountsMap);
|
||||
const allAvailableAccountsCount = computed<number>(() => accountsStore.allAvailableAccountsCount);
|
||||
const allPrimaryCategories = computed<Record<number, TransactionCategory[]>>(() => {
|
||||
const primaryCategories: Record<number, TransactionCategory[]> = {};
|
||||
|
||||
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<Record<string, TransactionCategory>>(() => transactionCategoriesStore.allTransactionCategoriesMap);
|
||||
const allAvailableCategoriesCount = computed<number>(() => {
|
||||
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<Record<string, TransactionTag>>(() => transactionTagsStore.allTransactionTagsMap);
|
||||
const allAvailableTagsCount = computed<number>(() => transactionTagsStore.allAvailableTagsCount);
|
||||
|
||||
const query = computed<TransactionListFilter>(() => transactionsStore.transactionsFilter);
|
||||
const queryDateRangeName = computed<string>(() => {
|
||||
if (query.value.dateType === DateRange.All.type) {
|
||||
return tt('Date');
|
||||
}
|
||||
|
||||
return formatDateRange(query.value.dateType, query.value.minTime, query.value.maxTime);
|
||||
});
|
||||
const queryMinTime = computed<string>(() => formatUnixTimeToLongDateTime(query.value.minTime));
|
||||
const queryMaxTime = computed<string>(() => formatUnixTimeToLongDateTime(query.value.maxTime));
|
||||
const queryAllFilterCategoryIds = computed<Record<string, boolean>>(() => transactionsStore.allFilterCategoryIds);
|
||||
const queryAllFilterAccountIds = computed<Record<string, boolean>>(() => transactionsStore.allFilterAccountIds);
|
||||
const queryAllFilterTagIds = computed<Record<string, boolean>>(() => transactionsStore.allFilterTagIds);
|
||||
const queryAllFilterCategoryIdsCount = computed<number>(() => transactionsStore.allFilterCategoryIdsCount);
|
||||
const queryAllFilterAccountIdsCount = computed<number>(() => transactionsStore.allFilterAccountIdsCount);
|
||||
const queryAllFilterTagIdsCount = computed<number>(() => transactionsStore.allFilterTagIdsCount);
|
||||
|
||||
const queryAccountName = computed<string>(() => {
|
||||
if (queryAllFilterAccountIdsCount.value > 1) {
|
||||
return tt('Multiple Accounts');
|
||||
}
|
||||
|
||||
return allAccounts.value[query.value.accountIds]?.name || tt('Account');
|
||||
});
|
||||
|
||||
const queryCategoryName = computed<string>(() => {
|
||||
if (queryAllFilterCategoryIdsCount.value > 1) {
|
||||
return tt('Multiple Categories');
|
||||
}
|
||||
|
||||
return allCategories.value[query.value.categoryIds]?.name || tt('Category');
|
||||
});
|
||||
|
||||
const queryTagName = computed<string>(() => {
|
||||
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<string>(() => {
|
||||
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<boolean>(() => {
|
||||
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,
|
||||
};
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user