migrate transaction edit page to composition API and typescript

This commit is contained in:
MaysWind
2025-02-05 00:02:40 +08:00
parent 3e7b3297aa
commit 833e767e6c
12 changed files with 1979 additions and 2264 deletions
-6
View File
@@ -5,7 +5,6 @@ type TemplateTypeName = 'Normal' | 'Schedule';
export class TemplateType implements TypeAndName { export class TemplateType implements TypeAndName {
private static readonly allInstances: TemplateType[] = []; private static readonly allInstances: TemplateType[] = [];
private static readonly allInstancesByType: Record<number, TemplateType> = {}; private static readonly allInstancesByType: Record<number, TemplateType> = {};
private static readonly allInstancesByTypeName: Record<string, TemplateType> = {};
public static readonly Normal = new TemplateType(1, 'Normal'); public static readonly Normal = new TemplateType(1, 'Normal');
public static readonly Schedule = new TemplateType(2, 'Schedule'); public static readonly Schedule = new TemplateType(2, 'Schedule');
@@ -19,17 +18,12 @@ export class TemplateType implements TypeAndName {
TemplateType.allInstances.push(this); TemplateType.allInstances.push(this);
TemplateType.allInstancesByType[type] = this; TemplateType.allInstancesByType[type] = this;
TemplateType.allInstancesByTypeName[name] = this;
} }
public static values(): TemplateType[] { public static values(): TemplateType[] {
return TemplateType.allInstances; return TemplateType.allInstances;
} }
public static all(): Record<TemplateTypeName, TemplateType> {
return TemplateType.allInstancesByTypeName;
}
public static valueOf(type: number): TemplateType | undefined { public static valueOf(type: number): TemplateType | undefined {
return TemplateType.allInstancesByType[type]; return TemplateType.allInstancesByType[type];
} }
+1 -1
View File
@@ -434,7 +434,7 @@ export default {
timeout: DEFAULT_IMPORT_API_TIMEOUT timeout: DEFAULT_IMPORT_API_TIMEOUT
} as ApiRequestConfig); } as ApiRequestConfig);
}, },
uploadTransactionPicture: ({ pictureFile, clientSessionId }: { pictureFile: File, clientSessionId: string }): ApiResponsePromise<TransactionPictureInfoBasicResponse> => { uploadTransactionPicture: ({ pictureFile, clientSessionId }: { pictureFile: File, clientSessionId?: string }): ApiResponsePromise<TransactionPictureInfoBasicResponse> => {
return axios.postForm<ApiResponse<TransactionPictureInfoBasicResponse>>('v1/transaction/pictures/upload.json', { return axios.postForm<ApiResponse<TransactionPictureInfoBasicResponse>>('v1/transaction/pictures/upload.json', {
picture: pictureFile, picture: pictureFile,
clientSessionId: clientSessionId clientSessionId: clientSessionId
+8 -8
View File
@@ -20,14 +20,14 @@ import {
} from './category.ts'; } from './category.ts';
export interface SetTransactionOptions { export interface SetTransactionOptions {
type: number; type?: number;
categoryId: string; categoryId?: string;
accountId: string; accountId?: string;
destinationAccountId: string; destinationAccountId?: string;
amount: number; amount?: number;
destinationAmount: number; destinationAmount?: number;
tagIds: string; tagIds?: string;
comment: string; comment?: string;
} }
function getDisplayAmount(amount: number, currency: string, hideAmount: boolean, formatAmountWithCurrencyFunc: (value: number | string, currencyCode?: string) => string): string { function getDisplayAmount(amount: number, currency: string, hideAmount: boolean, formatAmountWithCurrencyFunc: (value: number | string, currencyCode?: string) => string): string {
+24 -24
View File
@@ -37,26 +37,6 @@ export function showAlert(message: string, confirmCallback: ((dialog: Dialog.Dia
}); });
} }
export function showConfirm(message: string, confirmCallback: (dialog: Dialog.Dialog, e: Event) => void, cancelCallback: ((dialog: Dialog.Dialog, e: Event) => void) | undefined, translateFn: TranslateFunction): void {
f7ready((f7) => {
f7.dialog.create({
title: translateFn('global.app.title'),
text: translateFn(message),
animate: isEnableAnimate(),
buttons: [
{
text: translateFn('Cancel'),
onClick: cancelCallback
},
{
text: translateFn('OK'),
onClick: confirmCallback
}
]
}).open();
});
}
export function showToast(message: string, timeout: number | undefined, translateFn: TranslateFunction): void { export function showToast(message: string, timeout: number | undefined, translateFn: TranslateFunction): void {
f7ready((f7) => { f7ready((f7) => {
f7.toast.create({ f7.toast.create({
@@ -210,7 +190,7 @@ export function scrollToSelectedItem(parentEl: Framework7Dom, containerSelector:
} }
export function useI18nUIComponents() { export function useI18nUIComponents() {
const i18nGlobal = useVueI18n(); const { t } = useVueI18n();
function routeBackOnError<T>(f7router: Router.Router, errorRef: Ref<T>): void { function routeBackOnError<T>(f7router: Router.Router, errorRef: Ref<T>): void {
const unwatch = watch(errorRef, (newValue) => { const unwatch = watch(errorRef, (newValue) => {
@@ -228,10 +208,30 @@ export function useI18nUIComponents() {
}); });
} }
function showConfirm(message: string, confirmCallback?: (dialog: Dialog.Dialog, e: Event) => void, cancelCallback?: ((dialog: Dialog.Dialog, e: Event) => void) | undefined): void {
f7ready((f7) => {
f7.dialog.create({
title: t('global.app.title'),
text: t(message),
animate: isEnableAnimate(),
buttons: [
{
text: t('Cancel'),
onClick: cancelCallback
},
{
text: t('OK'),
onClick: confirmCallback
}
]
}).open();
});
}
return { return {
showAlert: (message: string, confirmCallback?: (dialog: Dialog.Dialog, e: Event) => void) => showAlert(message, confirmCallback, i18nGlobal.t), showAlert: (message: string, confirmCallback?: (dialog: Dialog.Dialog, e: Event) => void) => showAlert(message, confirmCallback, t),
showConfirm: (message: string, confirmCallback: (dialog: Dialog.Dialog, e: Event) => void, cancelCallback?: (dialog: Dialog.Dialog, e: Event) => void): void => showConfirm(message, confirmCallback, cancelCallback, i18nGlobal.t), showConfirm: showConfirm,
showToast: (message: string, timeout?: number): void => showToast(message, timeout, i18nGlobal.t), showToast: (message: string, timeout?: number): void => showToast(message, timeout, t),
routeBackOnError routeBackOnError
} }
} }
+4 -269
View File
@@ -1,29 +1,21 @@
import { WeekDay, LongDateFormat, ShortDateFormat, LongTimeFormat, ShortTimeFormat, DateRange } from '@/core/datetime.ts'; import { LongDateFormat, ShortDateFormat, LongTimeFormat, ShortTimeFormat, DateRange } from '@/core/datetime.ts';
import { DecimalSeparator, DigitGroupingSymbol, DigitGroupingType } from '@/core/numeral.ts'; import { DecimalSeparator, DigitGroupingSymbol, DigitGroupingType } from '@/core/numeral.ts';
import { CurrencyDisplayType } from '@/core/currency.ts' import { CurrencyDisplayType } from '@/core/currency.ts'
import { AccountCategory } from '@/core/account.ts';
import { TransactionTagFilterType } from '@/core/transaction.ts'; import { TransactionTagFilterType } from '@/core/transaction.ts';
import { UTC_TIMEZONE, ALL_TIMEZONES } from '@/consts/timezone.ts';
import { ALL_CURRENCIES } from '@/consts/currency.ts'; import { ALL_CURRENCIES } from '@/consts/currency.ts';
import { KnownErrorCode, SPECIFIED_API_NOT_FOUND_ERRORS, PARAMETERIZED_ERRORS } from '@/consts/api.ts'; import { KnownErrorCode, SPECIFIED_API_NOT_FOUND_ERRORS, PARAMETERIZED_ERRORS } from '@/consts/api.ts';
import { import {
isString, isString,
isNumber, isNumber,
isBoolean, isBoolean
copyObjectTo
} from '@/lib/common.ts'; } from '@/lib/common.ts';
import { import {
parseDateFromUnixTime, parseDateFromUnixTime,
formatUnixTime, formatUnixTime,
getYear, getYear,
getTimezoneOffset,
getTimezoneOffsetMinutes,
getBrowserTimezoneOffset,
getBrowserTimezoneOffsetMinutes,
getTimeDifferenceHoursAndMinutes,
getDateTimeFormatType, getDateTimeFormatType,
getRecentMonthDateRanges, getRecentMonthDateRanges,
isDateRangeMatchFullYears, isDateRangeMatchFullYears,
@@ -31,8 +23,7 @@ import {
} from '@/lib/datetime.ts'; } from '@/lib/datetime.ts';
import { import {
formatAmount, formatAmount
getAdaptiveDisplayAmountRate
} from '@/lib/numeral.ts'; } from '@/lib/numeral.ts';
import { import {
@@ -40,11 +31,6 @@ import {
appendCurrencySymbol appendCurrencySymbol
} from '@/lib/currency.ts'; } from '@/lib/currency.ts';
import {
getCategorizedAccountsMap,
getAllFilteredAccountsBalance
} from '@/lib/account.ts';
function getLocalizedDisplayNameAndType(typeAndNames, translateFn) { function getLocalizedDisplayNameAndType(typeAndNames, translateFn) {
const ret = []; const ret = [];
@@ -78,10 +64,6 @@ function getCurrencyUnitName(currencyCode, isPlural, translateFn) {
return ''; return '';
} }
function getMonthdayOrdinal(monthDay, translateFn) {
return translateFn(`datetime.monthDayOrdinal.${monthDay}`);
}
function getWeekdayShortName(weekDayName, translateFn) { function getWeekdayShortName(weekDayName, translateFn) {
return translateFn(`datetime.${weekDayName}.short`); return translateFn(`datetime.${weekDayName}.short`);
} }
@@ -90,50 +72,6 @@ function getWeekdayLongName(weekDayName, translateFn) {
return translateFn(`datetime.${weekDayName}.long`); return translateFn(`datetime.${weekDayName}.long`);
} }
function getMultiMonthdayShortNames(monthDays, translateFn) {
if (!monthDays) {
return '';
}
if (monthDays.length === 1) {
return translateFn('format.misc.monthDay', {
ordinal: getMonthdayOrdinal(monthDays[0], translateFn)
});
} else {
return translateFn('format.misc.monthDays', {
multiMonthDays: joinMultiText(monthDays.map(monthDay =>
translateFn('format.misc.eachMonthDayInMonthDays', {
ordinal: getMonthdayOrdinal(monthDay, translateFn)
})), translateFn)
});
}
}
function getMultiWeekdayLongNames(weekdayTypes, firstDayOfWeek, translateFn) {
const weekdayTypesMap = {};
if (!isNumber(firstDayOfWeek)) {
firstDayOfWeek = WeekDay.DefaultFirstDay.type;
}
for (let i = 0; i < weekdayTypes.length; i++) {
weekdayTypesMap[weekdayTypes[i]] = true;
}
const allWeekDays = getAllWeekDays(firstDayOfWeek, translateFn);
const finalWeekdayNames = [];
for (let i = 0; i < allWeekDays.length; i++) {
const weekDay = allWeekDays[i];
if (weekdayTypesMap[weekDay.type]) {
finalWeekdayNames.push(weekDay.displayName);
}
}
return joinMultiText(finalWeekdayNames, translateFn);
}
function getI18nLongDateFormat(translateFn, formatTypeValue) { function getI18nLongDateFormat(translateFn, formatTypeValue) {
const defaultLongDateFormatTypeName = translateFn('default.longDateFormat'); const defaultLongDateFormatTypeName = translateFn('default.longDateFormat');
return getDateTimeFormat(translateFn, LongDateFormat.all(), LongDateFormat.values(), 'format.longDate', defaultLongDateFormatTypeName, LongDateFormat.Default, formatTypeValue); return getDateTimeFormat(translateFn, LongDateFormat.all(), LongDateFormat.values(), 'format.longDate', defaultLongDateFormatTypeName, LongDateFormat.Default, formatTypeValue);
@@ -184,110 +122,6 @@ function getDateTimeFormat(translateFn, allFormatMap, allFormatArray, localeForm
return translateFn(`${localeFormatPathPrefix}.${type.key}`); return translateFn(`${localeFormatPathPrefix}.${type.key}`);
} }
function getAllTimezones(includeSystemDefault, translateFn) {
const defaultTimezoneOffset = getBrowserTimezoneOffset();
const defaultTimezoneOffsetMinutes = getBrowserTimezoneOffsetMinutes();
const allTimezoneInfos = [];
for (let i = 0; i < ALL_TIMEZONES.length; i++) {
const utcOffset = (ALL_TIMEZONES[i].timezoneName !== UTC_TIMEZONE.timezoneName ? getTimezoneOffset(ALL_TIMEZONES[i].timezoneName) : '');
const displayName = translateFn(`timezone.${ALL_TIMEZONES[i].displayName}`);
allTimezoneInfos.push({
name: ALL_TIMEZONES[i].timezoneName,
utcOffset: utcOffset,
utcOffsetMinutes: getTimezoneOffsetMinutes(ALL_TIMEZONES[i].timezoneName),
displayName: displayName,
displayNameWithUtcOffset: `(UTC${utcOffset}) ${displayName}`
});
}
if (includeSystemDefault) {
const defaultDisplayName = translateFn('System Default');
allTimezoneInfos.push({
name: '',
utcOffset: defaultTimezoneOffset,
utcOffsetMinutes: defaultTimezoneOffsetMinutes,
displayName: defaultDisplayName,
displayNameWithUtcOffset: `(UTC${defaultTimezoneOffset}) ${defaultDisplayName}`
});
}
allTimezoneInfos.sort(function(c1, c2) {
const utcOffset1 = parseInt(c1.utcOffset.replace(':', ''));
const utcOffset2 = parseInt(c2.utcOffset.replace(':', ''));
if (utcOffset1 !== utcOffset2) {
return utcOffset1 - utcOffset2;
}
return c1.displayName.localeCompare(c2.displayName);
})
return allTimezoneInfos;
}
function getTimezoneDifferenceDisplayText(utcOffset, translateFn) {
const defaultTimezoneOffset = getTimezoneOffsetMinutes();
const offsetTime = getTimeDifferenceHoursAndMinutes(utcOffset - defaultTimezoneOffset);
if (utcOffset > defaultTimezoneOffset) {
if (offsetTime.offsetMinutes) {
return translateFn('format.misc.hoursMinutesAheadOfDefaultTimezone', {
hours: offsetTime.offsetHours,
minutes: offsetTime.offsetMinutes
});
} else {
return translateFn('format.misc.hoursAheadOfDefaultTimezone', {
hours: offsetTime.offsetHours
});
}
} else if (utcOffset < defaultTimezoneOffset) {
if (offsetTime.offsetMinutes) {
return translateFn('format.misc.hoursMinutesBehindDefaultTimezone', {
hours: offsetTime.offsetHours,
minutes: offsetTime.offsetMinutes
});
} else {
return translateFn('format.misc.hoursBehindDefaultTimezone', {
hours: offsetTime.offsetHours
});
}
} else {
return translateFn('Same time as default timezone');
}
}
function getAllWeekDays(firstDayOfWeek, translateFn) {
const ret = [];
const allWeekDays = WeekDay.values();
if (!isNumber(firstDayOfWeek)) {
firstDayOfWeek = WeekDay.DefaultFirstDay.type;
}
for (let i = firstDayOfWeek; i < allWeekDays.length; i++) {
const weekDay = allWeekDays[i];
ret.push({
type: weekDay.type,
displayName: translateFn(`datetime.${weekDay.name}.long`)
});
}
for (let i = 0; i < firstDayOfWeek; i++) {
const weekDay = allWeekDays[i];
ret.push({
type: weekDay.type,
displayName: translateFn(`datetime.${weekDay.name}.long`)
});
}
return ret;
}
function getAllDateRanges(scene, includeCustom, includeBillingCycle, translateFn) { function getAllDateRanges(scene, includeCustom, includeBillingCycle, translateFn) {
const ret = []; const ret = [];
const allDateRanges = DateRange.values(); const allDateRanges = DateRange.values();
@@ -460,11 +294,6 @@ function getNumberFormatOptions(translateFn, userStore, currencyCode) {
}; };
} }
function getFormattedAmount(value, translateFn, userStore, currencyCode) {
const numberFormatOptions = getNumberFormatOptions(translateFn, userStore, currencyCode);
return formatAmount(value, numberFormatOptions);
}
function getCurrentCurrencyDisplayType(translateFn, userStore) { function getCurrentCurrencyDisplayType(translateFn, userStore) {
let currencyDisplayType = CurrencyDisplayType.valueOf(userStore.currentUserCurrencyDisplayType); let currencyDisplayType = CurrencyDisplayType.valueOf(userStore.currentUserCurrencyDisplayType);
@@ -525,96 +354,10 @@ function getFormattedAmountWithCurrency(value, currencyCode, translateFn, userSt
return appendCurrencySymbol(value, currencyDisplayType, currencyCode, currencyUnit, currencyName, isPlural); return appendCurrencySymbol(value, currencyDisplayType, currencyCode, currencyUnit, currencyName, isPlural);
} }
function getAdaptiveAmountRate(amount1, amount2, fromExchangeRate, toExchangeRate, translateFn, userStore) {
const numberFormatOptions = getNumberFormatOptions(translateFn, userStore);
return getAdaptiveDisplayAmountRate(amount1, amount2, fromExchangeRate, toExchangeRate, numberFormatOptions);
}
function getAllTransactionTagFilterTypes(translateFn) { function getAllTransactionTagFilterTypes(translateFn) {
return getLocalizedDisplayNameAndType(TransactionTagFilterType.values(), translateFn); return getLocalizedDisplayNameAndType(TransactionTagFilterType.values(), translateFn);
} }
function getCategorizedAccountsWithDisplayBalance(allVisibleAccounts, showAccountBalance, defaultCurrency, userStore, settingsStore, exchangeRatesStore, translateFn) {
const ret = [];
const allCategories = AccountCategory.values();
const categorizedAccounts = copyObjectTo(getCategorizedAccountsMap(allVisibleAccounts), {});
for (let i = 0; i < allCategories.length; i++) {
const category = allCategories[i];
if (!categorizedAccounts[category.type]) {
continue;
}
const accountCategory = categorizedAccounts[category.type];
if (accountCategory.accounts) {
for (let i = 0; i < accountCategory.accounts.length; i++) {
const account = accountCategory.accounts[i];
if (showAccountBalance && account.isAsset) {
account.displayBalance = getFormattedAmountWithCurrency(account.balance, account.currency, translateFn, userStore, settingsStore);
} else if (showAccountBalance && account.isLiability) {
account.displayBalance = getFormattedAmountWithCurrency(-account.balance, account.currency, translateFn, userStore, settingsStore);
} else {
account.displayBalance = '***';
}
}
}
if (showAccountBalance) {
const accountsBalance = getAllFilteredAccountsBalance(categorizedAccounts, account => account.category === accountCategory.category);
let totalBalance = 0;
let hasUnCalculatedAmount = false;
for (let i = 0; i < accountsBalance.length; i++) {
if (accountsBalance[i].currency === defaultCurrency) {
if (accountsBalance[i].isAsset) {
totalBalance += accountsBalance[i].balance;
} else if (accountsBalance[i].isLiability) {
totalBalance -= accountsBalance[i].balance;
}
} else {
const balance = exchangeRatesStore.getExchangedAmount(accountsBalance[i].balance, accountsBalance[i].currency, defaultCurrency);
if (!isNumber(balance)) {
hasUnCalculatedAmount = true;
continue;
}
if (accountsBalance[i].isAsset) {
totalBalance += Math.floor(balance);
} else if (accountsBalance[i].isLiability) {
totalBalance -= Math.floor(balance);
}
}
}
if (hasUnCalculatedAmount) {
totalBalance = totalBalance + '+';
}
accountCategory.displayBalance = getFormattedAmountWithCurrency(totalBalance, defaultCurrency, translateFn, userStore, settingsStore);
} else {
accountCategory.displayBalance = '***';
}
ret.push(accountCategory);
}
return ret;
}
function joinMultiText(textArray, translateFn) {
if (!textArray || !textArray.length) {
return '';
}
const separator = translateFn('format.misc.multiTextJoinSeparator');
return textArray.join(separator);
}
function getLocalizedError(error) { function getLocalizedError(error) {
if (error.errorCode === KnownErrorCode.ApiNotFound && SPECIFIED_API_NOT_FOUND_ERRORS[error.path]) { if (error.errorCode === KnownErrorCode.ApiNotFound && SPECIFIED_API_NOT_FOUND_ERRORS[error.path]) {
return { return {
@@ -685,23 +428,15 @@ export function i18nFunctions(i18nGlobal) {
return { return {
getWeekdayShortName: (weekDay) => getWeekdayShortName(weekDay, i18nGlobal.t), getWeekdayShortName: (weekDay) => getWeekdayShortName(weekDay, i18nGlobal.t),
getWeekdayLongName: (weekDay) => getWeekdayLongName(weekDay, i18nGlobal.t), getWeekdayLongName: (weekDay) => getWeekdayLongName(weekDay, i18nGlobal.t),
getMultiMonthdayShortNames: (monthdays) => getMultiMonthdayShortNames(monthdays, i18nGlobal.t),
getMultiWeekdayLongNames: (weekdayTypes, firstDayOfWeek) => getMultiWeekdayLongNames(weekdayTypes, firstDayOfWeek, i18nGlobal.t),
formatUnixTimeToLongDateTime: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nLongDateFormat(i18nGlobal.t, userStore.currentUserLongDateFormat) + ' ' + getI18nLongTimeFormat(i18nGlobal.t, userStore.currentUserLongTimeFormat), utcOffset, currentUtcOffset), 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), 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), 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), formatUnixTimeToLongYearMonth: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nLongYearMonthFormat(i18nGlobal.t, userStore.currentUserLongDateFormat), utcOffset, currentUtcOffset),
formatUnixTimeToLongTime: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nLongTimeFormat(i18nGlobal.t, userStore.currentUserLongTimeFormat), utcOffset, currentUtcOffset),
formatUnixTimeToShortTime: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nShortTimeFormat(i18nGlobal.t, userStore.currentUserShortTimeFormat), utcOffset, currentUtcOffset), formatUnixTimeToShortTime: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nShortTimeFormat(i18nGlobal.t, userStore.currentUserShortTimeFormat), utcOffset, currentUtcOffset),
getAllTimezones: (includeSystemDefault) => getAllTimezones(includeSystemDefault, i18nGlobal.t),
getTimezoneDifferenceDisplayText: (utcOffset) => getTimezoneDifferenceDisplayText(utcOffset, i18nGlobal.t),
getAllDateRanges: (scene, includeCustom, includeBillingCycle) => getAllDateRanges(scene, includeCustom, includeBillingCycle, i18nGlobal.t), getAllDateRanges: (scene, includeCustom, includeBillingCycle) => getAllDateRanges(scene, includeCustom, includeBillingCycle, i18nGlobal.t),
getAllRecentMonthDateRanges: (userStore, includeAll, includeCustom) => getAllRecentMonthDateRanges(userStore, includeAll, includeCustom, i18nGlobal.t), getAllRecentMonthDateRanges: (userStore, includeAll, includeCustom) => getAllRecentMonthDateRanges(userStore, includeAll, includeCustom, i18nGlobal.t),
getDateRangeDisplayName: (userStore, dateType, startTime, endTime) => getDateRangeDisplayName(userStore, dateType, startTime, endTime, i18nGlobal.t), getDateRangeDisplayName: (userStore, dateType, startTime, endTime) => getDateRangeDisplayName(userStore, dateType, startTime, endTime, i18nGlobal.t),
formatAmount: (userStore, value, currencyCode) => getFormattedAmount(value, i18nGlobal.t, userStore, currencyCode),
formatAmountWithCurrency: (settingsStore, userStore, value, currencyCode) => getFormattedAmountWithCurrency(value, currencyCode, i18nGlobal.t, userStore, settingsStore), formatAmountWithCurrency: (settingsStore, userStore, value, currencyCode) => getFormattedAmountWithCurrency(value, currencyCode, i18nGlobal.t, userStore, settingsStore),
getAdaptiveAmountRate: (userStore, amount1, amount2, fromExchangeRate, toExchangeRate) => getAdaptiveAmountRate(amount1, amount2, fromExchangeRate, toExchangeRate, i18nGlobal.t, userStore), getAllTransactionTagFilterTypes: () => getAllTransactionTagFilterTypes(i18nGlobal.t)
getAllTransactionTagFilterTypes: () => getAllTransactionTagFilterTypes(i18nGlobal.t),
getCategorizedAccountsWithDisplayBalance: (allVisibleAccounts, showAccountBalance, defaultCurrency, settingsStore, userStore, exchangeRatesStore) => getCategorizedAccountsWithDisplayBalance(allVisibleAccounts, showAccountBalance, defaultCurrency, userStore, settingsStore, exchangeRatesStore, i18nGlobal.t)
}; };
} }
-2
View File
@@ -86,7 +86,6 @@ import { getI18nOptions } from '@/locales/helpers.ts';
import { i18nFunctions } from '@/locales/helper.js'; import { i18nFunctions } from '@/locales/helper.js';
import { import {
showAlert, showAlert,
showConfirm,
showToast, showToast,
showLoading, showLoading,
hideLoading, hideLoading,
@@ -204,7 +203,6 @@ app.directive('TextareaAutoSize', TextareaAutoSize);
app.config.globalProperties['$locale'] = i18nFunctions(i18n.global); 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['$alert'] = (message: string, confirmCallback: ((dialog: Dialog.Dialog, e: Event) => void) | undefined) => showAlert(message, confirmCallback, i18n.global.t);
app.config.globalProperties['$confirm'] = (message: string, confirmCallback: (dialog: Dialog.Dialog, e: Event) => void, cancelCallback: ((dialog: Dialog.Dialog, e: Event) => void) | undefined) => showConfirm(message, confirmCallback, cancelCallback, i18n.global.t);
app.config.globalProperties['$toast'] = (message: string, timeout: number | undefined) => showToast(message, timeout, 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['$showLoading'] = showLoading;
app.config.globalProperties['$hideLoading'] = hideLoading; app.config.globalProperties['$hideLoading'] = hideLoading;
+12
View File
@@ -23,6 +23,18 @@ export class TransactionTemplate extends Transaction implements TransactionTempl
this.hidden = hidden; this.hidden = hidden;
} }
public from(other: TransactionTemplate): void {
this.templateType = other.templateType;
this.name = other.name;
if (this.templateType === TemplateType.Schedule.type) {
this.scheduledFrequencyType = other.scheduledFrequencyType;
this.scheduledFrequency = other.scheduledFrequency;
this.utcOffset = other.utcOffset;
this.timeZone = undefined;
}
}
public toTemplateCreateRequest(clientSessionId: string): TransactionTemplateCreateRequest { public toTemplateCreateRequest(clientSessionId: string): TransactionTemplateCreateRequest {
return { return {
templateType: this.templateType, templateType: this.templateType,
+3 -20
View File
@@ -43,7 +43,6 @@ import {
countSplitItems countSplitItems
} from '@/lib/common.ts'; } from '@/lib/common.ts';
import { import {
getCurrentUnixTime,
getTimezoneOffsetMinutes, getTimezoneOffsetMinutes,
getBrowserTimezoneOffsetMinutes, getBrowserTimezoneOffsetMinutes,
getActualUnixTimeForStore, getActualUnixTimeForStore,
@@ -510,21 +509,6 @@ export const useTransactionsStore = defineStore('transactions', () => {
clearUserTransactionDraft(); clearUserTransactionDraft();
} }
function generateNewTransactionModel(type: string): Transaction {
const now: number = getCurrentUnixTime();
const currentTimezone: string = settingsStore.appSettings.timeZone;
let defaultType: TransactionType = TransactionType.Expense;
if (type === TransactionType.Income.toString()) {
defaultType = TransactionType.Income;
} else if (type === TransactionType.Transfer.toString()) {
defaultType = TransactionType.Transfer;
}
return Transaction.createNewTransaction(defaultType, now, currentTimezone, getTimezoneOffsetMinutes(currentTimezone));
}
function setTransactionSuitableDestinationAmount(transaction: Transaction, oldValue: number, newValue: number): void { function setTransactionSuitableDestinationAmount(transaction: Transaction, oldValue: number, newValue: number): void {
if (transaction.type === TransactionType.Expense || transaction.type === TransactionType.Income) { if (transaction.type === TransactionType.Expense || transaction.type === TransactionType.Income) {
transaction.destinationAmount = newValue; transaction.destinationAmount = newValue;
@@ -1133,7 +1117,7 @@ export const useTransactionsStore = defineStore('transactions', () => {
}); });
} }
function uploadTransactionPicture({ pictureFile, clientSessionId }: { pictureFile: File, clientSessionId: string }): Promise<TransactionPictureInfoBasicResponse> { function uploadTransactionPicture({ pictureFile, clientSessionId }: { pictureFile: File, clientSessionId?: string }): Promise<TransactionPictureInfoBasicResponse> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
services.uploadTransactionPicture({ pictureFile, clientSessionId }).then(response => { services.uploadTransactionPicture({ pictureFile, clientSessionId }).then(response => {
const data = response.data; const data = response.data;
@@ -1183,9 +1167,9 @@ export const useTransactionsStore = defineStore('transactions', () => {
}); });
} }
function getTransactionPictureUrl(pictureInfo?: TransactionPictureInfoBasicResponse | null, disableBrowserCache?: boolean | string): string | null { function getTransactionPictureUrl(pictureInfo?: TransactionPictureInfoBasicResponse | null, disableBrowserCache?: boolean | string): string | undefined {
if (!pictureInfo || !pictureInfo.originalUrl) { if (!pictureInfo || !pictureInfo.originalUrl) {
return null; return undefined;
} }
return services.getTransactionPictureUrlWithToken(pictureInfo.originalUrl, disableBrowserCache); return services.getTransactionPictureUrlWithToken(pictureInfo.originalUrl, disableBrowserCache);
@@ -1218,7 +1202,6 @@ export const useTransactionsStore = defineStore('transactions', () => {
isTransactionDraftModified, isTransactionDraftModified,
saveTransactionDraft, saveTransactionDraft,
clearTransactionDraft, clearTransactionDraft,
generateNewTransactionModel,
setTransactionSuitableDestinationAmount, setTransactionSuitableDestinationAmount,
updateTransactionListInvalidState, updateTransactionListInvalidState,
resetTransactions, resetTransactions,
@@ -0,0 +1,450 @@
import { ref, computed, watch } 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 { useTransactionsStore } from '@/stores/transaction.ts';
import { useExchangeRatesStore } from '@/stores/exchangeRates.ts';
import type { LocalizedTimezoneInfo } from '@/core/timezone.ts';
import { CategoryType } from '@/core/category.ts';
import { TransactionType } from '@/core/transaction.ts';
import { TemplateType } from '@/core/template.ts';
import { TRANSACTION_MAX_PICTURE_COUNT } from '@/consts/transaction.ts';
import { Account, type CategorizedAccountWithDisplayBalance } from '@/models/account.ts';
import type { TransactionCategory } from '@/models/transaction_category.ts';
import type { TransactionTag } from '@/models/transaction_tag.ts';
import type { TransactionPictureInfoBasicResponse } from '@/models/transaction_picture_info.ts';
import { Transaction } from '@/models/transaction.ts';
import { TransactionTemplate } from '@/models/transaction_template.ts';
import {
isArray
} from '@/lib/common.ts';
import {
getUtcOffsetByUtcOffsetMinutes,
getTimezoneOffsetMinutes,
getCurrentUnixTime
} from '@/lib/datetime.ts';
import {
getFirstAvailableCategoryId
} from '@/lib/category.ts';
export enum TransactionEditPageType {
Transaction = 'transaction',
Template = 'template'
}
export enum TransactionEditPageMode {
Add = 'add',
Edit = 'edit',
View = 'view'
}
export enum GeoLocationStatus {
Getting = 'getting',
Success = 'success',
Error = 'error'
}
export function useTransactionEditPageBase(type: TransactionEditPageType, initMode?: TransactionEditPageMode, transactionDefaultType?: number) {
const {
tt,
getAllTimezones,
getTimezoneDifferenceDisplayText,
formatAmountWithCurrency,
getAdaptiveAmountRate,
getCategorizedAccountsWithDisplayBalance
} = useI18n();
const settingsStore = useSettingsStore();
const userStore = useUserStore();
const accountsStore = useAccountsStore();
const transactionCategoriesStore = useTransactionCategoriesStore();
const transactionTagsStore = useTransactionTagsStore();
const transactionsStore = useTransactionsStore();
const exchangeRatesStore = useExchangeRatesStore();
const isSupportGeoLocation: boolean = !!navigator.geolocation;
const mode = ref<TransactionEditPageMode>(initMode ?? TransactionEditPageMode.Add);
const editId = ref<string | null>(null);
const addByTemplateId = ref<string | null>(null);
const duplicateFromId = ref<string | null>(null);
const clientSessionId = ref<string>('');
const loading = ref<boolean>(true);
const submitting = ref<boolean>(false);
const uploadingPicture = ref<boolean>(false);
const geoLocationStatus = ref<GeoLocationStatus | null>(null);
const transaction = ref<Transaction | TransactionTemplate>(createNewTransactionModel(transactionDefaultType));
const currentTimezoneOffsetMinutes = computed<number>(() => getTimezoneOffsetMinutes(settingsStore.appSettings.timeZone));
const showAccountBalance = computed<boolean>(() => settingsStore.appSettings.showAccountBalance);
const defaultCurrency = computed<string>(() => userStore.currentUserDefaultCurrency);
const defaultAccountId = computed<string>(() => userStore.currentUserDefaultAccountId);
const firstDayOfWeek = computed<number>(() => userStore.currentUserFirstDayOfWeek);
const allTimezones = computed<LocalizedTimezoneInfo[]>(() => getAllTimezones(true));
const allAccounts = computed<Account[]>(() => accountsStore.allPlainAccounts);
const allVisibleAccounts = computed<Account[]>(() => accountsStore.allVisiblePlainAccounts);
const allAccountsMap = computed<Record<string, Account>>(() => accountsStore.allAccountsMap);
const allVisibleCategorizedAccounts = computed<CategorizedAccountWithDisplayBalance[]>(() => getCategorizedAccountsWithDisplayBalance(allVisibleAccounts.value, showAccountBalance.value));
const allCategories = computed<Record<number, TransactionCategory[]>>(() => transactionCategoriesStore.allTransactionCategories);
const allCategoriesMap = computed<Record<string, TransactionCategory>>(() => transactionCategoriesStore.allTransactionCategoriesMap);
const allTags = computed<TransactionTag[]>(() => transactionTagsStore.allTransactionTags);
const allTagsMap = computed<Record<string, TransactionTag>>(() => transactionTagsStore.allTransactionTagsMap);
const canAddTransactionPicture = computed<boolean>(() => {
if (type !== TransactionEditPageType.Transaction || (mode.value !== TransactionEditPageMode.Add && mode.value !== TransactionEditPageMode.Edit)) {
return false;
}
return !isArray(transaction.value.pictures) || transaction.value.pictures.length < TRANSACTION_MAX_PICTURE_COUNT;
});
const title = computed<string>(() => {
if (type === TransactionEditPageType.Transaction) {
if (mode.value === TransactionEditPageMode.Add) {
return 'Add Transaction';
} else if (mode.value === TransactionEditPageMode.Edit) {
return 'Edit Transaction';
} else {
return 'Transaction Detail';
}
} else if (type === TransactionEditPageType.Template && (transaction.value as TransactionTemplate).templateType === TemplateType.Normal.type) {
if (mode.value === TransactionEditPageMode.Add) {
return 'Add Transaction Template';
} else if (mode.value === TransactionEditPageMode.Edit) {
return 'Edit Transaction Template';
}
} else if (type === TransactionEditPageType.Template && (transaction.value as TransactionTemplate).templateType === TemplateType.Schedule.type) {
if (mode.value === TransactionEditPageMode.Add) {
return 'Add Scheduled Transaction';
} else if (mode.value === TransactionEditPageMode.Edit) {
return 'Edit Scheduled Transaction';
}
}
return '';
});
const saveButtonTitle = computed<string>(() => {
if (mode.value === TransactionEditPageMode.Add) {
return 'Add';
} else {
return 'Save';
}
});
const cancelButtonTitle = computed<string>(() => {
if (mode.value === TransactionEditPageMode.View) {
return 'Close';
} else {
return 'Cancel';
}
});
const sourceAmountName = computed<string>(() => {
if (transaction.value.type === TransactionType.Expense) {
return 'Expense Amount';
} else if (transaction.value.type === TransactionType.Income) {
return 'Income Amount';
} else if (transaction.value.type === TransactionType.Transfer) {
return 'Transfer Out Amount';
} else {
return 'Amount';
}
});
const sourceAccountTitle = computed<string>(() => {
if (transaction.value.type === TransactionType.Expense || transaction.value.type === TransactionType.Income) {
return 'Account';
} else if (transaction.value.type === TransactionType.Transfer) {
return 'Source Account';
} else {
return 'Account';
}
});
const transferInAmountTitle = computed<string>(() => {
const sourceAccount = allAccountsMap.value[transaction.value.sourceAccountId];
const destinationAccount = allAccountsMap.value[transaction.value.destinationAccountId];
if (!sourceAccount || !destinationAccount || sourceAccount.currency === destinationAccount.currency) {
return tt('Transfer In Amount');
}
const fromExchangeRate = exchangeRatesStore.latestExchangeRateMap[sourceAccount.currency];
const toExchangeRate = exchangeRatesStore.latestExchangeRateMap[destinationAccount.currency];
const amountRate = getAdaptiveAmountRate(transaction.value.sourceAmount, transaction.value.destinationAmount, fromExchangeRate, toExchangeRate);
if (!amountRate) {
return tt('Transfer In Amount');
}
return tt('Transfer In Amount') + ` (${amountRate})`;
});
const hasAvailableExpenseCategories = computed<boolean>(() => {
if (!allCategories.value || !allCategories.value[CategoryType.Expense] || !allCategories.value[CategoryType.Expense].length) {
return false;
}
const firstAvailableCategoryId = getFirstAvailableCategoryId(allCategories.value[CategoryType.Expense]);
return firstAvailableCategoryId !== '';
});
const hasAvailableIncomeCategories = computed<boolean>(() => {
if (!allCategories.value || !allCategories.value[CategoryType.Income] || !allCategories.value[CategoryType.Income].length) {
return false;
}
const firstAvailableCategoryId = getFirstAvailableCategoryId(allCategories.value[CategoryType.Income]);
return firstAvailableCategoryId !== '';
});
const hasAvailableTransferCategories = computed<boolean>(() => {
if (!allCategories.value || !allCategories.value[CategoryType.Transfer] || !allCategories.value[CategoryType.Transfer].length) {
return false;
}
const firstAvailableCategoryId = getFirstAvailableCategoryId(allCategories.value[CategoryType.Transfer]);
return firstAvailableCategoryId !== '';
});
const sourceAccountName = computed<string>(() => {
if (transaction.value.sourceAccountId) {
return Account.findAccountNameById(allAccounts.value, transaction.value.sourceAccountId) || '';
} else {
return tt('None');
}
});
const destinationAccountName = computed<string>(() => {
if (transaction.value.destinationAccountId) {
return Account.findAccountNameById(allAccounts.value, transaction.value.destinationAccountId) || '';
} else {
return tt('None');
}
});
const sourceAccountCurrency = computed<string>(() => {
const sourceAccount = allAccountsMap.value[transaction.value.sourceAccountId];
if (sourceAccount) {
return sourceAccount.currency;
}
return defaultCurrency.value;
});
const destinationAccountCurrency = computed<string>(() => {
const destinationAccount = allAccountsMap.value[transaction.value.destinationAccountId];
if (destinationAccount) {
return destinationAccount.currency;
}
return defaultCurrency.value;
});
const transactionDisplayTimezone = computed<string>(() => {
return `UTC${getUtcOffsetByUtcOffsetMinutes(transaction.value.utcOffset)}`;
});
const transactionTimezoneTimeDifference = computed<string>(() => {
return getTimezoneDifferenceDisplayText(transaction.value.utcOffset);
});
const geoLocationStatusInfo = computed<string>(() => {
if (geoLocationStatus.value === GeoLocationStatus.Success) {
return '';
} else if (geoLocationStatus.value === GeoLocationStatus.Getting) {
return tt('Getting Location...');
} else {
return tt('No Location');
}
});
const inputEmptyProblemMessage = computed<string | null>(() => {
if (transaction.value.type === TransactionType.Expense) {
if (!transaction.value.expenseCategoryId || transaction.value.expenseCategoryId === '') {
return 'Transaction category cannot be blank';
}
if (!transaction.value.sourceAccountId || transaction.value.sourceAccountId === '') {
return 'Transaction account cannot be blank';
}
} else if (transaction.value.type === TransactionType.Income) {
if (!transaction.value.incomeCategoryId || transaction.value.incomeCategoryId === '') {
return 'Transaction category cannot be blank';
}
if (!transaction.value.sourceAccountId || transaction.value.sourceAccountId === '') {
return 'Transaction account cannot be blank';
}
} else if (transaction.value.type === TransactionType.Transfer) {
if (!transaction.value.transferCategoryId || transaction.value.transferCategoryId === '') {
return 'Transaction category cannot be blank';
}
if (!transaction.value.sourceAccountId || transaction.value.sourceAccountId === '') {
return 'Source account cannot be blank';
}
if (!transaction.value.destinationAccountId || transaction.value.destinationAccountId === '') {
return 'Destination account cannot be blank';
}
}
if (type === 'template' && transaction.value instanceof TransactionTemplate) {
if (!transaction.value.name) {
return 'Template name cannot be blank';
}
}
return null;
});
const inputIsEmpty = computed<boolean>(() => {
return !!inputEmptyProblemMessage.value;
});
function createNewTransactionModel(transactionType?: number): Transaction | TransactionTemplate {
const now: number = getCurrentUnixTime();
const currentTimezone: string = settingsStore.appSettings.timeZone;
let defaultType: TransactionType = TransactionType.Expense;
if (transactionType === TransactionType.Income) {
defaultType = TransactionType.Income;
} else if (transactionType === TransactionType.Transfer) {
defaultType = TransactionType.Transfer;
}
let newTransaction: Transaction | TransactionTemplate = Transaction.createNewTransaction(defaultType, now, currentTimezone, getTimezoneOffsetMinutes(currentTimezone));
if (type === TransactionEditPageType.Template) {
newTransaction = TransactionTemplate.createNewTransactionTemplate(newTransaction);
}
return newTransaction;
}
function swapTransactionData(swapAccount: boolean, swapAmount: boolean): void {
if (swapAccount) {
const oldSourceAccountId = transaction.value.sourceAccountId;
transaction.value.sourceAccountId = transaction.value.destinationAccountId;
transaction.value.destinationAccountId = oldSourceAccountId;
}
if (swapAmount) {
const oldSourceAmount = transaction.value.sourceAmount;
transaction.value.sourceAmount = transaction.value.destinationAmount;
transaction.value.destinationAmount = oldSourceAmount;
}
}
function getDisplayAmount(amount: number | string, hideAmount: boolean, currencyCode: string): string {
if (hideAmount) {
return formatAmountWithCurrency('***', currencyCode);
}
return formatAmountWithCurrency(amount, currencyCode);
}
function getTransactionPictureUrl(pictureInfo?: TransactionPictureInfoBasicResponse | null): string | undefined {
return transactionsStore.getTransactionPictureUrl(pictureInfo);
}
watch(() => transaction.value.sourceAmount, (newValue, oldValue) => {
if (mode.value === TransactionEditPageMode.View || loading.value) {
return;
}
transactionsStore.setTransactionSuitableDestinationAmount(transaction.value, oldValue, newValue);
});
watch(() => transaction.value.destinationAmount, (newValue) => {
if (mode.value === TransactionEditPageMode.View || loading.value) {
return;
}
if (transaction.value.type === TransactionType.Expense || transaction.value.type === TransactionType.Income) {
transaction.value.sourceAmount = newValue;
}
});
watch(() => transaction.value.timeZone, (newValue) => {
for (let i = 0; i < allTimezones.value.length; i++) {
if (allTimezones.value[i].name === newValue) {
transaction.value.utcOffset = allTimezones.value[i].utcOffsetMinutes;
break;
}
}
});
return {
// constants
isSupportGeoLocation,
// states
mode,
editId,
addByTemplateId,
duplicateFromId,
clientSessionId,
loading,
submitting,
uploadingPicture,
geoLocationStatus,
transaction,
// computed states
currentTimezoneOffsetMinutes,
showAccountBalance,
defaultCurrency,
firstDayOfWeek,
defaultAccountId,
allTimezones,
allAccounts,
allVisibleAccounts,
allAccountsMap,
allVisibleCategorizedAccounts,
allCategories,
allCategoriesMap,
allTags,
allTagsMap,
canAddTransactionPicture,
title,
saveButtonTitle,
cancelButtonTitle,
sourceAmountName,
sourceAccountTitle,
transferInAmountTitle,
hasAvailableExpenseCategories,
hasAvailableIncomeCategories,
hasAvailableTransferCategories,
sourceAccountName,
destinationAccountName,
sourceAccountCurrency,
destinationAccountCurrency,
transactionDisplayTimezone,
transactionTimezoneTimeDifference,
geoLocationStatusInfo,
inputEmptyProblemMessage,
inputIsEmpty,
// functions
createNewTransactionModel,
swapTransactionData,
getDisplayAmount,
getTransactionPictureUrl
}
}
+2 -1
View File
@@ -139,7 +139,7 @@
</v-col> </v-col>
</v-row> </v-row>
<edit-dialog ref="editDialog" type="template" :persistent="true" /> <edit-dialog ref="editDialog" :type="TransactionEditPageType.Template" :persistent="true" />
<confirm-dialog ref="confirmDialog"/> <confirm-dialog ref="confirmDialog"/>
<snack-bar ref="snackbar" /> <snack-bar ref="snackbar" />
@@ -149,6 +149,7 @@
import ConfirmDialog from '@/components/desktop/ConfirmDialog.vue'; import ConfirmDialog from '@/components/desktop/ConfirmDialog.vue';
import SnackBar from '@/components/desktop/SnackBar.vue'; import SnackBar from '@/components/desktop/SnackBar.vue';
import EditDialog from '@/views/desktop/transactions/list/dialogs/EditDialog.vue'; import EditDialog from '@/views/desktop/transactions/list/dialogs/EditDialog.vue';
import { TransactionEditPageType } from '@/views/base/transactions/TransactionEditPageBase.ts';
import { ref, computed, useTemplateRef } from 'vue'; import { ref, computed, useTemplateRef } from 'vue';
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff