Skip to content

Add pagination to the member payments table#4

Merged
GodPuffin merged 3 commits into
mainfrom
feat/member-payments-pagination
Apr 3, 2026
Merged

Add pagination to the member payments table#4
GodPuffin merged 3 commits into
mainfrom
feat/member-payments-pagination

Conversation

@GodPuffin
Copy link
Copy Markdown
Owner

Summary

  • add server-side pagination for the owner-facing member payments table on the plan details page
  • fetch one extra payment row to detect whether a next page exists and expose pager metadata to the template
  • render previous/next controls anchored back to the member payments section and cover the new behavior with handler/template tests

Testing

  • go test ./...

@GodPuffin
Copy link
Copy Markdown
Owner Author

@claude pls review

@claude
Copy link
Copy Markdown

claude Bot commented Apr 3, 2026

Claude finished @GodPuffin's task in 1m 35s —— View job


Code Review

  • Gather context
  • Read changed files
  • Analyze code quality, bugs, and best practices
  • Post review feedback

Overall this is a clean, focused implementation. The page+1 trick to detect next page is idiomatic, the anchor fragment on pagination links is a nice UX touch, and the template condition for "No member payments on page N" vs "No member payments yet" is thoughtful. A few things worth addressing:


Redundant guards — dead code

details_pagination.go:20 — the if page < 1 { page = 1 } guard inside buildMemberPaymentsPagination is unreachable. Every caller goes through memberPaymentsPage() first, which already guarantees page >= 1. Same applies to the page < 1 and pageSize < 1 guards in details_payments.go:18-23 — by the time loadAllPaymentsPage is called, both values are already validated.

Not a bug, but dead code adds noise and can mislead future readers about what invariants are actually enforced.

Fix this →


Type safety — map[string]interface{} for pagination

details_pagination.go:19 and details_payments.go:37buildMemberPaymentsPagination returns a map[string]interface{}, which the template accesses by string key. A typo in either the map or the template silently renders empty instead of failing at compile time. A small typed struct would catch this:

type MemberPaymentsPagination struct {
    CurrentPage int
    HasPrev     bool
    PrevPage    int
    HasNext     bool
    NextPage    int
}

This is especially worthwhile because the template accesses it in multiple places (HasPrev, PrevPage, HasNext, NextPage, CurrentPage).

Fix this →


Missing test cases

details_pagination_test.goTestBuildMemberPaymentsPagination only covers page 3 with hasNext=true. Two gaps:

  1. Page 1 base casebuildMemberPaymentsPagination(1, false) should produce HasPrev=false, PrevPage=1, HasNext=false, NextPage=2. Worth asserting explicitly.
  2. The page < 1 normalization in buildMemberPaymentsPagination is untested (though as noted above, it's currently unreachable).

Fix this →


Pre-existing N+1 query pattern (FYI only)

details_payments.go:99loadPaymentsByTerms calls paymentIdentity per payment, which makes 1-2 DB queries each (membership lookup + user lookup). For a full page of 10, that's up to 20 extra queries. This is pre-existing and out of scope for this PR, but worth a future optimization note.


Summary: No blocking bugs. The two things I'd address before merge are the map[string]interface{} return type (type safety) and the missing page-1 test case. The redundant guards are minor but easy to clean up.

@GodPuffin
Copy link
Copy Markdown
Owner Author

@claude good now?

@claude
Copy link
Copy Markdown

claude Bot commented Apr 3, 2026

Claude finished @GodPuffin's task in 33s —— View job


Code Review — Follow-up

  • Read current state of changed files
  • Check redundant guards (dead code)
  • Check map[string]interface{} → typed struct
  • Check missing page-1 test case

Yes, all three issues from the previous review are addressed:

Redundant guards — removed. buildMemberPaymentsPagination no longer has a page < 1 guard, and loadAllPaymentsPage no longer re-validates page/pageSize. Clean.

Type safetydomain.MemberPaymentsPagination struct is defined in models.go:59-65 and used throughout. No more map[string]interface{}.

Page-1 testTestBuildMemberPaymentsPagination now has a "first page" subtest (details_pagination_test.go:27-45) covering page=1, hasNext=false with all five fields asserted. The "middle page" case is preserved and renamed from the original.

Ready to merge.

@GodPuffin GodPuffin merged commit daa4de9 into main Apr 3, 2026
2 checks passed
@GodPuffin GodPuffin deleted the feat/member-payments-pagination branch April 3, 2026 22:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant