diff --git a/internal/handler/transaction.go b/internal/handler/transaction.go index ff130da..45f3d54 100644 --- a/internal/handler/transaction.go +++ b/internal/handler/transaction.go @@ -58,6 +58,7 @@ func (h TransactionImpl) handleTransactionPage() http.HandlerFunc { AccountId: r.URL.Query().Get("account-id"), TreasureChestId: r.URL.Query().Get("treasure-chest-id"), Error: r.URL.Query().Get("error"), + Page: r.URL.Query().Get("page"), } transactions, err := h.s.GetAll(r.Context(), user, filter) diff --git a/internal/service/transaction.go b/internal/service/transaction.go index 5a0ad94..582e8a2 100644 --- a/internal/service/transaction.go +++ b/internal/service/transaction.go @@ -7,12 +7,15 @@ import ( "log/slog" "spend-sparrow/internal/db" "spend-sparrow/internal/types" + "strconv" "time" "github.com/google/uuid" "github.com/jmoiron/sqlx" ) +const page_size = 25 + type Transaction interface { Add(ctx context.Context, tx *sqlx.Tx, user *types.User, transaction types.Transaction) (*types.Transaction, error) Update(ctx context.Context, user *types.User, transaction types.Transaction) (*types.Transaction, error) @@ -231,22 +234,41 @@ func (s TransactionImpl) GetAll(ctx context.Context, user *types.User, filter ty return nil, ErrUnauthorized } + var ( + page int64 + offset int64 + err error + ) + if filter.Page != "" { + page, err = strconv.ParseInt(filter.Page, 10, 64) + if err != nil { + offset = 0 + } else { + offset = page - 1 + offset = offset * page_size + } + } + transactions := make([]*types.Transaction, 0) - err := s.db.SelectContext(ctx, &transactions, ` + err = s.db.SelectContext(ctx, &transactions, ` SELECT * FROM "transaction" WHERE user_id = ? - AND (? = '' OR account_id = ?) - AND (? = '' OR treasure_chest_id = ?) - AND (? = '' - OR (? = "true" AND error IS NOT NULL) - OR (? = "false" AND error IS NULL) + AND ($1 = '' OR account_id = $1) + AND ($2 = '' OR treasure_chest_id = $2) + AND ($3 = '' + OR ($3 = "true" AND error IS NOT NULL) + OR ($3 = "false" AND error IS NULL) ) - ORDER BY timestamp DESC, created_at DESC`, + ORDER BY timestamp DESC, created_at DESC + LIMIT $4 OFFSET $5 + `, user.Id, - filter.AccountId, filter.AccountId, - filter.TreasureChestId, filter.TreasureChestId, - filter.Error, filter.Error, filter.Error) + filter.AccountId, + filter.TreasureChestId, + filter.Error, + page_size, + offset) err = db.TransformAndLogDbError(ctx, "transaction GetAll", nil, err) if err != nil { return nil, err diff --git a/internal/template/layout.templ b/internal/template/layout.templ index a215db4..499e380 100644 --- a/internal/template/layout.templ +++ b/internal/template/layout.templ @@ -31,6 +31,7 @@ templ Layout(slot templ.Component, user templ.Component, loggedIn bool, path str + diff --git a/internal/template/transaction/transaction.templ b/internal/template/transaction/transaction.templ index e80fc6b..97a446b 100644 --- a/internal/template/transaction/transaction.templ +++ b/internal/template/transaction/transaction.templ @@ -10,6 +10,7 @@ templ Transaction(items templ.Component, filter types.TransactionItemsFilter, ac
Has no Errors +
+
+ + Page: { getPageNumber(filter.Page) } + +
@items +
+ + Page: { getPageNumber(filter.Page) } + +
} @@ -287,3 +307,11 @@ func formatFloat(balance int64) string { euros := float64(balance) / 100 return fmt.Sprintf("%.2f", euros) } + +func getPageNumber(page string) string { + if page == "" { + return "1" + } else { + return page + } +} diff --git a/internal/types/transaction.go b/internal/types/transaction.go index a4f8a9e..f0e263a 100644 --- a/internal/types/transaction.go +++ b/internal/types/transaction.go @@ -51,4 +51,5 @@ type TransactionItemsFilter struct { AccountId string TreasureChestId string Error string + Page string } diff --git a/static/js/transaction.js b/static/js/transaction.js new file mode 100644 index 0000000..abd1af7 --- /dev/null +++ b/static/js/transaction.js @@ -0,0 +1,43 @@ + +document.addEventListener("DOMContentLoaded", () => { + if (!page || !page1 || !pagePrev1 || !pageNext1 || !page2 || !pagePrev2 || !pageNext2 || !transactionFilterForm) { + return; + } + + + const scrollToTop = function() { + window.scrollTo(0, 0); + }; + const incPage = function() { + const currPage = Number(page.value); + var nextPage = currPage + if (currPage > 1) { + nextPage -= 1; + page.value = nextPage; + transactionFilterForm.dispatchEvent(new Event('change')); + } + page1.textContent = nextPage; + page2.textContent = nextPage; + scrollToTop(); + }; + const decPage = function() { + const currPage = Number(page.value); + var nextPage = currPage + 1; + page.value = nextPage; + transactionFilterForm.dispatchEvent(new Event('change')); + page1.textContent = nextPage; + page2.textContent = nextPage; + scrollToTop(); + }; + + + + pagePrev1.addEventListener("click", incPage); + pagePrev2.addEventListener("click", incPage); + + pageNext1.addEventListener("click", decPage); + pageNext2.addEventListener("click", decPage); + + console.log("initialized pagination"); +}) +