feat(transaction): #243 add pagination to transactions
This commit was merged in pull request #249.
This commit is contained in:
@@ -15,7 +15,6 @@ import (
|
||||
type migrationLogger struct{}
|
||||
|
||||
func (l migrationLogger) Printf(format string, v ...any) {
|
||||
//nolint:noctx
|
||||
slog.Info(format, v...)
|
||||
}
|
||||
func (l migrationLogger) Verbose() bool {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 *= 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
|
||||
|
||||
@@ -31,6 +31,7 @@ templ Layout(slot templ.Component, user templ.Component, loggedIn bool, path str
|
||||
<script src="/static/js/htmx.min.js"></script>
|
||||
<script src="/static/js/toast.js"></script>
|
||||
<script src="/static/js/layout.js"></script>
|
||||
<script src="/static/js/transaction.js"></script>
|
||||
<script src="/static/js/time.js"></script>
|
||||
<script src="/static/js/echarts.min.js"></script>
|
||||
<script src="/static/js/dashboard.js" defer></script>
|
||||
|
||||
@@ -10,6 +10,7 @@ templ Transaction(items templ.Component, filter types.TransactionItemsFilter, ac
|
||||
<div class="max-w-6xl mt-10 mx-auto">
|
||||
<div class="flex items-center gap-4">
|
||||
<form
|
||||
id="transactionFilterForm"
|
||||
hx-get="/transaction"
|
||||
hx-target="#transaction-items"
|
||||
hx-push-url="true"
|
||||
@@ -52,6 +53,7 @@ templ Transaction(items templ.Component, filter types.TransactionItemsFilter, ac
|
||||
selected?={ filter.Error == "false" }
|
||||
>Has no Errors</option>
|
||||
</select>
|
||||
<input id="page" name="page" type="hidden" value={ filter.Page }/>
|
||||
</form>
|
||||
<button
|
||||
hx-get="/transaction/new"
|
||||
@@ -63,7 +65,25 @@ templ Transaction(items templ.Component, filter types.TransactionItemsFilter, ac
|
||||
<p>New Transaction</p>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex justify-end items-center gap-5 mt-5">
|
||||
<button id="pagePrev1" class="text-2xl p-2 text-yellow-700 font-black hover:bg-gray-200 rounded-lg decoration-yellow-400 decoration-[0.25rem] hover:underline">
|
||||
<
|
||||
</button>
|
||||
<span class="text-gray-400 text-sm">Page: <span class="text-gray-800 text-xl" id="page1">{ getPageNumber(filter.Page) }</span></span>
|
||||
<button id="pageNext1" class="text-2xl p-2 text-yellow-700 font-black hover:bg-gray-200 rounded-lg decoration-yellow-400 decoration-[0.25rem] hover:underline">
|
||||
>
|
||||
</button>
|
||||
</div>
|
||||
@items
|
||||
<div class="flex justify-end items-center gap-5 mt-5">
|
||||
<button id="pagePrev2" class="text-2xl p-2 text-yellow-700 font-black hover:bg-gray-200 rounded-lg decoration-yellow-400 decoration-[0.25rem] hover:underline">
|
||||
<
|
||||
</button>
|
||||
<span class="text-gray-400 text-sm">Page: <span class="text-gray-800 text-xl" id="page2">{ getPageNumber(filter.Page) }</span></span>
|
||||
<button id="pageNext2" class="text-2xl p-2 text-yellow-700 font-black hover:bg-gray-200 rounded-lg decoration-yellow-400 decoration-[0.25rem] hover:underline">
|
||||
>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,4 +51,5 @@ type TransactionItemsFilter struct {
|
||||
AccountId string
|
||||
TreasureChestId string
|
||||
Error string
|
||||
Page string
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user