Skip to content

feat(sdk): add sorting and RQL infinite loading to views-new tokens#1565

Open
paanSinghCoder wants to merge 23 commits intomainfrom
feat/add-sorting-and-pagination-tokens
Open

feat(sdk): add sorting and RQL infinite loading to views-new tokens#1565
paanSinghCoder wants to merge 23 commits intomainfrom
feat/add-sorting-and-pagination-tokens

Conversation

@paanSinghCoder
Copy link
Copy Markdown
Contributor

@paanSinghCoder paanSinghCoder commented Apr 22, 2026

Summary

  • Replace listBillingTransactions (non-RQL, client-mode) with searchOrganizationTokens (RQL, server-mode, infinite scroll) on the views-new Tokens page.
  • DataTable runs in server mode with a Toolbar. Sort enabled on Date, Tokens, Member; filter on Date and Tokens; hide on Events and Member.
  • Default sort is createdAt desc.
  • Events column shows the raw description (the old source → friendly-label mapping is dropped; source isn't in the new RPC response).
  • Balance panel, useTokens, and AddTokensDialog are untouched.
Screen.Recording.2026-04-22.at.3.35.46.PM.mov

Dependency

Depends on #1564 (backend move + admin dashboard swap). Merge #1564 first, then this.

Test plan

  • pnpm run build (web turbo) passes
  • Navigate to Tokens → table loads first page, scrolls to load more
  • Sort via Display Settings on Date / Tokens / Member
  • Filter via Toolbar on Date (range) and Tokens (number)
  • Reset to default restores sort + column visibility
  • Add tokens via dialog → table refreshes

Move the RPC from AdminService to FrontierService so org admins (not only
platform superusers) can list their own org's token transactions. Matches
the FrontierService/ListInvoices gate pattern (UpdatePermission on the
org namespace). Superusers still pass via the standard interceptor bypass.

- Bump PROTON_COMMIT to pick up the proto move (raystack/proton#482).
- Regenerate proto/v1beta1 via make proto.
- Swap authorization.go entry from IsSuperUser to IsAuthorized(org, UpdatePermission).
- Switch the admin dashboard Tokens page + AddTokensDialog from
  AdminServiceQueries to FrontierServiceQueries; request/response shape
  is unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 22, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
frontier Ready Ready Preview, Comment Apr 29, 2026 9:22am

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 22, 2026

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added Source field to organization tokens, enabling filtering and visibility of token sources in the UI.
    • Implemented server-side filtering, sorting, and pagination for the tokens table.
  • Improvements

    • Redesigned tokens table with enhanced layout, better visual hierarchy, and improved column rendering.
    • Updated error handling and empty states for improved user feedback.

Walkthrough

Adds a token Source field through DB, service, API, and CSV export; bumps Proton proto commit and @raystack/proton prerelease; and converts the tokens UI to server-backed infinite-query pagination with column/rendering and styling updates.

Changes

Cohort / File(s) Summary
Build Configuration
Makefile, web/apps/admin/package.json, web/sdk/package.json
Updated Proton proto commit (PROTON_COMMIT hash) and bumped @raystack/proton prerelease dependency versions to match the new commit.
Backend — Aggregates & API
core/aggregates/orgtokens/service.go, internal/api/v1beta1connect/organization_tokens.go
Added Source string to AggregatedToken and CSV export; mapped Source into API SearchOrganizationTokens response.
Backend — Postgres Store & Tests
internal/store/postgres/org_tokens_repository.go, internal/store/postgres/org_tokens_repository_test.go
Selected billing_transactions.source AS token_source, added Source to scan/transform mapping, and updated test SQL expectations; wired source into RQL filter whitelist.
Frontend — Tokens UI
web/sdk/react/views-new/tokens/components/columns.tsx, web/sdk/react/views-new/tokens/tokens-view.tsx
Switched tokens table to server mode with useInfiniteQuery(searchOrganizationTokens), transformed DataTable queries to RQL, updated column accessors/rendering and filtering/sorting behavior.
Frontend — Styling
web/sdk/react/views-new/tokens/tokens-view.module.css
Added .truncate and .filtersBar, adjusted .tableRoot sizing and header border rules for table layout/ellipsis truncation.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • rohilsurana
  • rsbh
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

@coveralls
Copy link
Copy Markdown

coveralls commented Apr 22, 2026

Coverage Report for CI Build 25100950141

Coverage remained the same at 42.353%

Details

  • Coverage remained the same as the base build.
  • Patch coverage: 3 uncovered changes across 3 files (2 of 5 lines covered, 40.0%).
  • No coverage regressions found.

Uncovered Changes

File Changed Covered %
core/aggregates/orgtokens/service.go 1 0 0.0%
internal/api/v1beta1connect/organization_tokens.go 1 0 0.0%
internal/store/postgres/org_tokens_repository.go 3 2 66.67%

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 37074
Covered Lines: 15702
Line Coverage: 42.35%
Coverage Strength: 11.76 hits per line

💛 - Coveralls

@paanSinghCoder paanSinghCoder changed the base branch from refactor/search-org-tokens-to-frontier-service to main April 22, 2026 08:25
paanSinghCoder and others added 2 commits April 22, 2026 14:05
Updates PROTON_COMMIT to the merge commit of raystack/proton#482 and
bumps @raystack/proton npm pin to the same SHA. Regen produces no
.pb.go diff since proton branch SHA and proton main merge SHA have
identical content.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace listBillingTransactions (non-RQL, client-mode) with
searchOrganizationTokens (RQL, server-mode, infinite scroll) in the
views-new Tokens page.

- useInfiniteQuery with pageParamKey driven by getConnectNextPageParam.
- DataTable mode="server" with Toolbar (sort + filter + reset UI) and
  scroll-triggered fetchNextPage via onLoadMore.
- Sorting enabled on Date (createdAt), Tokens (amount), Member
  (userTitle). Column hiding on Events and Member. Date + number
  filters on the toolbar.
- Default sort: createdAt desc.
- Events column shows raw description (source -> friendly-label mapping
  dropped; not in the new RPC response shape).

Balance panel, useTokens, and AddTokensDialog are unchanged.

Shared utils (transform-query, connect-pagination) duplicated into
react/utils/ since the react and admin SDK entry points deploy
independently.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@paanSinghCoder paanSinghCoder force-pushed the feat/add-sorting-and-pagination-tokens branch from 301e0df to 31b444e Compare April 22, 2026 08:35
@paanSinghCoder paanSinghCoder changed the base branch from main to refactor/search-org-tokens-to-frontier-service April 22, 2026 08:37
Revert accidental regression during the views-new tokens rewrite. The
old BillingTransaction columns used getInitials(userTitle) which
produces two-letter initials (e.g. "Alice Smith" → "AS"). The rewrite
mistakenly used title?.[0] which only produces one letter.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Old behavior: userTitle -> userId UUID -> '-'
New behavior: userTitle -> '-'

Surfacing a raw UUID ("4f3b2a1c-...") is worse UX than a plain '-'.
Avatar color keying stays on userId since that's a stable
per-user identifier regardless of title.

The proto response shape (OrganizationToken) carries no email, so
restoring the pre-PR user.title -> user.email -> '-' chain is not
possible without a server-side proto change.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Keeps the inline ErrorState as the user-visible affordance (toast
was a worse UX), but re-adds the console log so operators have a
signal when debugging a failed page.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Accidentally dropped during the rewrite. Auto-sizing let the tiny
"Tokens" header collapse and the long description cell in Events
eat into adjacent columns. Matches the widths the pre-PR
BillingTransaction columns used.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Unhandled rejections fired from the DataTable scroll handler are
easy to miss in dev and noisy in prod. Wrap the awaited
fetchNextPage in try/catch and log on failure. The existing
isError state still drives the user-visible ErrorState.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Base automatically changed from refactor/search-org-tokens-to-frontier-service to main April 24, 2026 08:57
Comment thread web/sdk/admin/views/organizations/details/tokens/index.tsx
Comment thread web/sdk/react/views-new/tokens/components/columns.tsx
Comment thread web/sdk/react/views-new/tokens/components/columns.tsx Outdated
Comment thread web/sdk/react/views-new/tokens/tokens-view.tsx
Comment thread web/sdk/react/views-new/tokens/tokens-view.tsx Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
web/sdk/react/views-new/tokens/tokens-view.tsx (1)

251-254: ⚠️ Potential issue | 🟠 Major

Virtualization still missing for the infinite-scroll table.

This was raised in an earlier review: with infinite loading, the table can accumulate many rows and should use DataTable.VirtualizedContent (or equivalent) instead of DataTable.Content to keep DOM/render cost bounded.

🧹 Nitpick comments (2)
internal/store/postgres/org_tokens_repository_test.go (1)

11-146: Consider adding a test for the new source filter.

The SQL projection is updated everywhere, but no test case exercises a filter on the new source field that was added to validFilterFields in org_tokens_repository.go. A small case (e.g., Operator: "eq", Value: "system.buy") would lock in the whitelist wiring.

web/sdk/react/views-new/tokens/tokens-view.tsx (1)

45-50: Initial query doesn't include DEFAULT_SORT, likely causing a double fetch on mount.

tableQuery starts as INITIAL_QUERY (no sort) so the first RQL request goes out unsorted; once the DataTable echoes defaultSort back through onTableQueryChange, the debounced query changes and a second request fires with created_at desc. Seeding the initial state with the default sort avoids the wasted request and any momentary mis-ordering.

♻️ Proposed seed
 const INITIAL_QUERY: DataTableQuery = {
   offset: 0,
-  limit: DEFAULT_PAGE_SIZE
+  limit: DEFAULT_PAGE_SIZE,
+  sort: [DEFAULT_SORT]
 };

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c4a77814-c9d1-428e-bce0-021f6378af4e

📥 Commits

Reviewing files that changed from the base of the PR and between d11eb4e and 4a666cf.

⛔ Files ignored due to path filters (1)
  • proto/v1beta1/frontier.pb.go is excluded by !**/*.pb.go, !proto/**
📒 Files selected for processing (8)
  • Makefile
  • core/aggregates/orgtokens/service.go
  • internal/api/v1beta1connect/organization_tokens.go
  • internal/store/postgres/org_tokens_repository.go
  • internal/store/postgres/org_tokens_repository_test.go
  • web/sdk/react/views-new/tokens/components/columns.tsx
  • web/sdk/react/views-new/tokens/tokens-view.module.css
  • web/sdk/react/views-new/tokens/tokens-view.tsx

Comment thread web/sdk/react/views-new/tokens/components/columns.tsx
Comment thread web/sdk/react/views-new/tokens/tokens-view.tsx
…ged)

Drops the local file: tarball workaround now that proton#484 is
merged and the new @raystack/proton@0.1.0-7523cfd3 is published.

- Makefile: PROTON_COMMIT -> 7523cfd3 (proton main tip).
- web/sdk + web/apps/admin: @raystack/proton pinned to the published
  npm version.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the (non-existent) message/delayDuration props on the root
Tooltip with the standard Tooltip.Trigger + Tooltip.Content
composition. The trigger renders as a span so the truncated Text
fits the table cell layout. Delay is already 200ms inside apsara-v1's
TooltipTrigger wrapper; no need to pass it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Token transaction history is unbounded — orgs accumulate thousands
over time, and infinite scroll keeps all loaded pages in
infiniteData.pages. Without virtualization the DOM grows linearly
with scroll depth.

Bring back the pre-PR settings: rowHeight={48}, overscan={10}.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…roll

VirtualizedContent's internal scroll container kept the page header +
balance panel pinned and confined scrolling to the table area, which
isn't the intended UX. Content uses an IntersectionObserver-based
load-more so infinite pagination still fires while the page scrolls
naturally and the top section can scroll off.

Tradeoff: DOM grows linearly with loaded rows. Acceptable at 50/page
volumes; if it ever becomes a problem we'd need to upstream a window-
virtualization mode in apsara rather than paper over it here.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
web/sdk/react/views-new/tokens/components/columns.tsx (1)

88-101: Optional: simplify has/get to bracket access + nullish coalescing.

TxnEventSourceMap is a plain Record<string, string>, so the lodash indirection and the trailing as string aren't pulling their weight. Inline lookup also avoids the empty-string corner case (has(map, '') is false anyway, but the intent is clearer).

♻️ Suggested refactor
-      cell: ({ row, getValue }) => {
-        const value = getValue() as string;
-        const eventName = (
-          has(TxnEventSourceMap, value)
-            ? get(TxnEventSourceMap, value)
-            : row?.original?.description
-        ) as string;
+      cell: ({ row, getValue }) => {
+        const value = getValue() as string;
+        const eventName =
+          TxnEventSourceMap[value] ?? row?.original?.description ?? '';
         if (!eventName) {

The has/get imports from lodash can then be dropped if unused elsewhere in this file.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0ec31c7b-40b2-4c7b-bfa1-4c9e0128f6b3

📥 Commits

Reviewing files that changed from the base of the PR and between 4a666cf and 6a75c85.

⛔ Files ignored due to path filters (1)
  • web/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (4)
  • Makefile
  • web/apps/admin/package.json
  • web/sdk/package.json
  • web/sdk/react/views-new/tokens/components/columns.tsx
✅ Files skipped from review due to trivial changes (2)
  • web/sdk/package.json
  • web/apps/admin/package.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • Makefile

Use DataTable.VirtualizedContent (rowHeight=48, overscan=10) so the
DOM stays bounded as users scroll through many fetched pages of
token transactions. Bound .tableRoot height (max-height calc(100vh -
320px), min-height 320px) so the inner scroll container can overflow
and fire onLoadMore.

Tradeoff: page-level scroll keeps the balance panel + header pinned;
only the table region scrolls. Acceptable for tokens since the data
volume can be large.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
web/sdk/react/views-new/tokens/tokens-view.tsx (1)

140-141: ⚠️ Potential issue | 🟡 Minor

Keep pagination fetches out of table-wide loading state.

Line [141] still ties isFetchingNextPage to DataTable.isLoading, which can show global loading UI during infinite-scroll pagination instead of incremental loading behavior.

Proposed fix
 const isTxnDataLoading =
-  (isLoading || isTransactionsListLoading || isFetchingNextPage) && !isError;
+  (isLoading || isTransactionsListLoading) && !isError;
🧹 Nitpick comments (1)
web/sdk/react/views-new/tokens/tokens-view.tsx (1)

48-51: Seed the controlled query with the default sort.

Since query is controlled (Line [245]), initializing Line [48-51] without sort can allow an unsorted first request before table state sync. Put the default sort directly in INITIAL_QUERY to guarantee initial request ordering.

Proposed fix
 const INITIAL_QUERY: DataTableQuery = {
   offset: 0,
-  limit: DEFAULT_PAGE_SIZE
+  limit: DEFAULT_PAGE_SIZE,
+  sort: [DEFAULT_SORT]
 };

Also applies to: 241-241, 245-245


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8d69bdaa-36bb-44b0-a2ae-fa4faec6a66c

📥 Commits

Reviewing files that changed from the base of the PR and between 6a75c85 and 1cfc3e6.

📒 Files selected for processing (2)
  • web/sdk/react/views-new/tokens/tokens-view.module.css
  • web/sdk/react/views-new/tokens/tokens-view.tsx

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.

3 participants