feat(sdk): add sorting and RQL infinite loading to views-new tokens#1565
feat(sdk): add sorting and RQL infinite loading to views-new tokens#1565paanSinghCoder merged 23 commits intomainfrom
Conversation
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>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughSummary by CodeRabbit
WalkthroughAdds a token Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✏️ 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. Review rate limit: 0/1 reviews remaining, refill in 60 minutes.Comment |
Coverage Report for CI Build 25100950141Coverage remained the same at 42.353%Details
Uncovered Changes
Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
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>
301e0df to
31b444e
Compare
…at/add-sorting-and-pagination-tokens
…at/add-sorting-and-pagination-tokens
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>
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
web/sdk/react/views-new/tokens/tokens-view.tsx (1)
251-254:⚠️ Potential issue | 🟠 MajorVirtualization 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 ofDataTable.Contentto 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 newsourcefilter.The SQL projection is updated everywhere, but no test case exercises a filter on the new
sourcefield that was added tovalidFilterFieldsinorg_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 includeDEFAULT_SORT, likely causing a double fetch on mount.
tableQuerystarts asINITIAL_QUERY(no sort) so the first RQL request goes out unsorted; once the DataTable echoesdefaultSortback throughonTableQueryChange, the debounced query changes and a second request fires withcreated_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
⛔ Files ignored due to path filters (1)
proto/v1beta1/frontier.pb.gois excluded by!**/*.pb.go,!proto/**
📒 Files selected for processing (8)
Makefilecore/aggregates/orgtokens/service.gointernal/api/v1beta1connect/organization_tokens.gointernal/store/postgres/org_tokens_repository.gointernal/store/postgres/org_tokens_repository_test.goweb/sdk/react/views-new/tokens/components/columns.tsxweb/sdk/react/views-new/tokens/tokens-view.module.cssweb/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>
0065ab6 to
0602180
Compare
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>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
web/sdk/react/views-new/tokens/components/columns.tsx (1)
88-101: Optional: simplifyhas/getto bracket access + nullish coalescing.
TxnEventSourceMapis a plainRecord<string, string>, so the lodash indirection and the trailingas stringaren'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/getimports 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
⛔ Files ignored due to path filters (1)
web/pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (4)
Makefileweb/apps/admin/package.jsonweb/sdk/package.jsonweb/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
eb2f4a9 to
6a75c85
Compare
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>
There was a problem hiding this comment.
♻️ Duplicate comments (1)
web/sdk/react/views-new/tokens/tokens-view.tsx (1)
140-141:⚠️ Potential issue | 🟡 MinorKeep pagination fetches out of table-wide loading state.
Line [141] still ties
isFetchingNextPagetoDataTable.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
queryis controlled (Line [245]), initializing Line [48-51] without sort can allow an unsorted first request before table state sync. Put the default sort directly inINITIAL_QUERYto 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
📒 Files selected for processing (2)
web/sdk/react/views-new/tokens/tokens-view.module.cssweb/sdk/react/views-new/tokens/tokens-view.tsx
Summary
listBillingTransactions(non-RQL, client-mode) withsearchOrganizationTokens(RQL, server-mode, infinite scroll) on the views-new Tokens page.createdAt desc.description(the oldsource→ friendly-label mapping is dropped;sourceisn't in the new RPC response).useTokens, andAddTokensDialogare 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