feat(billing): Paginate invoice comparison admin UI#116647
Conversation
Adds page navigation to `/_admin/cells/$region/invoice-comparison/`. Each
of the page's two tables (the both-sides comparison list and the
one-sided unmatched debug list) gets its own paginator backed by URL
search params (`rows_page` / `unmatched_page`) so refresh and
shareable URLs preserve position. Running a new comparison resets both
pages to 1.
Consumes the new `summary.{rows,unmatched}_{page,page_size,total_pages}`
contract from getsentry#20496.
REVENG-131.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
📊 Type Coverage Diff
🔍 1 new type safety issue introducedType assertions (
This is informational only and does not block the PR. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit a4defb0. Configure here.
| align-items: center; | ||
| padding: 8px 12px; | ||
| border-bottom: 1px solid ${p => p.theme.tokens.border.primary}; | ||
| `; |
There was a problem hiding this comment.
New styled component replaces core layout primitives
Low Severity
The newly added PaginatorRow is a styled('div') using flex layout (display: flex, justify-content: space-between, align-items: center) plus padding and border-bottom — all of which map directly to existing Flex component props. Per static/AGENTS.md, core components like Flex and Container are preferred over new styled() calls. Since space.md = 8px and space.lg = 12px, the styling maps exactly to <Flex justify="between" align="center" padding="md lg" borderBottom="primary">.
Triggered by project rule: Frontend guidelines
Reviewed by Cursor Bugbot for commit a4defb0. Configure here.
…omparison Two fixes + one feature: - Move `start` / `end` from local state into URL search params. Previously a refresh wiped the in-memory `submitted` value, which disabled the query and reset the page to the empty input form — and the same gap made `?rows_page=99` URLs render nothing. The window is now reread from the URL on mount and the query fires whenever start+end are present. - After every response, if the server clamped the requested page (e.g. asked for page 99 against a 2-page result), replace the URL with the clamped value so refresh converges on the real page rather than re-requesting the bad one. - Add a "Results per page" dropdown with 25 / 50 / 100 / 250 options. Selection persists in the URL as `page_size` and is applied to both tables (sent as `rows_page_size` and `unmatched_page_size` on the request). Default is 50. Changing the value resets both paginators to page 1 — the previous offset doesn't map cleanly to the resized list. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>


Summary
REVENG-131. Paginates the
/_admin/cells/$region/invoice-comparison/page so operators can navigate past the first 200 rows.rows_page,unmatched_page) so refresh and share-links keep their position.start–end of total · page X of Yplus prev/next.Consumes the new
summary.{rows,unmatched}_{page,page_size,total_pages}contract from getsentry#20496 — that PR needs to land first.Test plan
make seed-invoice-comparisonrun, load/_admin/cells/<region>/invoice-comparison/, click Run, and confirm both tables show page 1 of 2 by default (page_size=200, 250 rows each)?rows_page=2, rows change, unmatched paginator stays on page 1?unmatched_page=2, unmatched rows change, rows table stays on page 2?rows_page=1&unmatched_page=1)start = end = now) — paginator row shows "No rows" rather than a broken Prev/Next pair?rows_page=99— backend clamps to last page, frontend shows that page with Next disabled🤖 Generated with Claude Code