From 9c87436a36ae985901a7543a58834f8a88fb705e Mon Sep 17 00:00:00 2001 From: MaysWind Date: Mon, 20 Apr 2026 01:01:25 +0800 Subject: [PATCH] support batch update categories for transactions in insights explorer --- cmd/webserver.go | 1 + pkg/api/transactions.go | 99 +++++ pkg/models/transaction.go | 6 + pkg/services/transactions.go | 52 +++ src/lib/common.ts | 15 + src/lib/services.ts | 4 + src/locales/de.json | 6 + src/locales/en.json | 6 + src/locales/es.json | 6 + src/locales/fr.json | 6 + src/locales/it.json | 6 + src/locales/ja.json | 6 + src/locales/kn.json | 6 + src/locales/ko.json | 6 + src/locales/nl.json | 6 + src/locales/pt_BR.json | 6 + src/locales/ru.json | 6 + src/locales/sl.json | 6 + src/locales/ta.json | 6 + src/locales/th.json | 6 + src/locales/tr.json | 6 + src/locales/uk.json | 6 + src/locales/vi.json | 6 + src/locales/zh_Hans.json | 6 + src/locales/zh_Hant.json | 6 + src/models/transaction.ts | 5 + src/stores/transaction.ts | 46 +++ .../explorer/ExplorerDataTablePageBase.ts | 208 ++++++++++ src/views/desktop/insights/ExplorerPage.vue | 60 ++- .../dialogs/BatchUpdateCategoryDialog.vue | 193 ++++++++++ .../insights/tabs/ExplorerDataTableTab.vue | 183 +-------- .../tabs/ExplorerEditableDataTableTab.vue | 359 ++++++++++++++++++ 32 files changed, 1166 insertions(+), 179 deletions(-) create mode 100644 src/views/base/explorer/ExplorerDataTablePageBase.ts create mode 100644 src/views/desktop/insights/dialogs/BatchUpdateCategoryDialog.vue create mode 100644 src/views/desktop/insights/tabs/ExplorerEditableDataTableTab.vue diff --git a/cmd/webserver.go b/cmd/webserver.go index 3c6eb9c8..b95249a7 100644 --- a/cmd/webserver.go +++ b/cmd/webserver.go @@ -393,6 +393,7 @@ func startWebServer(c *core.CliContext) error { apiV1Route.GET("/transactions/get.json", bindApi(api.Transactions.TransactionGetHandler)) apiV1Route.POST("/transactions/add.json", bindApi(api.Transactions.TransactionCreateHandler)) apiV1Route.POST("/transactions/modify.json", bindApi(api.Transactions.TransactionModifyHandler)) + apiV1Route.POST("/transactions/batch_update/category.json", bindApi(api.Transactions.TransactionBatchUpdateCategoriesHandler)) apiV1Route.POST("/transactions/move/all.json", bindApi(api.Transactions.TransactionMoveAllBetweenAccountsHandler)) apiV1Route.POST("/transactions/delete.json", bindApi(api.Transactions.TransactionDeleteHandler)) diff --git a/pkg/api/transactions.go b/pkg/api/transactions.go index f4dfc37e..b2251a71 100644 --- a/pkg/api/transactions.go +++ b/pkg/api/transactions.go @@ -1338,6 +1338,105 @@ func (a *TransactionsApi) TransactionModifyHandler(c *core.WebContext) (any, *er return newTransactionResp, nil } +// TransactionBatchUpdateCategoriesHandler batch updates categories of transactions by request parameters for current user +func (a *TransactionsApi) TransactionBatchUpdateCategoriesHandler(c *core.WebContext) (any, *errs.Error) { + var transactionBatchUpdateReq models.TransactionBatchUpdateCategoryRequest + err := c.ShouldBindJSON(&transactionBatchUpdateReq) + + if err != nil { + log.Warnf(c, "[transactions.TransactionBatchUpdateCategoriesHandler] parse request failed, because %s", err.Error()) + return nil, errs.NewIncompleteOrIncorrectSubmissionError(err) + } + + clientTimezone, err := c.GetClientTimezone() + + if err != nil { + log.Warnf(c, "[transactions.TransactionBatchUpdateCategoriesHandler] cannot get client timezone, because %s", err.Error()) + return nil, errs.ErrClientTimezoneOffsetInvalid + } + + transactionIds, err := utils.StringArrayToInt64Array(transactionBatchUpdateReq.TransactionIds) + + if err != nil { + log.Warnf(c, "[transactions.TransactionBatchUpdateCategoriesHandler] parse transaction ids failed, because %s", err.Error()) + return nil, errs.ErrTransactionIdInvalid + } + + uid := c.GetCurrentUid() + user, err := a.users.GetUserById(c, uid) + + if err != nil { + if !errs.IsCustomError(err) { + log.Errorf(c, "[transactions.TransactionBatchUpdateCategoriesHandler] failed to get user, because %s", err.Error()) + } + + return nil, errs.ErrUserNotFound + } + + category, err := a.transactionCategories.GetCategoryByCategoryId(c, uid, transactionBatchUpdateReq.CategoryId) + + if err != nil { + log.Errorf(c, "[transactions.TransactionBatchUpdateCategoriesHandler] failed to get category \"id:%d\" for user \"uid:%d\", because %s", transactionBatchUpdateReq.CategoryId, uid, err.Error()) + return nil, errs.Or(err, errs.ErrOperationFailed) + } + + if category.ParentCategoryId == models.LevelOneTransactionCategoryParentId { + log.Warnf(c, "[transactions.TransactionBatchUpdateCategoriesHandler] transaction category \"id:%d\" is not a sub category", category.CategoryId) + return nil, errs.ErrCannotUsePrimaryCategoryForTransaction + } + + var expectedTransactionType models.TransactionDbType + + if category.Type == models.CATEGORY_TYPE_EXPENSE { + expectedTransactionType = models.TRANSACTION_DB_TYPE_EXPENSE + } else if category.Type == models.CATEGORY_TYPE_INCOME { + expectedTransactionType = models.TRANSACTION_DB_TYPE_INCOME + } else if category.Type == models.CATEGORY_TYPE_TRANSFER { + expectedTransactionType = models.TRANSACTION_DB_TYPE_TRANSFER_OUT + } + + transactions, err := a.transactions.GetTransactionsByTransactionIds(c, uid, transactionIds) + + if err != nil { + log.Errorf(c, "[transactions.TransactionBatchUpdateCategoriesHandler] failed to get transactions for user \"uid:%d\", because %s", uid, err.Error()) + return nil, errs.Or(err, errs.ErrOperationFailed) + } + + allTransactionIds := make([]int64, 0, len(transactions)) + + for i := 0; i < len(transactions); i++ { + transaction := transactions[i] + + if transaction.Type != expectedTransactionType { + log.Warnf(c, "[transactions.TransactionBatchUpdateCategoriesHandler] transaction \"id:%d\" type is not expected type \"%d\" for user \"uid:%d\"", transaction.TransactionId, expectedTransactionType, uid) + return nil, errs.ErrTransactionTypeInvalid + } + + transactionEditable := user.CanEditTransactionByTransactionTime(transaction.TransactionTime, clientTimezone) + + if !transactionEditable { + log.Warnf(c, "[transactions.TransactionBatchUpdateCategoriesHandler] transaction \"id:%d\" is not editable for user \"uid:%d\"", transaction.TransactionId, uid) + return nil, errs.ErrCannotModifyTransactionWithThisTransactionTime + } + + allTransactionIds = append(allTransactionIds, transaction.TransactionId) + + if transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_OUT { + allTransactionIds = append(allTransactionIds, transaction.RelatedId) + } + } + + err = a.transactions.BatchUpdateTransactionsCategory(c, uid, allTransactionIds, category.CategoryId) + + if err != nil { + log.Errorf(c, "[transactions.TransactionBatchUpdateCategoriesHandler] failed to batch update transactions category for user \"uid:%d\", because %s", uid, err.Error()) + return nil, errs.Or(err, errs.ErrOperationFailed) + } + + log.Infof(c, "[transactions.TransactionBatchUpdateCategoriesHandler] user \"uid:%d\" has batch updated category of %d transactions successfully", uid, len(transactionBatchUpdateReq.TransactionIds)) + return true, nil +} + // TransactionMoveAllBetweenAccountsHandler moves all transactions from one account to another account for current user func (a *TransactionsApi) TransactionMoveAllBetweenAccountsHandler(c *core.WebContext) (any, *errs.Error) { var transactionMoveReq models.TransactionMoveBetweenAccountsRequest diff --git a/pkg/models/transaction.go b/pkg/models/transaction.go index 28e1cf85..83d9858d 100644 --- a/pkg/models/transaction.go +++ b/pkg/models/transaction.go @@ -325,6 +325,12 @@ type TransactionGetRequest struct { TrimTag bool `form:"trim_tag"` } +// TransactionBatchUpdateCategoryRequest represents all parameters of transaction batch update category request +type TransactionBatchUpdateCategoryRequest struct { + TransactionIds []string `json:"transactionIds,string" binding:"required"` + CategoryId int64 `json:"categoryId,string" binding:"required"` +} + // TransactionMoveBetweenAccountsRequest represents all parameters of moving all transactions between accounts request type TransactionMoveBetweenAccountsRequest struct { FromAccountId int64 `json:"fromAccountId,string" binding:"required,min=1"` diff --git a/pkg/services/transactions.go b/pkg/services/transactions.go index 1f8b80bc..257ed96b 100644 --- a/pkg/services/transactions.go +++ b/pkg/services/transactions.go @@ -434,6 +434,22 @@ func (s *TransactionService) GetTransactionByTransactionId(c core.Context, uid i return transaction, nil } +// GetTransactionsByTransactionIds returns transaction models according to transaction ids +func (s *TransactionService) GetTransactionsByTransactionIds(c core.Context, uid int64, transactionIds []int64) ([]*models.Transaction, error) { + if uid <= 0 { + return nil, errs.ErrUserIdInvalid + } + + if len(transactionIds) <= 0 { + return nil, errs.ErrTransactionIdInvalid + } + + var transactions []*models.Transaction + err := s.UserDataDB(uid).NewSession(c).Where("uid=? AND deleted=?", uid, false).In("transaction_id", transactionIds).Find(&transactions) + + return transactions, err +} + // GetAllTransactionCount returns total count of transactions func (s *TransactionService) GetAllTransactionCount(c core.Context, uid int64) (int64, error) { return s.GetTransactionCount(c, uid, 0, 0, 0, nil, nil, nil, false, "", "") @@ -1322,6 +1338,42 @@ func (s *TransactionService) ModifyTransaction(c core.Context, transaction *mode return nil } +// BatchUpdateTransactionsCategory batch updates the categories of transactions +func (s *TransactionService) BatchUpdateTransactionsCategory(c core.Context, uid int64, transactionIds []int64, newCategoryId int64) error { + if uid <= 0 { + return errs.ErrUserIdInvalid + } + + if len(transactionIds) < 1 { + return errs.ErrTransactionIdInvalid + } + + if newCategoryId <= 0 { + return errs.ErrTransactionCategoryIdInvalid + } + + uniqueTransactionIds := utils.ToUniqueInt64Slice(transactionIds) + now := time.Now().Unix() + + updateModel := &models.Transaction{ + CategoryId: newCategoryId, + UpdatedUnixTime: now, + } + + return s.UserDataDB(uid).DoTransaction(c, func(sess *xorm.Session) error { + updatedRows, err := sess.Cols("category_id", "updated_unix_time").Where("uid=? AND deleted=?", uid, false).In("transaction_id", uniqueTransactionIds).Update(updateModel) + + if err != nil { + return err + } else if updatedRows < int64(len(uniqueTransactionIds)) { + return errs.ErrTransactionNotFound + } + + return err + }) +} + +// MoveAllTransactionsBetweenAccounts moves all transactions from one account to another account, and combine balance modification transactions if necessary func (s *TransactionService) MoveAllTransactionsBetweenAccounts(c core.Context, uid int64, fromAccountId int64, toAccountId int64) error { if uid <= 0 { return errs.ErrUserIdInvalid diff --git a/src/lib/common.ts b/src/lib/common.ts index 819dba1b..7c1fa170 100644 --- a/src/lib/common.ts +++ b/src/lib/common.ts @@ -191,6 +191,21 @@ export function getObjectOwnFieldCount(object: object): number { return count; } +export function getObjectOwnFieldWithValueCount(object: object, value: unknown): number { + let count = 0; + + if (!object || !isObject(object)) { + return count; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + for (const _ of keysIfValueEquals(object, value)) { + count++; + } + + return count; +} + export function replaceAll(value: string, originalValue: string, targetValue: string): string { // Escape special characters in originalValue to safely use it in a regex pattern. // This ensures that characters like . (dot), * (asterisk), +, ?, etc. are treated literally, diff --git a/src/lib/services.ts b/src/lib/services.ts index 5aa5a8bf..d3469855 100644 --- a/src/lib/services.ts +++ b/src/lib/services.ts @@ -64,6 +64,7 @@ import type { import type { TransactionCreateRequest, TransactionModifyRequest, + TransactionBatchUpdateCategoryRequest, TransactionMoveBetweenAccountsRequest, TransactionDeleteRequest, TransactionImportRequest, @@ -611,6 +612,9 @@ export default { modifyTransaction: (req: TransactionModifyRequest): ApiResponsePromise => { return axios.post>('v1/transactions/modify.json', req); }, + batchUpdateTransactionCategories: (req: TransactionBatchUpdateCategoryRequest): ApiResponsePromise => { + return axios.post>('v1/transactions/batch_update/category.json', req); + }, moveAllTransactionsBetweenAccounts: (req: TransactionMoveBetweenAccountsRequest): ApiResponsePromise => { return axios.post>('v1/transactions/move/all.json', req); }, diff --git a/src/locales/de.json b/src/locales/de.json index 2873b26d..6bb75a9a 100644 --- a/src/locales/de.json +++ b/src/locales/de.json @@ -1757,6 +1757,8 @@ "Save As New Explorer": "Als neuen Explorer speichern", "Restore to Last Saved": "Auf letzten Speicherstand zurücksetzen", "Are you sure you want to restore to last saved state? All unsaved changes will be lost.": "Sind Sie sicher, dass Sie auf den letzten Speicherstand zurücksetzen möchten? Alle nicht gespeicherten Änderungen gehen verloren.", + "Enter Edit Mode": "Enter Edit Mode", + "Exit Edit Mode": "Exit Edit Mode", "Set Explorer Name": "Explorer-Name festlegen", "Rename Explorer": "Explorer umbenennen", "Hide Explorer": "Explorer ausblenden", @@ -1843,6 +1845,10 @@ "Coefficient of Variation": "Variationskoeffizient", "Skewness": "Skewness", "Kurtosis": "Kurtosis", + "Update Categories for Expense Transactions": "Update Categories for Expense Transactions", + "Update Categories for Income Transactions": "Update Categories for Income Transactions", + "Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions", + "Unable to update categories for transactions": "Unable to update categories for transactions", "Account List": "Kontoliste", "This Week": "Diese Woche", "This Month": "Dieser Monat", diff --git a/src/locales/en.json b/src/locales/en.json index 66c20126..99163155 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1757,6 +1757,8 @@ "Save As New Explorer": "Save As New Explorer", "Restore to Last Saved": "Restore to Last Saved", "Are you sure you want to restore to last saved state? All unsaved changes will be lost.": "Are you sure you want to restore to last saved state? All unsaved changes will be lost.", + "Enter Edit Mode": "Enter Edit Mode", + "Exit Edit Mode": "Exit Edit Mode", "Set Explorer Name": "Set Explorer Name", "Rename Explorer": "Rename Explorer", "Hide Explorer": "Hide Explorer", @@ -1843,6 +1845,10 @@ "Coefficient of Variation": "Coefficient of Variation", "Skewness": "Skewness", "Kurtosis": "Kurtosis", + "Update Categories for Expense Transactions": "Update Categories for Expense Transactions", + "Update Categories for Income Transactions": "Update Categories for Income Transactions", + "Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions", + "Unable to update categories for transactions": "Unable to update categories for transactions", "Account List": "Account List", "This Week": "This Week", "This Month": "This Month", diff --git a/src/locales/es.json b/src/locales/es.json index 2ffcd849..96a3194f 100644 --- a/src/locales/es.json +++ b/src/locales/es.json @@ -1757,6 +1757,8 @@ "Save As New Explorer": "Guardar Como Nueva Exploración", "Restore to Last Saved": "Restaurar al Último Guardado", "Are you sure you want to restore to last saved state? All unsaved changes will be lost.": "¿Seguro que quieres restaurar al último estado guardado? Se perderán todos los cambios no guardados.", + "Enter Edit Mode": "Enter Edit Mode", + "Exit Edit Mode": "Exit Edit Mode", "Set Explorer Name": "Asignar Nombre a la Exploración", "Rename Explorer": "Renombrar Exploración", "Hide Explorer": "Ocultar Exploración", @@ -1843,6 +1845,10 @@ "Coefficient of Variation": "Coefficient of Variation", "Skewness": "Skewness", "Kurtosis": "Kurtosis", + "Update Categories for Expense Transactions": "Update Categories for Expense Transactions", + "Update Categories for Income Transactions": "Update Categories for Income Transactions", + "Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions", + "Unable to update categories for transactions": "Unable to update categories for transactions", "Account List": "Lista de Cuentas", "This Week": "Esta Semana", "This Month": "Este Mes", diff --git a/src/locales/fr.json b/src/locales/fr.json index d53d1135..bcefd732 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -1757,6 +1757,8 @@ "Save As New Explorer": "Save As New Explorer", "Restore to Last Saved": "Restore to Last Saved", "Are you sure you want to restore to last saved state? All unsaved changes will be lost.": "Are you sure you want to restore to last saved state? All unsaved changes will be lost.", + "Enter Edit Mode": "Enter Edit Mode", + "Exit Edit Mode": "Exit Edit Mode", "Set Explorer Name": "Set Explorer Name", "Rename Explorer": "Rename Explorer", "Hide Explorer": "Hide Explorer", @@ -1843,6 +1845,10 @@ "Coefficient of Variation": "Coefficient of Variation", "Skewness": "Skewness", "Kurtosis": "Kurtosis", + "Update Categories for Expense Transactions": "Update Categories for Expense Transactions", + "Update Categories for Income Transactions": "Update Categories for Income Transactions", + "Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions", + "Unable to update categories for transactions": "Unable to update categories for transactions", "Account List": "Liste des comptes", "This Week": "Cette semaine", "This Month": "Ce mois", diff --git a/src/locales/it.json b/src/locales/it.json index 82021784..9013eb8a 100644 --- a/src/locales/it.json +++ b/src/locales/it.json @@ -1757,6 +1757,8 @@ "Save As New Explorer": "Save As New Explorer", "Restore to Last Saved": "Restore to Last Saved", "Are you sure you want to restore to last saved state? All unsaved changes will be lost.": "Are you sure you want to restore to last saved state? All unsaved changes will be lost.", + "Enter Edit Mode": "Enter Edit Mode", + "Exit Edit Mode": "Exit Edit Mode", "Set Explorer Name": "Set Explorer Name", "Rename Explorer": "Rename Explorer", "Hide Explorer": "Hide Explorer", @@ -1843,6 +1845,10 @@ "Coefficient of Variation": "Coefficient of Variation", "Skewness": "Skewness", "Kurtosis": "Kurtosis", + "Update Categories for Expense Transactions": "Update Categories for Expense Transactions", + "Update Categories for Income Transactions": "Update Categories for Income Transactions", + "Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions", + "Unable to update categories for transactions": "Unable to update categories for transactions", "Account List": "Elenco account", "This Week": "Questa settimana", "This Month": "Questo mese", diff --git a/src/locales/ja.json b/src/locales/ja.json index 4a4a853f..620537e3 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -1757,6 +1757,8 @@ "Save As New Explorer": "Save As New Explorer", "Restore to Last Saved": "Restore to Last Saved", "Are you sure you want to restore to last saved state? All unsaved changes will be lost.": "Are you sure you want to restore to last saved state? All unsaved changes will be lost.", + "Enter Edit Mode": "Enter Edit Mode", + "Exit Edit Mode": "Exit Edit Mode", "Set Explorer Name": "Set Explorer Name", "Rename Explorer": "Rename Explorer", "Hide Explorer": "Hide Explorer", @@ -1843,6 +1845,10 @@ "Coefficient of Variation": "Coefficient of Variation", "Skewness": "Skewness", "Kurtosis": "Kurtosis", + "Update Categories for Expense Transactions": "Update Categories for Expense Transactions", + "Update Categories for Income Transactions": "Update Categories for Income Transactions", + "Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions", + "Unable to update categories for transactions": "Unable to update categories for transactions", "Account List": "口座リスト", "This Week": "今週", "This Month": "今月", diff --git a/src/locales/kn.json b/src/locales/kn.json index 50c1ecbc..08656fdd 100644 --- a/src/locales/kn.json +++ b/src/locales/kn.json @@ -1757,6 +1757,8 @@ "Save As New Explorer": "Save As New Explorer", "Restore to Last Saved": "Restore to Last Saved", "Are you sure you want to restore to last saved state? All unsaved changes will be lost.": "Are you sure you want to restore to last saved state? All unsaved changes will be lost.", + "Enter Edit Mode": "Enter Edit Mode", + "Exit Edit Mode": "Exit Edit Mode", "Set Explorer Name": "Set Explorer Name", "Rename Explorer": "Rename Explorer", "Hide Explorer": "Hide Explorer", @@ -1843,6 +1845,10 @@ "Coefficient of Variation": "Coefficient of Variation", "Skewness": "Skewness", "Kurtosis": "Kurtosis", + "Update Categories for Expense Transactions": "Update Categories for Expense Transactions", + "Update Categories for Income Transactions": "Update Categories for Income Transactions", + "Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions", + "Unable to update categories for transactions": "Unable to update categories for transactions", "Account List": "ಖಾತೆಗಳ ಪಟ್ಟಿ", "This Week": "ಈ ವಾರ", "This Month": "ಈ ತಿಂಗಳು", diff --git a/src/locales/ko.json b/src/locales/ko.json index f1940d49..dc1a2205 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -1757,6 +1757,8 @@ "Save As New Explorer": "새 탐색기로 저장", "Restore to Last Saved": "마지막 저장 상태로 복원", "Are you sure you want to restore to last saved state? All unsaved changes will be lost.": "마지막 저장 상태로 복원하시겠습니까? 저장되지 않은 모든 변경 사항이 손실됩니다.", + "Enter Edit Mode": "Enter Edit Mode", + "Exit Edit Mode": "Exit Edit Mode", "Set Explorer Name": "탐색기 이름 설정", "Rename Explorer": "탐색기 이름 바꾸기", "Hide Explorer": "탐색기 숨기기", @@ -1843,6 +1845,10 @@ "Coefficient of Variation": "Coefficient of Variation", "Skewness": "Skewness", "Kurtosis": "Kurtosis", + "Update Categories for Expense Transactions": "Update Categories for Expense Transactions", + "Update Categories for Income Transactions": "Update Categories for Income Transactions", + "Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions", + "Unable to update categories for transactions": "Unable to update categories for transactions", "Account List": "계좌 목록", "This Week": "이번 주", "This Month": "이번 달", diff --git a/src/locales/nl.json b/src/locales/nl.json index d3043e62..c6f9eeed 100644 --- a/src/locales/nl.json +++ b/src/locales/nl.json @@ -1757,6 +1757,8 @@ "Save As New Explorer": "Save As New Explorer", "Restore to Last Saved": "Restore to Last Saved", "Are you sure you want to restore to last saved state? All unsaved changes will be lost.": "Are you sure you want to restore to last saved state? All unsaved changes will be lost.", + "Enter Edit Mode": "Enter Edit Mode", + "Exit Edit Mode": "Exit Edit Mode", "Set Explorer Name": "Set Explorer Name", "Rename Explorer": "Rename Explorer", "Hide Explorer": "Hide Explorer", @@ -1843,6 +1845,10 @@ "Coefficient of Variation": "Coefficient of Variation", "Skewness": "Skewness", "Kurtosis": "Kurtosis", + "Update Categories for Expense Transactions": "Update Categories for Expense Transactions", + "Update Categories for Income Transactions": "Update Categories for Income Transactions", + "Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions", + "Unable to update categories for transactions": "Unable to update categories for transactions", "Account List": "Rekeningenlijst", "This Week": "Deze week", "This Month": "Deze maand", diff --git a/src/locales/pt_BR.json b/src/locales/pt_BR.json index 73fb9d9e..a9ce0d7f 100644 --- a/src/locales/pt_BR.json +++ b/src/locales/pt_BR.json @@ -1757,6 +1757,8 @@ "Save As New Explorer": "Salvar como Novo Explorador", "Restore to Last Saved": "Restaurar para o Último Salvo", "Are you sure you want to restore to last saved state? All unsaved changes will be lost.": "Tem certeza de que deseja restaurar para o último estado salvo? Todas as alterações não salvas serão perdidas.", + "Enter Edit Mode": "Enter Edit Mode", + "Exit Edit Mode": "Exit Edit Mode", "Set Explorer Name": "Definir Nome do Explorador", "Rename Explorer": "Renomear Explorador", "Hide Explorer": "Ocultar Explorador", @@ -1843,6 +1845,10 @@ "Coefficient of Variation": "Coeficiente de Variação", "Skewness": "Skewness", "Kurtosis": "Kurtosis", + "Update Categories for Expense Transactions": "Update Categories for Expense Transactions", + "Update Categories for Income Transactions": "Update Categories for Income Transactions", + "Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions", + "Unable to update categories for transactions": "Unable to update categories for transactions", "Account List": "Lista de Contas", "This Week": "Esta Semana", "This Month": "Este Mês", diff --git a/src/locales/ru.json b/src/locales/ru.json index 33f778af..a821da1d 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -1757,6 +1757,8 @@ "Save As New Explorer": "Сохранить как новое исследование", "Restore to Last Saved": "Восстановить с последнего сохранения", "Are you sure you want to restore to last saved state? All unsaved changes will be lost.": "Вы действительно хотите восстановить из последнего сохранения? Вы несохранённые изминения будут потеряны.", + "Enter Edit Mode": "Enter Edit Mode", + "Exit Edit Mode": "Exit Edit Mode", "Set Explorer Name": "Установить название исследования", "Rename Explorer": "Переименовать исследование", "Hide Explorer": "Скрыть исследование", @@ -1843,6 +1845,10 @@ "Coefficient of Variation": "Coefficient of Variation", "Skewness": "Skewness", "Kurtosis": "Kurtosis", + "Update Categories for Expense Transactions": "Update Categories for Expense Transactions", + "Update Categories for Income Transactions": "Update Categories for Income Transactions", + "Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions", + "Unable to update categories for transactions": "Unable to update categories for transactions", "Account List": "Список счетов", "This Week": "На этой неделе", "This Month": "В этом месяце", diff --git a/src/locales/sl.json b/src/locales/sl.json index 386c471f..fcd9a979 100644 --- a/src/locales/sl.json +++ b/src/locales/sl.json @@ -1757,6 +1757,8 @@ "Save As New Explorer": "Shrani kot novo raziskovanje", "Restore to Last Saved": "Povrni na zadnje shranjeno stanje", "Are you sure you want to restore to last saved state? All unsaved changes will be lost.": "Ali ste prepričani, da želite povrniti na zadnje shranjeno stanje? Vse neshranjene spremembe bodo izgubljene.", + "Enter Edit Mode": "Enter Edit Mode", + "Exit Edit Mode": "Exit Edit Mode", "Set Explorer Name": "Nastavi ime raziskovanja", "Rename Explorer": "Preimenuj raziskovanje", "Hide Explorer": "Skrij raziskovanje", @@ -1843,6 +1845,10 @@ "Coefficient of Variation": "Coefficient of Variation", "Skewness": "Skewness", "Kurtosis": "Kurtosis", + "Update Categories for Expense Transactions": "Update Categories for Expense Transactions", + "Update Categories for Income Transactions": "Update Categories for Income Transactions", + "Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions", + "Unable to update categories for transactions": "Unable to update categories for transactions", "Account List": "Seznam računov", "This Week": "Ta teden", "This Month": "Ta mesec", diff --git a/src/locales/ta.json b/src/locales/ta.json index 112ccf2b..00ca8cd2 100644 --- a/src/locales/ta.json +++ b/src/locales/ta.json @@ -1757,6 +1757,8 @@ "Save As New Explorer": "புதிய ஆய்வுக்கருவியாக சேமி", "Restore to Last Saved": "கடைசி சேமிப்புக்கு மீட்டமை", "Are you sure you want to restore to last saved state? All unsaved changes will be lost.": "கடைசி சேமிக்கப்பட்ட நிலைக்கு மீட்டமைக்க விரும்புகிறீர்களா? சேமிக்கப்படாத அனைத்து மாற்றங்களும் இழக்கப்படும்.", + "Enter Edit Mode": "Enter Edit Mode", + "Exit Edit Mode": "Exit Edit Mode", "Set Explorer Name": "ஆய்வுக்கருவி பெயர் அமை", "Rename Explorer": "ஆய்வுக்கருவி மறுபெயரிடு", "Hide Explorer": "ஆய்வுக்கருவி மறை", @@ -1843,6 +1845,10 @@ "Coefficient of Variation": "Coefficient of Variation", "Skewness": "Skewness", "Kurtosis": "Kurtosis", + "Update Categories for Expense Transactions": "Update Categories for Expense Transactions", + "Update Categories for Income Transactions": "Update Categories for Income Transactions", + "Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions", + "Unable to update categories for transactions": "Unable to update categories for transactions", "Account List": "கணக்குகளின் பட்டியல்", "This Week": "இந்த வாரம்", "This Month": "இந்த மாதம்", diff --git a/src/locales/th.json b/src/locales/th.json index af665cc3..f11963bd 100644 --- a/src/locales/th.json +++ b/src/locales/th.json @@ -1757,6 +1757,8 @@ "Save As New Explorer": "Save As New Explorer", "Restore to Last Saved": "Restore to Last Saved", "Are you sure you want to restore to last saved state? All unsaved changes will be lost.": "Are you sure you want to restore to last saved state? All unsaved changes will be lost.", + "Enter Edit Mode": "Enter Edit Mode", + "Exit Edit Mode": "Exit Edit Mode", "Set Explorer Name": "Set Explorer Name", "Rename Explorer": "Rename Explorer", "Hide Explorer": "Hide Explorer", @@ -1843,6 +1845,10 @@ "Coefficient of Variation": "Coefficient of Variation", "Skewness": "Skewness", "Kurtosis": "Kurtosis", + "Update Categories for Expense Transactions": "Update Categories for Expense Transactions", + "Update Categories for Income Transactions": "Update Categories for Income Transactions", + "Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions", + "Unable to update categories for transactions": "Unable to update categories for transactions", "Account List": "รายการบัญชี", "This Week": "สัปดาห์นี้", "This Month": "เดือนนี้", diff --git a/src/locales/tr.json b/src/locales/tr.json index e3d1c41d..ec5a7d24 100644 --- a/src/locales/tr.json +++ b/src/locales/tr.json @@ -1757,6 +1757,8 @@ "Save As New Explorer": "Save As New Explorer", "Restore to Last Saved": "Restore to Last Saved", "Are you sure you want to restore to last saved state? All unsaved changes will be lost.": "Are you sure you want to restore to last saved state? All unsaved changes will be lost.", + "Enter Edit Mode": "Enter Edit Mode", + "Exit Edit Mode": "Exit Edit Mode", "Set Explorer Name": "Set Explorer Name", "Rename Explorer": "Rename Explorer", "Hide Explorer": "Hide Explorer", @@ -1843,6 +1845,10 @@ "Coefficient of Variation": "Coefficient of Variation", "Skewness": "Skewness", "Kurtosis": "Kurtosis", + "Update Categories for Expense Transactions": "Update Categories for Expense Transactions", + "Update Categories for Income Transactions": "Update Categories for Income Transactions", + "Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions", + "Unable to update categories for transactions": "Unable to update categories for transactions", "Account List": "Hesap Listesi", "This Week": "Bu Hafta", "This Month": "Bu Ay", diff --git a/src/locales/uk.json b/src/locales/uk.json index e5ffa50e..e35ad1ef 100644 --- a/src/locales/uk.json +++ b/src/locales/uk.json @@ -1757,6 +1757,8 @@ "Save As New Explorer": "Save As New Explorer", "Restore to Last Saved": "Restore to Last Saved", "Are you sure you want to restore to last saved state? All unsaved changes will be lost.": "Are you sure you want to restore to last saved state? All unsaved changes will be lost.", + "Enter Edit Mode": "Enter Edit Mode", + "Exit Edit Mode": "Exit Edit Mode", "Set Explorer Name": "Set Explorer Name", "Rename Explorer": "Rename Explorer", "Hide Explorer": "Hide Explorer", @@ -1843,6 +1845,10 @@ "Coefficient of Variation": "Coefficient of Variation", "Skewness": "Skewness", "Kurtosis": "Kurtosis", + "Update Categories for Expense Transactions": "Update Categories for Expense Transactions", + "Update Categories for Income Transactions": "Update Categories for Income Transactions", + "Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions", + "Unable to update categories for transactions": "Unable to update categories for transactions", "Account List": "Список рахунків", "This Week": "Цього тижня", "This Month": "Цього місяця", diff --git a/src/locales/vi.json b/src/locales/vi.json index a1ba1645..5882b7e7 100644 --- a/src/locales/vi.json +++ b/src/locales/vi.json @@ -1757,6 +1757,8 @@ "Save As New Explorer": "Save As New Explorer", "Restore to Last Saved": "Restore to Last Saved", "Are you sure you want to restore to last saved state? All unsaved changes will be lost.": "Are you sure you want to restore to last saved state? All unsaved changes will be lost.", + "Enter Edit Mode": "Enter Edit Mode", + "Exit Edit Mode": "Exit Edit Mode", "Set Explorer Name": "Set Explorer Name", "Rename Explorer": "Rename Explorer", "Hide Explorer": "Hide Explorer", @@ -1843,6 +1845,10 @@ "Coefficient of Variation": "Coefficient of Variation", "Skewness": "Skewness", "Kurtosis": "Kurtosis", + "Update Categories for Expense Transactions": "Update Categories for Expense Transactions", + "Update Categories for Income Transactions": "Update Categories for Income Transactions", + "Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions", + "Unable to update categories for transactions": "Unable to update categories for transactions", "Account List": "Danh sách tài khoản", "This Week": "Tuần này", "This Month": "Tháng này", diff --git a/src/locales/zh_Hans.json b/src/locales/zh_Hans.json index d01d8806..e59bba5a 100644 --- a/src/locales/zh_Hans.json +++ b/src/locales/zh_Hans.json @@ -1757,6 +1757,8 @@ "Save As New Explorer": "另存为新的探索", "Restore to Last Saved": "恢复到上次保存", "Are you sure you want to restore to last saved state? All unsaved changes will be lost.": "您确定要恢复到上次保存的状态吗?所有未保存的更改将会丢失。", + "Enter Edit Mode": "进入编辑模式", + "Exit Edit Mode": "退出编辑模式", "Set Explorer Name": "设置探索名称", "Rename Explorer": "重命名探索", "Hide Explorer": "隐藏探索", @@ -1843,6 +1845,10 @@ "Coefficient of Variation": "变异系数", "Skewness": "偏度", "Kurtosis": "峰度", + "Update Categories for Expense Transactions": "更新支出交易的分类", + "Update Categories for Income Transactions": "更新收入交易的分类", + "Update Categories for Transfer Transactions": "更新转账交易的分类", + "Unable to update categories for transactions": "无法更新交易的分类", "Account List": "账户列表", "This Week": "本周", "This Month": "本月", diff --git a/src/locales/zh_Hant.json b/src/locales/zh_Hant.json index e49f5e1f..6af48dee 100644 --- a/src/locales/zh_Hant.json +++ b/src/locales/zh_Hant.json @@ -1757,6 +1757,8 @@ "Save As New Explorer": "另存新探索", "Restore to Last Saved": "還原到上次儲存", "Are you sure you want to restore to last saved state? All unsaved changes will be lost.": "您確定要還原到上次儲存的狀態嗎?所有未儲存的更改將會遺失。", + "Enter Edit Mode": "進入編輯模式", + "Exit Edit Mode": "退出編輯模式", "Set Explorer Name": "設定探索名稱", "Rename Explorer": "重新命名探索", "Hide Explorer": "隱藏探索", @@ -1843,6 +1845,10 @@ "Coefficient of Variation": "變異係數", "Skewness": "偏度", "Kurtosis": "峰度", + "Update Categories for Expense Transactions": "更新支出交易的分類", + "Update Categories for Income Transactions": "更新收入交易的分類", + "Update Categories for Transfer Transactions": "更新轉帳交易的分類", + "Unable to update categories for transactions": "無法更新交易的分類", "Account List": "帳戶清單", "This Week": "本週", "This Month": "本月", diff --git a/src/models/transaction.ts b/src/models/transaction.ts index 28455276..97fd0f90 100644 --- a/src/models/transaction.ts +++ b/src/models/transaction.ts @@ -558,6 +558,11 @@ export interface TransactionModifyRequest { readonly geoLocation?: TransactionGeoLocationRequest; } +export interface TransactionBatchUpdateCategoryRequest { + readonly transactionIds: string[]; + readonly categoryId: string; +} + export interface TransactionMoveBetweenAccountsRequest { readonly fromAccountId: string; readonly toAccountId: string; diff --git a/src/stores/transaction.ts b/src/stores/transaction.ts index 5cf2b338..57ff7673 100644 --- a/src/stores/transaction.ts +++ b/src/stores/transaction.ts @@ -1117,6 +1117,51 @@ export const useTransactionsStore = defineStore('transactions', () => { }); } + function batchUpdateTransactionCategories({ transactionIds, categoryId }: { transactionIds: string[], categoryId: string }): Promise { + return new Promise((resolve, reject) => { + services.batchUpdateTransactionCategories({ transactionIds, categoryId }).then(response => { + const data = response.data; + + if (!data || !data.success || !data.result) { + reject({ message: 'Unable to update categories for transactions' }); + return; + } + + if (!transactionListStateInvalid.value) { + updateTransactionListInvalidState(true); + } + + if (!transactionReconciliationStatementStateInvalid.value) { + updateTransactionReconciliationStatementInvalidState(true); + } + + if (!overviewStore.transactionOverviewStateInvalid) { + overviewStore.updateTransactionOverviewInvalidState(true); + } + + if (!statisticsStore.transactionStatisticsStateInvalid) { + statisticsStore.updateTransactionStatisticsInvalidState(true); + } + + if (!explorersStore.transactionExplorerStateInvalid) { + explorersStore.updateTransactionExplorerInvalidState(true); + } + + resolve(data.result); + }).catch(error => { + logger.error('failed to update categories for transactions', error); + + if (error.response && error.response.data && error.response.data.errorMessage) { + reject({ error: error.response.data }); + } else if (!error.processed) { + reject({ message: 'Unable to update categories for transactions' }); + } else { + reject(error); + } + }); + }); + } + function moveAllTransactionsBetweenAccounts({ fromAccountId, toAccountId }: { fromAccountId: string, toAccountId: string }): Promise { return new Promise((resolve, reject) => { services.moveAllTransactionsBetweenAccounts({ fromAccountId, toAccountId }).then(response => { @@ -1472,6 +1517,7 @@ export const useTransactionsStore = defineStore('transactions', () => { getReconciliationStatements, getTransaction, saveTransaction, + batchUpdateTransactionCategories, moveAllTransactionsBetweenAccounts, deleteTransaction, recognizeReceiptImage, diff --git a/src/views/base/explorer/ExplorerDataTablePageBase.ts b/src/views/base/explorer/ExplorerDataTablePageBase.ts new file mode 100644 index 00000000..443f2faa --- /dev/null +++ b/src/views/base/explorer/ExplorerDataTablePageBase.ts @@ -0,0 +1,208 @@ +import { ref, computed } from 'vue'; + +import { useI18n } from '@/locales/helpers.ts'; + +import { useSettingsStore } from '@/stores/setting.ts'; +import { useUserStore } from '@/stores/user.ts'; +import { useExplorersStore } from '@/stores/explorer.ts'; + +import { type NameValue, type NameNumeralValue, itemAndIndex } from '@/core/base.ts'; +import type { NumeralSystem } from '@/core/numeral.ts'; + +import { TransactionType } from '@/core/transaction.ts'; + +import type { TransactionInsightDataItem } from '@/models/transaction.ts'; +import type { InsightsExplorer} from '@/models/explorer.ts'; + +import { + getUtcOffsetByUtcOffsetMinutes, + getTimezoneOffsetMinutes, + parseDateTimeFromUnixTimeWithTimezoneOffset +} from '@/lib/datetime.ts'; + +export function useExplorerDataTablePageBase() { + const { + tt, + getCurrentNumeralSystemType, + formatDateTimeToLongDateTime, + formatAmountToLocalizedNumeralsWithCurrency + } = useI18n(); + + const settingsStore = useSettingsStore(); + const userStore = useUserStore(); + const explorersStore = useExplorersStore(); + + const currentPage = ref(1); + + const numeralSystem = computed(() => getCurrentNumeralSystemType()); + const defaultCurrency = computed(() => userStore.currentUserDefaultCurrency); + + const currentExplorer = computed(() => explorersStore.currentInsightsExplorer); + + const filteredTransactions = computed(() => explorersStore.filteredTransactionsInDataTable); + + const allDataTableQuerySources = computed(() => { + const sources: NameValue[] = []; + + sources.push({ + name: tt('All Queries'), + value: '' + }); + + for (const [query, index] of itemAndIndex(currentExplorer.value.queries)) { + if (query.name) { + sources.push({ + name: query.name, + value: query.id + }); + } else { + sources.push({ + name: tt('format.misc.queryIndex', { index: index + 1 }), + value: query.id + }); + } + } + + return sources; + }); + + const allPageCounts = computed(() => { + const pageCounts: NameNumeralValue[] = []; + const availableCountPerPage: number[] = [ 5, 10, 15, 20, 25, 30, 50 ]; + + for (const count of availableCountPerPage) { + pageCounts.push({ value: count, name: numeralSystem.value.formatNumber(count) }); + } + + pageCounts.push({ value: -1, name: tt('All') }); + + return pageCounts; + }); + + const skeletonData = computed(() => { + const data: number[] = []; + + for (let i = 0; i < currentExplorer.value.countPerPage; i++) { + data.push(i); + } + + return data; + }); + + const totalPageCount = computed(() => { + if (!filteredTransactions.value || filteredTransactions.value.length < 1) { + return 1; + } + + const count = filteredTransactions.value.length; + return Math.ceil(count / currentExplorer.value.countPerPage); + }); + + const dataTableHeaders = computed(() => { + const headers: object[] = []; + + headers.push({ key: 'time', value: 'time', title: tt('Transaction Time'), sortable: true, nowrap: true }); + headers.push({ key: 'type', value: 'type', title: tt('Type'), sortable: true, nowrap: true }); + headers.push({ key: 'secondaryCategoryName', value: 'secondaryCategoryName', title: tt('Category'), sortable: true, nowrap: true }); + headers.push({ key: 'sourceAmount', value: 'sourceAmount', title: tt('Amount'), sortable: true, nowrap: true }); + headers.push({ key: 'sourceAccountName', value: 'sourceAccountName', title: tt('Account'), sortable: true, nowrap: true }); + + if (settingsStore.appSettings.showTagInInsightsExplorerPage) { + headers.push({ key: 'tags', value: 'tags', title: tt('Tags'), sortable: true, nowrap: true }); + } + + headers.push({ key: 'comment', value: 'comment', title: tt('Description'), sortable: true, nowrap: true }); + headers.push({ key: 'operation', title: tt('Operation'), sortable: false, nowrap: true, align: 'center' }); + return headers; + }); + + function getDisplayDateTime(transaction: TransactionInsightDataItem): string { + const dateTime = parseDateTimeFromUnixTimeWithTimezoneOffset(transaction.time, transaction.utcOffset); + return formatDateTimeToLongDateTime(dateTime); + } + + function isSameAsDefaultTimezoneOffsetMinutes(transaction: TransactionInsightDataItem): boolean { + return transaction.utcOffset === getTimezoneOffsetMinutes(transaction.time); + } + + function getDisplayTimezone(transaction: TransactionInsightDataItem): string { + return `UTC${getUtcOffsetByUtcOffsetMinutes(transaction.utcOffset)}`; + } + + function getDisplayTimeInDefaultTimezone(transaction: TransactionInsightDataItem): string { + const timezoneOffsetMinutes = getTimezoneOffsetMinutes(transaction.time); + const dateTime = parseDateTimeFromUnixTimeWithTimezoneOffset(transaction.time, timezoneOffsetMinutes); + const utcOffset = numeralSystem.value.replaceWesternArabicDigitsToLocalizedDigits(getUtcOffsetByUtcOffsetMinutes(timezoneOffsetMinutes)); + return `${formatDateTimeToLongDateTime(dateTime)} (UTC${utcOffset})`; + } + + function getDisplayTransactionType(transaction: TransactionInsightDataItem): string { + if (transaction.type === TransactionType.ModifyBalance) { + return tt('Modify Balance'); + } else if (transaction.type === TransactionType.Income) { + return tt('Income'); + } else if (transaction.type === TransactionType.Expense) { + return tt('Expense'); + } else if (transaction.type === TransactionType.Transfer) { + return tt('Transfer'); + } else { + return tt('Unknown'); + } + } + + function getTransactionTypeColor(transaction: TransactionInsightDataItem): string | undefined { + if (transaction.type === TransactionType.ModifyBalance) { + return 'secondary'; + } else if (transaction.type === TransactionType.Income) { + return undefined; + } else if (transaction.type === TransactionType.Expense) { + return undefined; + } else if (transaction.type === TransactionType.Transfer) { + return 'primary'; + } else { + return 'default'; + } + } + + function getDisplaySourceAmount(transaction: TransactionInsightDataItem): string { + let currency = defaultCurrency.value; + + if (transaction.sourceAccount) { + currency = transaction.sourceAccount.currency; + } + + return formatAmountToLocalizedNumeralsWithCurrency(transaction.sourceAmount, currency); + } + + function getDisplayDestinationAmount(transaction: TransactionInsightDataItem): string { + let currency = defaultCurrency.value; + + if (transaction.destinationAccount) { + currency = transaction.destinationAccount.currency; + } + + return formatAmountToLocalizedNumeralsWithCurrency(transaction.destinationAmount, currency); + } + + return { + // states + currentPage, + // computed states + currentExplorer, + filteredTransactions, + allDataTableQuerySources, + allPageCounts, + skeletonData, + totalPageCount, + dataTableHeaders, + // functions + getDisplayDateTime, + isSameAsDefaultTimezoneOffsetMinutes, + getDisplayTimezone, + getDisplayTimeInDefaultTimezone, + getDisplayTransactionType, + getTransactionTypeColor, + getDisplaySourceAmount, + getDisplayDestinationAmount + }; +} diff --git a/src/views/desktop/insights/ExplorerPage.vue b/src/views/desktop/insights/ExplorerPage.vue index 5520b664..7df2b4d8 100644 --- a/src/views/desktop/insights/ExplorerPage.vue +++ b/src/views/desktop/insights/ExplorerPage.vue @@ -5,7 +5,7 @@
- +
{{ tt('New Explorer') }} {{ explorer.name || tt('Untitled Explorer') }} @@ -41,11 +41,11 @@ {{ tt('Insights Explorer') }} @@ -68,7 +68,7 @@ @@ -84,7 +84,7 @@ + :disabled="loading || updating || isCurrentDataTableEditable" @click="saveExplorer(false)"> {{ tt('Save Explorer') }} @@ -113,29 +113,41 @@ v-for="timezoneType in allTimezoneTypesUsedForDateRange" @click="currentExplorer.timezoneUsedForDateRange = timezoneType.type"> + + + - - + v-if="(activeTab === 'table' || activeTab === 'chart') && !isCurrentDataTableEditable"> + + {{ tt('Rename Explorer') }} - + {{ tt('Hide Explorer') }} - + {{ tt('Unhide Explorer') }} - + {{ tt('Delete Explorer') }} - + + @click="showChangeExplorerDisplayOrderDialog" + v-if="!isCurrentDataTableEditable"> @@ -149,7 +161,13 @@ + @click:transaction="onShowTransaction" + v-if="!isCurrentDataTableEditable" /> + (true); const updating = ref(false); const clientSessionId = ref(''); const isCurrentExplorerModified = ref(false); +const isCurrentDataTableEditable = ref(false); const alwaysShowNav = ref(display.mdAndUp.value); const showNav = ref(display.mdAndUp.value); const activeTab = ref('query'); @@ -726,6 +748,10 @@ function onShowTransaction(transaction: TransactionInsightDataItem): void { }); } +function onUpdateTransactions(): void { + reload(false); +} + function onShowDateRangeError(message: string): void { snackbar.value?.showError(message); } diff --git a/src/views/desktop/insights/dialogs/BatchUpdateCategoryDialog.vue b/src/views/desktop/insights/dialogs/BatchUpdateCategoryDialog.vue new file mode 100644 index 00000000..483255f1 --- /dev/null +++ b/src/views/desktop/insights/dialogs/BatchUpdateCategoryDialog.vue @@ -0,0 +1,193 @@ + + + diff --git a/src/views/desktop/insights/tabs/ExplorerDataTableTab.vue b/src/views/desktop/insights/tabs/ExplorerDataTableTab.vue index 69818e84..4d3ec927 100644 --- a/src/views/desktop/insights/tabs/ExplorerDataTableTab.vue +++ b/src/views/desktop/insights/tabs/ExplorerDataTableTab.vue @@ -207,26 +207,20 @@ + +