From 0ce66d907085fe96f1a898556051a9f2632af55a Mon Sep 17 00:00:00 2001 From: MaysWind Date: Sun, 4 Jan 2026 22:50:13 +0800 Subject: [PATCH] support changing account category order --- pkg/models/user_app_cloud_setting.go | 1 + src/core/account.ts | 45 ++++++- src/core/setting.ts | 3 + src/lib/account.ts | 18 +-- src/locales/de.json | 4 + src/locales/en.json | 4 + src/locales/es.json | 4 + src/locales/fr.json | 4 + src/locales/helpers.ts | 11 +- src/locales/it.json | 4 + src/locales/ja.json | 4 + src/locales/kn.json | 4 + src/locales/ko.json | 4 + src/locales/nl.json | 4 + src/locales/pt_BR.json | 4 + src/locales/ru.json | 4 + src/locales/sl.json | 4 + src/locales/th.json | 4 + src/locales/tr.json | 4 + src/locales/uk.json | 4 + src/locales/vi.json | 4 + src/locales/zh_Hans.json | 4 + src/locales/zh_Hant.json | 4 + src/models/account.ts | 19 ++- src/router/mobile.ts | 6 + src/stores/account.ts | 27 ++-- src/stores/explorer.ts | 12 +- src/stores/setting.ts | 13 ++ src/stores/statistics.ts | 20 ++- .../base/accounts/AccountEditPageBase.ts | 11 +- .../base/accounts/AccountListPageBase.ts | 7 +- .../accounts/MoveAllTransactionsPageBase.ts | 3 +- ...untCategoryDisplayOrderSettingsPageBase.ts | 63 ++++++++++ .../settings/AccountFilterSettingPageBase.ts | 10 +- .../base/settings/AppCloudSyncPageBase.ts | 1 + .../base/settings/AppSettingsPageBase.ts | 9 ++ .../transactions/TransactionEditPageBase.ts | 3 +- src/views/base/users/UserProfilePageBase.ts | 2 +- src/views/desktop/accounts/ListPage.vue | 6 +- .../accounts/list/dialogs/EditDialog.vue | 5 +- .../AccountCategoryDisplayOrderDialog.vue | 113 +++++++++++++++++ .../app/settings/tabs/AppBasicSettingTab.vue | 19 +++ .../cards/AccountFilterSettingsCard.vue | 3 +- .../dialogs/BatchReplaceAllTypesDialog.vue | 3 +- .../import/dialogs/BatchReplaceDialog.vue | 3 +- .../tabs/ImportTransactionCheckDataTab.vue | 3 +- src/views/mobile/accounts/ListPage.vue | 3 +- ...ccountCategoryDisplayOrderSettingsPage.vue | 117 ++++++++++++++++++ .../settings/AccountFilterSettingsPage.vue | 3 +- .../mobile/settings/PageSettingsPage.vue | 13 ++ 50 files changed, 575 insertions(+), 72 deletions(-) create mode 100644 src/views/base/settings/AccountCategoryDisplayOrderSettingsPageBase.ts create mode 100644 src/views/desktop/app/settings/dialogs/AccountCategoryDisplayOrderDialog.vue create mode 100644 src/views/mobile/settings/AccountCategoryDisplayOrderSettingsPage.vue diff --git a/pkg/models/user_app_cloud_setting.go b/pkg/models/user_app_cloud_setting.go index 09548b82..6b512473 100644 --- a/pkg/models/user_app_cloud_setting.go +++ b/pkg/models/user_app_cloud_setting.go @@ -34,6 +34,7 @@ var ALL_ALLOWED_CLOUD_SYNC_APP_SETTING_KEY_TYPES = map[string]UserApplicationClo "showTagInInsightsExplorerPage": USER_APPLICATION_CLOUD_SETTING_TYPE_BOOLEAN, // Account List Page "totalAmountExcludeAccountIds": USER_APPLICATION_CLOUD_SETTING_TYPE_STRING_BOOLEAN_MAP, + "accountCategoryOrders": USER_APPLICATION_CLOUD_SETTING_TYPE_STRING, "hideCategoriesWithoutAccounts": USER_APPLICATION_CLOUD_SETTING_TYPE_BOOLEAN, // Exchange Rates Data Page "currencySortByInExchangeRatesPage": USER_APPLICATION_CLOUD_SETTING_TYPE_NUMBER, diff --git a/src/core/account.ts b/src/core/account.ts index 21b5a1a8..c66e94a3 100644 --- a/src/core/account.ts +++ b/src/core/account.ts @@ -1,4 +1,4 @@ -import type { TypeAndName, TypeAndDisplayName } from './base.ts'; +import { type TypeAndName, type TypeAndDisplayName, itemAndIndex } from './base.ts'; export class AccountType implements TypeAndName { private static readonly allInstances: AccountType[] = []; @@ -38,15 +38,15 @@ export class AccountCategory implements TypeAndName { public static readonly Default = AccountCategory.Cash; public readonly type: number; - public readonly displayOrder: number; + public readonly defaultDisplayOrder: number; public readonly name: string; public readonly isAsset: boolean; public readonly isLiability: boolean public readonly defaultAccountIconId: string; - private constructor(type: number, displayOrder: number, name: string, isAsset: boolean, isLiability: boolean, defaultAccountIconId: string) { + private constructor(type: number, defaultDisplayOrder: number, name: string, isAsset: boolean, isLiability: boolean, defaultAccountIconId: string) { this.type = type; - this.displayOrder = displayOrder; + this.defaultDisplayOrder = defaultDisplayOrder; this.name = name; this.isAsset = isAsset; this.isLiability = isLiability; @@ -56,8 +56,41 @@ export class AccountCategory implements TypeAndName { AccountCategory.allInstancesByType[type] = this; } - public static values(): AccountCategory[] { - return AccountCategory.allInstances; + public static values(customAccountCategoryOrder?: string): AccountCategory[] { + if (!customAccountCategoryOrder) { + return [...AccountCategory.allInstances]; + } + + const typeOrders: string[] = customAccountCategoryOrder.split(','); + const orderedCategories: AccountCategory[] = []; + const addedTypes: Record = {}; + + for (const type of typeOrders) { + const category = AccountCategory.valueOf(parseInt(type.trim())); + + if (category) { + orderedCategories.push(category); + addedTypes[type] = true; + } + } + + for (const category of AccountCategory.allInstances) { + if (!addedTypes[category.type]) { + orderedCategories.push(category); + } + } + + return orderedCategories; + } + + public static allDisplayOrders(customAccountCategoryOrder: string): Record { + const displayOrders: Record = {}; + + for (const [category, index] of itemAndIndex(AccountCategory.values(customAccountCategoryOrder))) { + displayOrders[category.type] = index + 1; + } + + return displayOrders; } public static valueOf(type: number): AccountCategory | undefined { diff --git a/src/core/setting.ts b/src/core/setting.ts index 776f1ccf..61c4babc 100644 --- a/src/core/setting.ts +++ b/src/core/setting.ts @@ -55,6 +55,7 @@ export interface ApplicationSettings extends BaseApplicationSetting { showTagInInsightsExplorerPage: boolean; // Account List Page totalAmountExcludeAccountIds: Record; + accountCategoryOrders: string; hideCategoriesWithoutAccounts: boolean; // Exchange Rates Data Page currencySortByInExchangeRatesPage: number; @@ -121,6 +122,7 @@ export const ALL_ALLOWED_CLOUD_SYNC_APP_SETTING_KEY_TYPES: Record, allowAccountName?: string, showHidden?: boolean): Record { - const ret: Record = {}; - const allCategories = AccountCategory.values(); +export function filterCategorizedAccounts(categorizedAccountsMap: Record, customAccountCategoryOrder: string, allowAccountName?: string, showHidden?: boolean): CategorizedAccount[] { + const ret: CategorizedAccount[] = []; + const allCategories = AccountCategory.values(customAccountCategoryOrder); const lowercaseFilterContent = allowAccountName ? allowAccountName.toLowerCase() : ''; for (const accountCategory of allCategories) { @@ -122,20 +122,20 @@ export function filterCategorizedAccounts(categorizedAccountsMap: Record 0) { - ret[accountCategory.type] = { + ret.push({ category: categorizedAccount.category, name: categorizedAccount.name, icon: categorizedAccount.icon, accounts: allFilteredAccounts - }; + }); } } return ret; } -export function getAllFilteredAccountsBalance(categorizedAccounts: Record, accountFilter: (account: Account) => boolean): AccountBalance[] { - const allAccountCategories = AccountCategory.values(); +export function getAllFilteredAccountsBalance(categorizedAccounts: Record, customAccountCategoryOrder: string, accountFilter: (account: Account) => boolean): AccountBalance[] { + const allAccountCategories = AccountCategory.values(customAccountCategoryOrder); const ret: AccountBalance[] = []; for (const accountCategory of allAccountCategories) { diff --git a/src/locales/de.json b/src/locales/de.json index b056ccd3..c3c6d797 100644 --- a/src/locales/de.json +++ b/src/locales/de.json @@ -1435,6 +1435,7 @@ "Apply": "Anwenden", "Save": "Speichern", "Save Changes": "Änderungen speichern", + "Reset to Default": "Reset to Default", "Reset": "Zurücksetzen", "Update": "Aktualisieren", "Refresh": "Aktualisieren", @@ -2190,6 +2191,9 @@ "Insights Explorer Page": "Insights Explorer Page", "Account List Page": "Account List Page", "Accounts Included in Total": "Accounts Included in Total", + "Account Category Order": "Account Category Order", + "Account category order saved": "Account category order saved", + "Unable to move account category": "Unable to move account category", "Hide Categories Without Accounts": "Hide Categories Without Accounts", "Exchange Rates Data Page": "Wechselkursdatenseite", "Exchange Rate": "Wechselkurs", diff --git a/src/locales/en.json b/src/locales/en.json index c2ed2787..a45f73cd 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1435,6 +1435,7 @@ "Apply": "Apply", "Save": "Save", "Save Changes": "Save Changes", + "Reset to Default": "Reset to Default", "Reset": "Reset", "Update": "Update", "Refresh": "Refresh", @@ -2190,6 +2191,9 @@ "Insights Explorer Page": "Insights Explorer Page", "Account List Page": "Account List Page", "Accounts Included in Total": "Accounts Included in Total", + "Account Category Order": "Account Category Order", + "Account category order saved": "Account category order saved", + "Unable to move account category": "Unable to move account category", "Hide Categories Without Accounts": "Hide Categories Without Accounts", "Exchange Rates Data Page": "Exchange Rates Data Page", "Exchange Rate": "Exchange Rate", diff --git a/src/locales/es.json b/src/locales/es.json index ca67e231..f1659d28 100644 --- a/src/locales/es.json +++ b/src/locales/es.json @@ -1435,6 +1435,7 @@ "Apply": "Aplicar", "Save": "Guardar", "Save Changes": "Guardar Cambios", + "Reset to Default": "Reset to Default", "Reset": "Reiniciar", "Update": "Actualizar", "Refresh": "Refrescar", @@ -2190,6 +2191,9 @@ "Insights Explorer Page": "Insights Explorer Page", "Account List Page": "Página de Cuentas", "Accounts Included in Total": "Cuentas Incluidas en el Total", + "Account Category Order": "Account Category Order", + "Account category order saved": "Account category order saved", + "Unable to move account category": "Unable to move account category", "Hide Categories Without Accounts": "Hide Categories Without Accounts", "Exchange Rates Data Page": "Página de Tipos de Cambio", "Exchange Rate": "Tipo de Cambio", diff --git a/src/locales/fr.json b/src/locales/fr.json index 9598311e..2be1b524 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -1435,6 +1435,7 @@ "Apply": "Appliquer", "Save": "Enregistrer", "Save Changes": "Enregistrer les modifications", + "Reset to Default": "Reset to Default", "Reset": "Réinitialiser", "Update": "Mettre à jour", "Refresh": "Actualiser", @@ -2190,6 +2191,9 @@ "Insights Explorer Page": "Insights Explorer Page", "Account List Page": "Page de liste des comptes", "Accounts Included in Total": "Comptes inclus dans le total", + "Account Category Order": "Account Category Order", + "Account category order saved": "Account category order saved", + "Unable to move account category": "Unable to move account category", "Hide Categories Without Accounts": "Hide Categories Without Accounts", "Exchange Rates Data Page": "Page des données de taux de change", "Exchange Rate": "Taux de change", diff --git a/src/locales/helpers.ts b/src/locales/helpers.ts index 66c616d1..ede373ef 100644 --- a/src/locales/helpers.ts +++ b/src/locales/helpers.ts @@ -1349,9 +1349,9 @@ export function useI18n() { return ret; } - function getAllAccountCategories(): LocalizedAccountCategory[] { + function getAllAccountCategories(customAccountCategoryOrder: string): LocalizedAccountCategory[] { const ret: LocalizedAccountCategory[] = []; - const allCategories = AccountCategory.values(); + const allCategories = AccountCategory.values(customAccountCategoryOrder); for (const accountCategory of allCategories) { ret.push({ @@ -2094,10 +2094,10 @@ export function useI18n() { return getAmountPrependAndAppendCurrencySymbol(currencyDisplayType, currencyCode, currencyUnit, currencyName, isPlural); } - function getCategorizedAccountsWithDisplayBalance(allVisibleAccounts: Account[], showAccountBalance: boolean): CategorizedAccountWithDisplayBalance[] { + function getCategorizedAccountsWithDisplayBalance(allVisibleAccounts: Account[], showAccountBalance: boolean, customAccountCategoryOrder: string): CategorizedAccountWithDisplayBalance[] { const ret: CategorizedAccountWithDisplayBalance[] = []; const defaultCurrency = userStore.currentUserDefaultCurrency; - const allCategories = AccountCategory.values(); + const allCategories = AccountCategory.values(customAccountCategoryOrder); const categorizedAccounts: Record = getCategorizedAccountsMap(Account.cloneAccounts(allVisibleAccounts)); for (const category of allCategories) { @@ -2128,7 +2128,8 @@ export function useI18n() { let finalTotalBalance = ''; if (showAccountBalance) { - const accountsBalance = getAllFilteredAccountsBalance(categorizedAccounts, account => account.category === accountCategory.category); + const accountsBalance = getAllFilteredAccountsBalance(categorizedAccounts, customAccountCategoryOrder, + account => account.category === accountCategory.category); let totalBalance = 0; let hasUnCalculatedAmount = false; diff --git a/src/locales/it.json b/src/locales/it.json index 3adfa71c..c844cfed 100644 --- a/src/locales/it.json +++ b/src/locales/it.json @@ -1435,6 +1435,7 @@ "Apply": "Applica", "Save": "Salva", "Save Changes": "Salva modifiche", + "Reset to Default": "Reset to Default", "Reset": "Reimposta", "Update": "Aggiorna", "Refresh": "Aggiorna", @@ -2190,6 +2191,9 @@ "Insights Explorer Page": "Insights Explorer Page", "Account List Page": "Account List Page", "Accounts Included in Total": "Accounts Included in Total", + "Account Category Order": "Account Category Order", + "Account category order saved": "Account category order saved", + "Unable to move account category": "Unable to move account category", "Hide Categories Without Accounts": "Hide Categories Without Accounts", "Exchange Rates Data Page": "Pagina dati tassi di cambio", "Exchange Rate": "Tasso di cambio", diff --git a/src/locales/ja.json b/src/locales/ja.json index 530337d2..76a32ae3 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -1435,6 +1435,7 @@ "Apply": "適用", "Save": "保存", "Save Changes": "変更を保存", + "Reset to Default": "Reset to Default", "Reset": "リセット", "Update": "アップデート", "Refresh": "リフレッシュ", @@ -2190,6 +2191,9 @@ "Insights Explorer Page": "Insights Explorer Page", "Account List Page": "Account List Page", "Accounts Included in Total": "Accounts Included in Total", + "Account Category Order": "Account Category Order", + "Account category order saved": "Account category order saved", + "Unable to move account category": "Unable to move account category", "Hide Categories Without Accounts": "Hide Categories Without Accounts", "Exchange Rates Data Page": "為替レートデータページ", "Exchange Rate": "為替レート", diff --git a/src/locales/kn.json b/src/locales/kn.json index 0d3eaccd..3e01ac99 100644 --- a/src/locales/kn.json +++ b/src/locales/kn.json @@ -1435,6 +1435,7 @@ "Apply": "ಅನ್ವಯಿಸು", "Save": "ಉಳಿಸು", "Save Changes": "ಬದಲಾವಣೆಗಳನ್ನು ಉಳಿಸು", + "Reset to Default": "Reset to Default", "Reset": "ಮರುಹೊಂದಿಸು", "Update": "ನವೀಕರಿಸು", "Refresh": "ರಿಫ್ರೆಶ್", @@ -2190,6 +2191,9 @@ "Insights Explorer Page": "Insights Explorer Page", "Account List Page": "ಖಾತೆ ಪಟ್ಟಿ ಪುಟ", "Accounts Included in Total": "ಒಟ್ಟು ಮೊತ್ತದಲ್ಲಿ ಒಳಗೊಂಡ ಖಾತೆಗಳು", + "Account Category Order": "Account Category Order", + "Account category order saved": "Account category order saved", + "Unable to move account category": "Unable to move account category", "Hide Categories Without Accounts": "Hide Categories Without Accounts", "Exchange Rates Data Page": "ವಿನಿಮಯ ದರಗಳ ಪುಟ", "Exchange Rate": "ವಿನಿಮಯ ದರ", diff --git a/src/locales/ko.json b/src/locales/ko.json index 3b3544b8..b9fba4ff 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -1435,6 +1435,7 @@ "Apply": "적용", "Save": "저장", "Save Changes": "변경사항 저장", + "Reset to Default": "Reset to Default", "Reset": "재설정", "Update": "업데이트", "Refresh": "새로고침", @@ -2190,6 +2191,9 @@ "Insights Explorer Page": "Insights Explorer Page", "Account List Page": "계좌 목록 페이지", "Accounts Included in Total": "총계에 포함된 계좌", + "Account Category Order": "Account Category Order", + "Account category order saved": "Account category order saved", + "Unable to move account category": "Unable to move account category", "Hide Categories Without Accounts": "Hide Categories Without Accounts", "Exchange Rates Data Page": "환율 데이터 페이지", "Exchange Rate": "환율", diff --git a/src/locales/nl.json b/src/locales/nl.json index 022a2a40..00eaa819 100644 --- a/src/locales/nl.json +++ b/src/locales/nl.json @@ -1435,6 +1435,7 @@ "Apply": "Toepassen", "Save": "Opslaan", "Save Changes": "Wijzigingen opslaan", + "Reset to Default": "Reset to Default", "Reset": "Resetten", "Update": "Bijwerken", "Refresh": "Vernieuwen", @@ -2190,6 +2191,9 @@ "Insights Explorer Page": "Insights Explorer Page", "Account List Page": "Rekeningenpagina", "Accounts Included in Total": "Rekeningen opgenomen in totaal", + "Account Category Order": "Account Category Order", + "Account category order saved": "Account category order saved", + "Unable to move account category": "Unable to move account category", "Hide Categories Without Accounts": "Hide Categories Without Accounts", "Exchange Rates Data Page": "Wisselkoersgegevenspagina", "Exchange Rate": "Wisselkoers", diff --git a/src/locales/pt_BR.json b/src/locales/pt_BR.json index f95f60b2..a44d0daa 100644 --- a/src/locales/pt_BR.json +++ b/src/locales/pt_BR.json @@ -1435,6 +1435,7 @@ "Apply": "Aplicar", "Save": "Salvar", "Save Changes": "Salvar Alterações", + "Reset to Default": "Reset to Default", "Reset": "Redefinir", "Update": "Atualizar", "Refresh": "Atualizar", @@ -2190,6 +2191,9 @@ "Insights Explorer Page": "Insights Explorer Page", "Account List Page": "Account List Page", "Accounts Included in Total": "Accounts Included in Total", + "Account Category Order": "Account Category Order", + "Account category order saved": "Account category order saved", + "Unable to move account category": "Unable to move account category", "Hide Categories Without Accounts": "Hide Categories Without Accounts", "Exchange Rates Data Page": "Página de Dados de Taxas de Câmbio", "Exchange Rate": "Taxa de Câmbio", diff --git a/src/locales/ru.json b/src/locales/ru.json index 5a8ca6b9..42da4e81 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -1435,6 +1435,7 @@ "Apply": "Применить", "Save": "Сохранить", "Save Changes": "Сохранить изменения", + "Reset to Default": "Reset to Default", "Reset": "Сбросить", "Update": "Обновить", "Refresh": "Обновить", @@ -2190,6 +2191,9 @@ "Insights Explorer Page": "Insights Explorer Page", "Account List Page": "Account List Page", "Accounts Included in Total": "Accounts Included in Total", + "Account Category Order": "Account Category Order", + "Account category order saved": "Account category order saved", + "Unable to move account category": "Unable to move account category", "Hide Categories Without Accounts": "Hide Categories Without Accounts", "Exchange Rates Data Page": "Страница данных о курсах валют", "Exchange Rate": "Курс обмена", diff --git a/src/locales/sl.json b/src/locales/sl.json index d89bcc19..33016854 100644 --- a/src/locales/sl.json +++ b/src/locales/sl.json @@ -1435,6 +1435,7 @@ "Apply": "Uporabi", "Save": "Shrani", "Save Changes": "Shrani spremembe", + "Reset to Default": "Reset to Default", "Reset": "Ponastavi", "Update": "Posodobi", "Refresh": "Osveži", @@ -2190,6 +2191,9 @@ "Insights Explorer Page": "Stran za vpoglede in raziskovanje", "Account List Page": "Stran s seznamom računov", "Accounts Included in Total": "Računi vključeni v skupno vsoto", + "Account Category Order": "Account Category Order", + "Account category order saved": "Account category order saved", + "Unable to move account category": "Unable to move account category", "Hide Categories Without Accounts": "Hide Categories Without Accounts", "Exchange Rates Data Page": "Stran z menjalnimi tečaji", "Exchange Rate": "Menjalni tečaj", diff --git a/src/locales/th.json b/src/locales/th.json index b55d1f0a..cce91e4a 100644 --- a/src/locales/th.json +++ b/src/locales/th.json @@ -1435,6 +1435,7 @@ "Apply": "ใช้", "Save": "บันทึก", "Save Changes": "บันทึกการเปลี่ยนแปลง", + "Reset to Default": "Reset to Default", "Reset": "รีเซ็ต", "Update": "อัปเดต", "Refresh": "รีเฟรช", @@ -2190,6 +2191,9 @@ "Insights Explorer Page": "Insights Explorer Page", "Account List Page": "หน้าบัญชี", "Accounts Included in Total": "บัญชีที่รวมในผลรวม", + "Account Category Order": "Account Category Order", + "Account category order saved": "Account category order saved", + "Unable to move account category": "Unable to move account category", "Hide Categories Without Accounts": "Hide Categories Without Accounts", "Exchange Rates Data Page": "หน้าข้อมูลอัตราแลกเปลี่ยน", "Exchange Rate": "อัตราแลกเปลี่ยน", diff --git a/src/locales/tr.json b/src/locales/tr.json index 7153be79..0dd1fa29 100644 --- a/src/locales/tr.json +++ b/src/locales/tr.json @@ -1435,6 +1435,7 @@ "Apply": "Uygula", "Save": "Kaydet", "Save Changes": "Değişiklikleri Kaydet", + "Reset to Default": "Reset to Default", "Reset": "Sıfırla", "Update": "Güncelle", "Refresh": "Yenile", @@ -2190,6 +2191,9 @@ "Insights Explorer Page": "Insights Explorer Page", "Account List Page": "Hesap Listesi Sayfası", "Accounts Included in Total": "Toplama Dahil Edilen Hesaplar", + "Account Category Order": "Account Category Order", + "Account category order saved": "Account category order saved", + "Unable to move account category": "Unable to move account category", "Hide Categories Without Accounts": "Hide Categories Without Accounts", "Exchange Rates Data Page": "Döviz Kuru Verileri Sayfası", "Exchange Rate": "Döviz Kuru", diff --git a/src/locales/uk.json b/src/locales/uk.json index 2484a272..441c6517 100644 --- a/src/locales/uk.json +++ b/src/locales/uk.json @@ -1435,6 +1435,7 @@ "Apply": "Застосувати", "Save": "Зберегти", "Save Changes": "Зберегти зміни", + "Reset to Default": "Reset to Default", "Reset": "Скинути", "Update": "Оновити", "Refresh": "Оновити", @@ -2190,6 +2191,9 @@ "Insights Explorer Page": "Insights Explorer Page", "Account List Page": "Account List Page", "Accounts Included in Total": "Accounts Included in Total", + "Account Category Order": "Account Category Order", + "Account category order saved": "Account category order saved", + "Unable to move account category": "Unable to move account category", "Hide Categories Without Accounts": "Hide Categories Without Accounts", "Exchange Rates Data Page": "Сторінка курсів валют", "Exchange Rate": "Курс обміну", diff --git a/src/locales/vi.json b/src/locales/vi.json index 1765ea65..056278d7 100644 --- a/src/locales/vi.json +++ b/src/locales/vi.json @@ -1435,6 +1435,7 @@ "Apply": "Áp dụng", "Save": "Lưu", "Save Changes": "Lưu thay đổi", + "Reset to Default": "Reset to Default", "Reset": "Đặt lại", "Update": "Cập nhật", "Refresh": "Làm mới", @@ -2190,6 +2191,9 @@ "Insights Explorer Page": "Insights Explorer Page", "Account List Page": "Account List Page", "Accounts Included in Total": "Accounts Included in Total", + "Account Category Order": "Account Category Order", + "Account category order saved": "Account category order saved", + "Unable to move account category": "Unable to move account category", "Hide Categories Without Accounts": "Hide Categories Without Accounts", "Exchange Rates Data Page": "Trang dữ liệu tỷ giá hối đoái", "Exchange Rate": "Tỷ giá hối đoái", diff --git a/src/locales/zh_Hans.json b/src/locales/zh_Hans.json index 99b5e127..73658fc5 100644 --- a/src/locales/zh_Hans.json +++ b/src/locales/zh_Hans.json @@ -1435,6 +1435,7 @@ "Apply": "应用", "Save": "保存", "Save Changes": "保存修改", + "Reset to Default": "恢复为默认值", "Reset": "重置", "Update": "更新", "Refresh": "刷新", @@ -2190,6 +2191,9 @@ "Insights Explorer Page": "洞察探索页面", "Account List Page": "账户列表页面", "Accounts Included in Total": "计入总金额的账户", + "Account Category Order": "账户分类顺序", + "Account category order saved": "账户分类顺序已保存", + "Unable to move account category": "无法移动账户分类", "Hide Categories Without Accounts": "隐藏没有账户的分类", "Exchange Rates Data Page": "汇率数据页面", "Exchange Rate": "汇率", diff --git a/src/locales/zh_Hant.json b/src/locales/zh_Hant.json index bda09378..3584f3ce 100644 --- a/src/locales/zh_Hant.json +++ b/src/locales/zh_Hant.json @@ -1435,6 +1435,7 @@ "Apply": "套用", "Save": "儲存", "Save Changes": "儲存修改", + "Reset to Default": "重設為預設值", "Reset": "重置", "Update": "更新", "Refresh": "重新載入", @@ -2190,6 +2191,9 @@ "Insights Explorer Page": "洞察探索頁面", "Account List Page": "帳戶清單頁面", "Accounts Included in Total": "計入總金額的帳戶", + "Account Category Order": "帳戶分類順序", + "Account category order saved": "帳戶分類順序已儲存", + "Unable to move account category": "無法移動帳戶分類", "Hide Categories Without Accounts": "隱藏沒有帳戶的分類", "Exchange Rates Data Page": "匯率資料頁面", "Exchange Rate": "匯率", diff --git a/src/models/account.ts b/src/models/account.ts index dec7ba4f..830ff2c0 100644 --- a/src/models/account.ts +++ b/src/models/account.ts @@ -2,7 +2,6 @@ import type { HiddenAmount, NumberWithSuffix } from '@/core/numeral.ts'; import type { ColorValue } from '@/core/color.ts'; import { AccountType, AccountCategory } from '@/core/account.ts'; import { PARENT_ACCOUNT_CURRENCY_PLACEHOLDER } from '@/consts/currency.ts'; -import { DEFAULT_ACCOUNT_ICON_ID } from '@/consts/icon.ts'; import { DEFAULT_ACCOUNT_COLOR } from '@/consts/color.ts'; export class Account implements AccountInfoResponse { @@ -410,14 +409,14 @@ export class Account implements AccountInfoResponse { ); } - public static createNewAccount(currency: string, balanceTime: number): Account { + public static createNewAccount(accountCategory: AccountCategory, currency: string, balanceTime: number): Account { return new Account( '', // id '', // name '', // parentId - AccountCategory.Cash.type, // category + accountCategory.type, // category AccountType.SingleAccount.type, // type - DEFAULT_ACCOUNT_ICON_ID, // icon + accountCategory.defaultAccountIconId, // icon DEFAULT_ACCOUNT_COLOR, // color currency, // currency 0, // balance @@ -481,25 +480,25 @@ export class Account implements AccountInfoResponse { return clonedAccounts; } - public static sortAccounts(accounts: Account[], allAccountsMap?: Record): Account[] { + public static sortAccounts(accounts: Account[], accountCategoryDisplayOrders: Record, allAccountsMap?: Record): Account[] { if (!accounts || !accounts.length) { return accounts; } return accounts.sort(function (account1, account2) { if (account1.category !== account2.category) { - const account1Category = AccountCategory.valueOf(account1.category); - const account2Category = AccountCategory.valueOf(account2.category); + const account1CategoryDisplayOrder = accountCategoryDisplayOrders[account1.category]; + const account2CategoryDisplayOrder = accountCategoryDisplayOrders[account2.category]; - if (!account1Category) { + if (!account1CategoryDisplayOrder) { return 1; } - if (!account2Category) { + if (!account2CategoryDisplayOrder) { return -1; } - return account1Category.displayOrder - account2Category.displayOrder; + return account1CategoryDisplayOrder - account2CategoryDisplayOrder; } if (account1.parentId === account2.parentId) { diff --git a/src/router/mobile.ts b/src/router/mobile.ts index 993f3582..e10290a1 100644 --- a/src/router/mobile.ts +++ b/src/router/mobile.ts @@ -21,6 +21,7 @@ import StatisticsSettingsPage from '@/views/mobile/statistics/SettingsPage.vue'; import TextSizeSettingsPage from '@/views/mobile/settings/TextSizeSettingsPage.vue'; import PageSettingsPage from '@/views/mobile/settings/PageSettingsPage.vue'; +import AccountCategoryDisplayOrderSettingsPage from '@/views/mobile/settings/AccountCategoryDisplayOrderSettingsPage.vue'; import ApplicationCloudSyncSettingsPage from '@/views/mobile/settings/ApplicationCloudSyncSettingsPage.vue'; import AccountFilterSettingsPage from '@/views/mobile/settings/AccountFilterSettingsPage.vue'; import CategoryFilterSettingsPage from '@/views/mobile/settings/CategoryFilterSettingsPage.vue'; @@ -238,6 +239,11 @@ const routes: Router.RouteParameters[] = [ async: asyncResolve(PageSettingsPage), beforeEnter: [checkLogin] }, + { + path: '/settings/account_category_display_order', + async: asyncResolve(AccountCategoryDisplayOrderSettingsPage), + beforeEnter: [checkLogin] + }, { path: '/settings/sync', async: asyncResolve(ApplicationCloudSyncSettingsPage), diff --git a/src/stores/account.ts b/src/stores/account.ts index 2843bb7d..8e26c43a 100644 --- a/src/stores/account.ts +++ b/src/stores/account.ts @@ -48,7 +48,7 @@ export const useAccountsStore = defineStore('accounts', () => { } } - return Account.sortAccounts(allAccountsList, allAccountsMap.value); + return Account.sortAccounts(allAccountsList, settingsStore.accountCategoryDisplayOrders, allAccountsMap.value); }); const allMixedPlainAccounts = computed(() => { @@ -68,7 +68,7 @@ export const useAccountsStore = defineStore('accounts', () => { } } - return Account.sortAccounts(allAccountsList, allAccountsMap.value); + return Account.sortAccounts(allAccountsList, settingsStore.accountCategoryDisplayOrders, allAccountsMap.value); }); const allVisiblePlainAccounts = computed(() => { @@ -95,7 +95,7 @@ export const useAccountsStore = defineStore('accounts', () => { } } - return Account.sortAccounts(allVisibleAccounts, allAccountsMap.value); + return Account.sortAccounts(allVisibleAccounts, settingsStore.accountCategoryDisplayOrders, allAccountsMap.value); }); const allAvailableAccountsCount = computed(() => { @@ -148,8 +148,10 @@ export const useAccountsStore = defineStore('accounts', () => { if (newAccountCategory) { for (const [account, index] of itemAndIndex(allAccounts.value)) { const accountCategory = AccountCategory.valueOf(account.category); + const accountCategoryDisplayOrder = settingsStore.accountCategoryDisplayOrders[accountCategory?.type ?? 0] || Number.MAX_SAFE_INTEGER; + const newAccountCategoryDisplayOrder = settingsStore.accountCategoryDisplayOrders[newAccountCategory.type] || Number.MAX_SAFE_INTEGER; - if (accountCategory && accountCategory.displayOrder > newAccountCategory.displayOrder) { + if (accountCategory && accountCategoryDisplayOrder > newAccountCategoryDisplayOrder) { insertIndexToAllList = index; break; } @@ -457,8 +459,8 @@ export const useAccountsStore = defineStore('accounts', () => { return DISPLAY_HIDDEN_AMOUNT; } - const accountsBalance = getAllFilteredAccountsBalance(allCategorizedAccountsMap.value, account => - !(account.type === AccountType.SingleAccount.type && settingsStore.appSettings.totalAmountExcludeAccountIds[account.id]) + const accountsBalance = getAllFilteredAccountsBalance(allCategorizedAccountsMap.value, settingsStore.appSettings.accountCategoryOrders, + account => !(account.type === AccountType.SingleAccount.type && settingsStore.appSettings.totalAmountExcludeAccountIds[account.id]) ); let netAssets = 0; let hasUnCalculatedAmount = false; @@ -493,8 +495,8 @@ export const useAccountsStore = defineStore('accounts', () => { return DISPLAY_HIDDEN_AMOUNT; } - const accountsBalance = getAllFilteredAccountsBalance(allCategorizedAccountsMap.value, account => - (account.isAsset || false) && !(account.type === AccountType.SingleAccount.type && settingsStore.appSettings.totalAmountExcludeAccountIds[account.id]) + const accountsBalance = getAllFilteredAccountsBalance(allCategorizedAccountsMap.value, settingsStore.appSettings.accountCategoryOrders, + account => (account.isAsset || false) && !(account.type === AccountType.SingleAccount.type && settingsStore.appSettings.totalAmountExcludeAccountIds[account.id]) ); let totalAssets = 0; let hasUnCalculatedAmount = false; @@ -529,8 +531,8 @@ export const useAccountsStore = defineStore('accounts', () => { return DISPLAY_HIDDEN_AMOUNT; } - const accountsBalance = getAllFilteredAccountsBalance(allCategorizedAccountsMap.value, account => - (account.isLiability || false) && !(account.type === AccountType.SingleAccount.type && settingsStore.appSettings.totalAmountExcludeAccountIds[account.id]) + const accountsBalance = getAllFilteredAccountsBalance(allCategorizedAccountsMap.value, settingsStore.appSettings.accountCategoryOrders, + account => (account.isLiability || false) && !(account.type === AccountType.SingleAccount.type && settingsStore.appSettings.totalAmountExcludeAccountIds[account.id]) ); let totalLiabilities = 0; let hasUnCalculatedAmount = false; @@ -565,7 +567,8 @@ export const useAccountsStore = defineStore('accounts', () => { return DISPLAY_HIDDEN_AMOUNT; } - const accountsBalance = getAllFilteredAccountsBalance(allCategorizedAccountsMap.value, account => account.category === accountCategory.type); + const accountsBalance = getAllFilteredAccountsBalance(allCategorizedAccountsMap.value, settingsStore.appSettings.accountCategoryOrders, + account => account.category === accountCategory.type); let totalBalance = 0; let hasUnCalculatedAmount = false; @@ -775,7 +778,7 @@ export const useAccountsStore = defineStore('accounts', () => { updateAccountListInvalidState(false); } - const accounts = Account.sortAccounts(Account.ofMulti(data.result)); + const accounts = Account.sortAccounts(Account.ofMulti(data.result), settingsStore.accountCategoryDisplayOrders); if (force && data.result && isEquals(allAccounts.value, accounts)) { reject({ message: 'Account list is up to date', isUpToDate: true }); diff --git a/src/stores/explorer.ts b/src/stores/explorer.ts index bf8ff2b8..3bcb4cf1 100644 --- a/src/stores/explorer.ts +++ b/src/stores/explorer.ts @@ -296,23 +296,25 @@ export const useExplorersStore = defineStore('explorers', () => { }; } else if (dimension === TransactionExplorerDataDimension.SourceAccount) { const primaryAccount = accountsStore.allAccountsMap[transaction.sourceAccount.parentId] ?? transaction.sourceAccount; + const primaryAccountCategoryDisplayOrder: number = settingsStore.accountCategoryDisplayOrders[primaryAccount.category] || Number.MAX_SAFE_INTEGER; return { categoryName: transaction.sourceAccountName || 'Unknown', categoryNameNeedI18n: !transaction.sourceAccountName, categoryId: transaction.sourceAccountId || 'unknown', categoryIdType: TransactionExplorerDimensionType.Account, - categoryDisplayOrders: [primaryAccount.category, primaryAccount.displayOrder, transaction.sourceAccount.displayOrder] + categoryDisplayOrders: [primaryAccountCategoryDisplayOrder, primaryAccount.displayOrder, transaction.sourceAccount.displayOrder] }; } else if (dimension === TransactionExplorerDataDimension.SourceAccountCategory) { const accountCategory = AccountCategory.valueOf(transaction.sourceAccount.category); + const accountCategoryDisplayOrder: number = settingsStore.accountCategoryDisplayOrders[accountCategory?.type ?? 0] || Number.MAX_SAFE_INTEGER; return { categoryName: accountCategory?.name || 'Unknown', categoryNameNeedI18n: true, categoryId: accountCategory?.type.toString(10) || 'unknown', categoryIdType: TransactionExplorerDimensionType.Other, - categoryDisplayOrders: [accountCategory ? accountCategory.type : 0] + categoryDisplayOrders: [accountCategoryDisplayOrder] }; } else if (dimension === TransactionExplorerDataDimension.SourceAccountCurrency) { return { @@ -324,23 +326,25 @@ export const useExplorersStore = defineStore('explorers', () => { }; } else if (dimension === TransactionExplorerDataDimension.DestinationAccount) { const primaryAccount = accountsStore.allAccountsMap[transaction.destinationAccount?.parentId ?? ''] ?? transaction.destinationAccount; + const primaryAccountCategoryDisplayOrder: number = settingsStore.accountCategoryDisplayOrders[primaryAccount?.category || 0] || Number.MAX_SAFE_INTEGER; return { categoryName: transaction.type === TransactionType.Transfer ? (transaction.destinationAccountName || 'Unknown') : 'None', categoryNameNeedI18n: transaction.type !== TransactionType.Transfer || !transaction.destinationAccountName, categoryId: transaction.type === TransactionType.Transfer ? (transaction.destinationAccountId || 'unknown') : 'none', categoryIdType: TransactionExplorerDimensionType.Account, - categoryDisplayOrders: transaction.type === TransactionType.Transfer && primaryAccount && transaction.destinationAccount ? [primaryAccount.category, primaryAccount.displayOrder, transaction.destinationAccount.displayOrder] : [0] + categoryDisplayOrders: transaction.type === TransactionType.Transfer && primaryAccount && transaction.destinationAccount ? [primaryAccountCategoryDisplayOrder, primaryAccount.displayOrder, transaction.destinationAccount.displayOrder] : [0] }; } else if (dimension === TransactionExplorerDataDimension.DestinationAccountCategory) { const accountCategory = transaction.type === TransactionType.Transfer && transaction.destinationAccount ? AccountCategory.valueOf(transaction.destinationAccount.category) : undefined; + const accountCategoryDisplayOrder: number = settingsStore.accountCategoryDisplayOrders[accountCategory?.type ?? 0] || Number.MAX_SAFE_INTEGER; return { categoryName: transaction.type === TransactionType.Transfer ? (accountCategory?.name || 'Unknown') : 'None', categoryNameNeedI18n: true, categoryId: transaction.type === TransactionType.Transfer ? (accountCategory?.name || 'unknown') : 'none', categoryIdType: TransactionExplorerDimensionType.Other, - categoryDisplayOrders: transaction.type === TransactionType.Transfer ? [accountCategory?.type || 0] : [0] + categoryDisplayOrders: transaction.type === TransactionType.Transfer ? [accountCategoryDisplayOrder] : [0] }; } else if (dimension === TransactionExplorerDataDimension.DestinationAccountCurrency) { return { diff --git a/src/stores/setting.ts b/src/stores/setting.ts index 587a62d1..9f4399b5 100644 --- a/src/stores/setting.ts +++ b/src/stores/setting.ts @@ -15,6 +15,9 @@ import { ALL_ALLOWED_CLOUD_SYNC_APP_SETTING_KEY_TYPES } from '@/core/setting.ts'; + +import { AccountCategory } from '@/core/account.ts'; + import { isObject, isString, @@ -41,6 +44,8 @@ export const useSettingsStore = defineStore('settings', () => { const enableApplicationCloudSync = computed(() => getObjectOwnFieldCount(syncedAppSettings.value) > 0); + const accountCategoryDisplayOrders = computed>(() => AccountCategory.allDisplayOrders(appSettings.value.accountCategoryOrders)); + function updateApplicationSettingsValueAndAppSettingsFromCloudSetting(key: string, value: string | number | boolean | Record): void { const keyItems = key.split('.'); const keyFirstPart = keyItems[0] as string; @@ -265,6 +270,12 @@ export const useSettingsStore = defineStore('settings', () => { updateUserApplicationCloudSettingValue('totalAmountExcludeAccountIds', value); } + function setAccountCategoryOrders(value: string): void { + updateApplicationSettingsValue('accountCategoryOrders', value); + appSettings.value.accountCategoryOrders = value; + updateUserApplicationCloudSettingValue('accountCategoryOrders', value); + } + function setHideCategoriesWithoutAccounts(value: boolean): void { updateApplicationSettingsValue('hideCategoriesWithoutAccounts', value); appSettings.value.hideCategoriesWithoutAccounts = value; @@ -459,6 +470,7 @@ export const useSettingsStore = defineStore('settings', () => { localeDefaultSettings, // computed states enableApplicationCloudSync, + accountCategoryDisplayOrders, // functions // -- Basic Settings setTheme, @@ -491,6 +503,7 @@ export const useSettingsStore = defineStore('settings', () => { setShowTagInInsightsExplorerPage, // -- Account List Page setTotalAmountExcludeAccountIds, + setAccountCategoryOrders, setHideCategoriesWithoutAccounts, // -- Exchange Rates Data Page setCurrencySortByInExchangeRatesPage, diff --git a/src/stores/statistics.ts b/src/stores/statistics.ts index 95c3fa2e..cf39f30b 100644 --- a/src/stores/statistics.ts +++ b/src/stores/statistics.ts @@ -284,6 +284,7 @@ export const useStatisticsStore = defineStore('statistics', () => { totalExpense += item.amountInDefaultCurrency; } + const primaryAccountCategoryDisplayOrder = settingsStore.accountCategoryDisplayOrders[item.primaryAccount.category] || Number.MAX_SAFE_INTEGER; const incomeByAccountKey = `${TransactionCategoricalOverviewAnalysisDataItemType.IncomeByAccount}:${item.account.id}`; const expenseByAccountKey = `${TransactionCategoricalOverviewAnalysisDataItemType.ExpenseByAccount}:${item.account.id}`; let incomeByAccountItem: TransactionCategoricalOverviewAnalysisDataItem | undefined = allDataItemsMap[incomeByAccountKey]; @@ -294,7 +295,7 @@ export const useStatisticsStore = defineStore('statistics', () => { item.account.id, item.account.name, TransactionCategoricalOverviewAnalysisDataItemType.IncomeByAccount, - [item.primaryAccount.category, item.primaryAccount.displayOrder, item.account.displayOrder], + [primaryAccountCategoryDisplayOrder, item.primaryAccount.displayOrder, item.account.displayOrder], item.primaryAccount.hidden || item.account.hidden); allDataItemsMap[incomeByAccountKey] = incomeByAccountItem; allIncomeByAccountDataItems.push(incomeByAccountItem); @@ -305,7 +306,7 @@ export const useStatisticsStore = defineStore('statistics', () => { item.account.id, item.account.name, TransactionCategoricalOverviewAnalysisDataItemType.ExpenseByAccount, - [item.primaryAccount.category, item.primaryAccount.displayOrder, item.account.displayOrder], + [primaryAccountCategoryDisplayOrder, item.primaryAccount.displayOrder, item.account.displayOrder], item.primaryAccount.hidden || item.account.hidden); allDataItemsMap[expenseByAccountKey] = expenseByAccountItem; allExpenseByAccountDataItems.push(expenseByAccountItem); @@ -404,11 +405,12 @@ export const useStatisticsStore = defineStore('statistics', () => { let transferToAccountItem: TransactionCategoricalOverviewAnalysisDataItem | undefined = allDataItemsMap[transferToAccountKey]; if (!transferToAccountItem) { + const relatedPrimaryAccountCategoryDisplayOrder = settingsStore.accountCategoryDisplayOrders[item.relatedPrimaryAccount.category] || Number.MAX_SAFE_INTEGER; transferToAccountItem = createNewTransactionCategoricalOverviewAnalysisDataItem( item.relatedAccount.id, item.relatedAccount.name, TransactionCategoricalOverviewAnalysisDataItemType.ExpenseByAccount, - [item.relatedPrimaryAccount.category, item.relatedPrimaryAccount.displayOrder, item.relatedAccount.displayOrder], + [relatedPrimaryAccountCategoryDisplayOrder, item.relatedPrimaryAccount.displayOrder, item.relatedAccount.displayOrder], item.relatedPrimaryAccount.hidden || item.relatedAccount.hidden); allDataItemsMap[transferToAccountKey] = transferToAccountItem; allExpenseByAccountDataItems.push(transferToAccountItem); @@ -543,6 +545,8 @@ export const useStatisticsStore = defineStore('statistics', () => { primaryAccount = account; } + const primaryAccountCategoryDisplayOrder = settingsStore.accountCategoryDisplayOrders[primaryAccount.category] || Number.MAX_SAFE_INTEGER; + let amount = account.balance; if (account.currency !== userStore.currentUserDefaultCurrency) { @@ -566,7 +570,7 @@ export const useStatisticsStore = defineStore('statistics', () => { icon: account.icon || DEFAULT_ACCOUNT_ICON.icon, color: account.color || DEFAULT_ACCOUNT_COLOR, hidden: primaryAccount.hidden || account.hidden, - displayOrders: [primaryAccount.category, primaryAccount.displayOrder, account.displayOrder], + displayOrders: [primaryAccountCategoryDisplayOrder, primaryAccount.displayOrder, account.displayOrder], totalAmount: amount }; @@ -888,6 +892,8 @@ export const useStatisticsStore = defineStore('statistics', () => { if (data) { data.totalAmount += amount; } else { + const primaryAccountCategoryDisplayOrder = settingsStore.accountCategoryDisplayOrders[item.primaryAccount.category] || Number.MAX_SAFE_INTEGER; + data = { name: item.account.name, type: 'account', @@ -895,7 +901,7 @@ export const useStatisticsStore = defineStore('statistics', () => { icon: item.account.icon || DEFAULT_ACCOUNT_ICON.icon, color: item.account.color || DEFAULT_ACCOUNT_COLOR, hidden: item.primaryAccount.hidden || item.account.hidden, - displayOrders: [item.primaryAccount.category, item.primaryAccount.displayOrder, item.account.displayOrder], + displayOrders: [primaryAccountCategoryDisplayOrder, item.primaryAccount.displayOrder, item.account.displayOrder], totalAmount: amount, items: [] }; @@ -1131,6 +1137,8 @@ export const useStatisticsStore = defineStore('statistics', () => { if (data) { data.totalAmount += item.amountInDefaultCurrency; } else { + const primaryAccountCategoryDisplayOrder = settingsStore.accountCategoryDisplayOrders[item.primaryAccount.category] || Number.MAX_SAFE_INTEGER; + data = { name: item.account.name, type: 'account', @@ -1138,7 +1146,7 @@ export const useStatisticsStore = defineStore('statistics', () => { icon: item.account.icon || DEFAULT_ACCOUNT_ICON.icon, color: item.account.color || DEFAULT_ACCOUNT_COLOR, hidden: item.primaryAccount.hidden || item.account.hidden, - displayOrders: [item.primaryAccount.category, item.primaryAccount.displayOrder, item.account.displayOrder], + displayOrders: [primaryAccountCategoryDisplayOrder, item.primaryAccount.displayOrder, item.account.displayOrder], totalAmount: item.amountInDefaultCurrency }; } diff --git a/src/views/base/accounts/AccountEditPageBase.ts b/src/views/base/accounts/AccountEditPageBase.ts index 3c3f1dc3..8ce60cf6 100644 --- a/src/views/base/accounts/AccountEditPageBase.ts +++ b/src/views/base/accounts/AccountEditPageBase.ts @@ -2,6 +2,7 @@ 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 type { TypeAndDisplayName } from '@/core/base.ts'; @@ -25,13 +26,16 @@ export interface DayAndDisplayName { export function useAccountEditPageBase() { const { tt, getAllAccountCategories, getAllAccountTypes, getMonthdayShortName } = useI18n(); + const settingsStore = useSettingsStore(); const userStore = useUserStore(); + const defaultAccountCategory = AccountCategory.values(settingsStore.appSettings.accountCategoryOrders)[0] ?? AccountCategory.Default; + const editAccountId = ref(null); const clientSessionId = ref(''); const loading = ref(false); const submitting = ref(false); - const account = ref(Account.createNewAccount(userStore.currentUserDefaultCurrency, getCurrentUnixTimeForNewAccount())); + const account = ref(Account.createNewAccount(defaultAccountCategory, userStore.currentUserDefaultCurrency, getCurrentUnixTimeForNewAccount())); const subAccounts = ref([]); const title = computed(() => { @@ -72,7 +76,8 @@ export function useAccountEditPageBase() { const inputIsEmpty = computed(() => !!inputEmptyProblemMessage.value); - const allAccountCategories = computed(() => getAllAccountCategories()); + const customAccountCategoryOrder = computed(() => settingsStore.appSettings.accountCategoryOrders); + const allAccountCategories = computed(() => getAllAccountCategories(customAccountCategoryOrder.value)); const allAccountTypes = computed(() => getAllAccountTypes()); const allAvailableMonthDays = computed(() => { @@ -181,6 +186,8 @@ export function useAccountEditPageBase() { }); return { + // constants + defaultAccountCategory, // states editAccountId, clientSessionId, diff --git a/src/views/base/accounts/AccountListPageBase.ts b/src/views/base/accounts/AccountListPageBase.ts index de8285f4..662c3f84 100644 --- a/src/views/base/accounts/AccountListPageBase.ts +++ b/src/views/base/accounts/AccountListPageBase.ts @@ -8,7 +8,7 @@ import { useAccountsStore } from '@/stores/account.ts'; import type { HiddenAmount, NumberWithSuffix } from '@/core/numeral.ts'; import type { WeekDayValue } from '@/core/datetime.ts'; -import { type AccountCategory, AccountType } from '@/core/account.ts'; +import { AccountCategory, AccountType } from '@/core/account.ts'; import type { Account, CategorizedAccount } from '@/models/account.ts'; import { isObject, isNumber, isString } from '@/lib/common.ts'; @@ -29,6 +29,9 @@ export function useAccountListPageBase() { set: (value) => settingsStore.setShowAccountBalance(value) }); + const customAccountCategoryOrder = computed(() => settingsStore.appSettings.accountCategoryOrders); + const defaultAccountCategory = computed(() => AccountCategory.values(customAccountCategoryOrder.value)[0] ?? AccountCategory.Default); + const firstDayOfWeek = computed(() => userStore.currentUserFirstDayOfWeek); const fiscalYearStart = computed(() => userStore.currentUserFiscalYearStart); const defaultCurrency = computed(() => userStore.currentUserDefaultCurrency); @@ -90,6 +93,8 @@ export function useAccountListPageBase() { displayOrderModified, // computed states showAccountBalance, + customAccountCategoryOrder, + defaultAccountCategory, firstDayOfWeek, fiscalYearStart, defaultCurrency, diff --git a/src/views/base/accounts/MoveAllTransactionsPageBase.ts b/src/views/base/accounts/MoveAllTransactionsPageBase.ts index ed55ce63..6372b337 100644 --- a/src/views/base/accounts/MoveAllTransactionsPageBase.ts +++ b/src/views/base/accounts/MoveAllTransactionsPageBase.ts @@ -19,9 +19,10 @@ export function useMoveAllTransactionsPageBase() { const toAccountName = ref(''); const showAccountBalance = computed(() => settingsStore.appSettings.showAccountBalance); + const customAccountCategoryOrder = computed(() => settingsStore.appSettings.accountCategoryOrders); const allAccounts = computed(() => accountsStore.allPlainAccounts); const allVisibleAccounts = computed(() => accountsStore.allVisiblePlainAccounts); - const allVisibleCategorizedAccounts = computed(() => getCategorizedAccountsWithDisplayBalance(allVisibleAccounts.value, showAccountBalance.value)); + const allVisibleCategorizedAccounts = computed(() => getCategorizedAccountsWithDisplayBalance(allVisibleAccounts.value, showAccountBalance.value, customAccountCategoryOrder.value)); const displayToAccountName = computed(() => { if (!toAccountId.value) { diff --git a/src/views/base/settings/AccountCategoryDisplayOrderSettingsPageBase.ts b/src/views/base/settings/AccountCategoryDisplayOrderSettingsPageBase.ts new file mode 100644 index 00000000..ca46f504 --- /dev/null +++ b/src/views/base/settings/AccountCategoryDisplayOrderSettingsPageBase.ts @@ -0,0 +1,63 @@ +import { ref } from 'vue'; + +import { AccountCategory } from '@/core/account.ts'; + +import { useSettingsStore } from '@/stores/setting.ts'; + +export function useAccountCategoryDisplayOrderSettingsPageBase() { + const settingsStore = useSettingsStore(); + + const accountCategories = ref(AccountCategory.values(settingsStore.appSettings.accountCategoryOrders)); + + function isDisplayOrderModified(): boolean { + const currentOrders = AccountCategory.values(settingsStore.appSettings.accountCategoryOrders); + + if (currentOrders.length !== accountCategories.value.length) { + return true; + } + + for (let i = 0; i < currentOrders.length; i++) { + const accountCategory = accountCategories.value[i]; + const currentCategory = currentOrders[i]; + + if (!accountCategory || !currentCategory) { + return true; + } + + if (accountCategory.type !== currentCategory.type) { + return true; + } + } + + return false; + } + + function loadDisplayOrderFromSettings(): void { + accountCategories.value = AccountCategory.values(settingsStore.appSettings.accountCategoryOrders); + } + + function saveDisplayOrderToSettings(): void { + const displayOrders = accountCategories.value.map(category => category.type).join(','); + const defaultOrders = AccountCategory.values('').map(category => category.type).join(','); + + if (displayOrders === defaultOrders) { + settingsStore.setAccountCategoryOrders(''); + } else { + settingsStore.setAccountCategoryOrders(displayOrders); + } + } + + function resetDisplayOrderToDefault(): void { + accountCategories.value = AccountCategory.values(''); + } + + return { + // states + accountCategories, + // functions + isDisplayOrderModified, + loadDisplayOrderFromSettings, + saveDisplayOrderToSettings, + resetDisplayOrderToDefault + }; +} diff --git a/src/views/base/settings/AccountFilterSettingPageBase.ts b/src/views/base/settings/AccountFilterSettingPageBase.ts index 49969ce5..3606ca1d 100644 --- a/src/views/base/settings/AccountFilterSettingPageBase.ts +++ b/src/views/base/settings/AccountFilterSettingPageBase.ts @@ -7,7 +7,7 @@ import { useStatisticsStore } from '@/stores/statistics.ts'; import { useOverviewStore } from '@/stores/overview.ts'; import { keys, keysIfValueEquals, values } from '@/core/base.ts'; -import type {Account, CategorizedAccount} from '@/models/account.ts'; +import type { Account, CategorizedAccount } from '@/models/account.ts'; import { filterCategorizedAccounts, @@ -49,11 +49,12 @@ export function useAccountFilterSettingPageBase(type?: AccountFilterType, select return type === 'statisticsDefault' || type === 'statisticsCurrent' || type === 'homePageOverview' || type === 'transactionListCurrent' || type === 'custom'; }); - const allCategorizedAccounts = computed>(() => filterCategorizedAccounts(accountsStore.allCategorizedAccountsMap, filterContent.value, showHidden.value)); + const customAccountCategoryOrder = computed(() => settingsStore.appSettings.accountCategoryOrders); + const allCategorizedAccounts = computed(() => filterCategorizedAccounts(accountsStore.allCategorizedAccountsMap, customAccountCategoryOrder.value, filterContent.value, showHidden.value)); const allVisibleAccountMap = computed>(() => { const accountMap: Record = {}; - for (const accountCategory of values(allCategorizedAccounts.value)) { + for (const accountCategory of allCategorizedAccounts.value) { for (const account of accountCategory.accounts) { accountMap[account.id] = account; @@ -69,7 +70,7 @@ export function useAccountFilterSettingPageBase(type?: AccountFilterType, select }); const hasAnyAvailableAccount = computed(() => accountsStore.allAvailableAccountsCount > 0); const hasAnyVisibleAccount = computed(() => { - for (const accountCategory of values(allCategorizedAccounts.value)) { + for (const accountCategory of allCategorizedAccounts.value) { if (accountCategory.accounts.length > 0) { return true; } @@ -204,6 +205,7 @@ export function useAccountFilterSettingPageBase(type?: AccountFilterType, select title, applyText, allowHiddenAccount, + customAccountCategoryOrder, allCategorizedAccounts, allVisibleAccountMap, hasAnyAvailableAccount, diff --git a/src/views/base/settings/AppCloudSyncPageBase.ts b/src/views/base/settings/AppCloudSyncPageBase.ts index 5647b375..10df3563 100644 --- a/src/views/base/settings/AppCloudSyncPageBase.ts +++ b/src/views/base/settings/AppCloudSyncPageBase.ts @@ -61,6 +61,7 @@ export const ALL_APPLICATION_CLOUD_SETTINGS: CategorizedApplicationCloudSettingI categoryName: 'Account List Page', items: [ { settingKey: 'totalAmountExcludeAccountIds', settingName: 'Accounts Included in Total', mobile: true, desktop: true }, + { settingKey: 'accountCategoryOrders', settingName: 'Account Category Order', mobile: true, desktop: true }, { settingKey: 'hideCategoriesWithoutAccounts', settingName: 'Hide Categories Without Accounts', mobile: false, desktop: true } ] }, diff --git a/src/views/base/settings/AppSettingsPageBase.ts b/src/views/base/settings/AppSettingsPageBase.ts index ac3a03e1..4fdd9357 100644 --- a/src/views/base/settings/AppSettingsPageBase.ts +++ b/src/views/base/settings/AppSettingsPageBase.ts @@ -134,6 +134,14 @@ export function useAppSettingPageBase() { return getIncludedAccountsDisplayContent(excludeAccountIds, accountsStore.allVisiblePlainAccounts); }); + const accountCategorysDisplayOrderContent = computed(() => { + if (!settingsStore.appSettings.accountCategoryOrders) { + return tt('Default'); + } + + return tt('Custom'); + }); + const transactionCategoriesIncludedInHomePageOverviewDisplayContent = computed(() => { const excludeAccountIds = settingsStore.appSettings.overviewTransactionCategoryFilterInHomePage; return getIncludedTransactionCategoriesDisplayContent(excludeAccountIds); @@ -237,6 +245,7 @@ export function useAppSettingPageBase() { currencySortByInExchangeRatesPage, accountsIncludedInHomePageOverviewDisplayContent, accountsIncludedInTotalDisplayContent, + accountCategorysDisplayOrderContent, transactionCategoriesIncludedInHomePageOverviewDisplayContent }; } diff --git a/src/views/base/transactions/TransactionEditPageBase.ts b/src/views/base/transactions/TransactionEditPageBase.ts index c83ae8ea..ff63041c 100644 --- a/src/views/base/transactions/TransactionEditPageBase.ts +++ b/src/views/base/transactions/TransactionEditPageBase.ts @@ -96,6 +96,7 @@ export function useTransactionEditPageBase(type: TransactionEditPageType, initMo const numeralSystem = computed(() => getCurrentNumeralSystemType()); const currentTimezoneOffsetMinutes = computed(() => getTimezoneOffsetMinutes(transaction.value.time)); const showAccountBalance = computed(() => settingsStore.appSettings.showAccountBalance); + const customAccountCategoryOrder = computed(() => settingsStore.appSettings.accountCategoryOrders); const defaultCurrency = computed(() => userStore.currentUserDefaultCurrency); const defaultAccountId = computed(() => userStore.currentUserDefaultAccountId); const firstDayOfWeek = computed(() => userStore.currentUserFirstDayOfWeek); @@ -105,7 +106,7 @@ export function useTransactionEditPageBase(type: TransactionEditPageType, initMo const allAccounts = computed(() => accountsStore.allPlainAccounts); const allVisibleAccounts = computed(() => accountsStore.allVisiblePlainAccounts); const allAccountsMap = computed>(() => accountsStore.allAccountsMap); - const allVisibleCategorizedAccounts = computed(() => getCategorizedAccountsWithDisplayBalance(allVisibleAccounts.value, showAccountBalance.value)); + const allVisibleCategorizedAccounts = computed(() => getCategorizedAccountsWithDisplayBalance(allVisibleAccounts.value, showAccountBalance.value, customAccountCategoryOrder.value)); const allCategories = computed>(() => transactionCategoriesStore.allTransactionCategories); const allCategoriesMap = computed>(() => transactionCategoriesStore.allTransactionCategoriesMap); const allTags = computed(() => transactionTagsStore.allTransactionTags); diff --git a/src/views/base/users/UserProfilePageBase.ts b/src/views/base/users/UserProfilePageBase.ts index c9ab5b13..46ce37c6 100644 --- a/src/views/base/users/UserProfilePageBase.ts +++ b/src/views/base/users/UserProfilePageBase.ts @@ -68,7 +68,7 @@ export function useUserProfilePageBase() { const allAccounts = computed(() => accountsStore.allPlainAccounts); const allVisibleAccounts = computed(() => accountsStore.allVisiblePlainAccounts); - const allVisibleCategorizedAccounts = computed(() => getCategorizedAccounts(allVisibleAccounts.value)); + const allVisibleCategorizedAccounts = computed(() => getCategorizedAccounts(allVisibleAccounts.value, settingsStore.appSettings.accountCategoryOrders)); const allWeekDays = computed(() => getAllWeekDays()); const allCalendarDisplayTypes = computed(() => getAllCalendarDisplayTypes()); const allDateDisplayTypes = computed(() => getAllDateDisplayTypes()); diff --git a/src/views/desktop/accounts/ListPage.vue b/src/views/desktop/accounts/ListPage.vue index 3fd12de3..d4e73b49 100644 --- a/src/views/desktop/accounts/ListPage.vue +++ b/src/views/desktop/accounts/ListPage.vue @@ -31,7 +31,7 @@