Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughAdds a modular data-table system for the dashboard: cursor pagination caching and prefetching, URL query-state synchronization, stable-value hook, pagination UI, table rendering utilities and skeletons, and a large refactor of the user table to use these primitives; plus minor import/type-only import tweaks in team tables. Changes
Sequence Diagram(s)sequenceDiagram
participant User as User
participant UI as UserTable UI
participant QS as useUrlQueryState
participant PC as useCursorPaginationCache
participant Fetch as Data Fetch
participant Router as Router
User->>UI: interact (search, sort, change page/size)
UI->>QS: setState(partial)
QS->>QS: merge + sanitize + validate
QS->>Router: router.replace (update URL)
QS->>UI: updated state
UI->>PC: readCursorForPage(page)
alt cursor cached
PC->>UI: cached cursor
else cache miss
UI->>Fetch: fetch page with cursor
Fetch->>UI: return rows + nextCursor
UI->>PC: recordPageCursor(page, cursor)
UI->>PC: recordNextCursor(page, nextCursor)
end
UI->>PC: prefetchCursor(nextCursor, fetchTask)
PC->>PC: dedupe & enqueue
PC->>Fetch: run async prefetch
UI->>UI: render TableContent or TableSkeleton
UI->>User: display rows + PaginationControls
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Greptile Overview
Greptile Summary
This PR introduces a new table component system for the dashboard, replacing the existing user table implementation with a custom-built solution that provides better control and UX.
Key changes:
- Replaced the old user table with a complete rewrite featuring custom pagination, URL state management, and improved filtering
- Created 6 new reusable table utilities in
common/directory for pagination, skeleton loading, URL state sync, and cursor caching - Implemented cursor-based pagination with intelligent prefetching to improve performance
- Added URL query parameter synchronization for shareable table states (search, filters, pagination)
- Used
MapandSetfor cursor caching (follows custom instruction cd0e08f7) - Used
runAsynchronouslyWithAlertfor async user actions (follows custom instruction 5e671275)
Architecture improvements:
- Better separation of concerns with reusable hooks and components
- Type-safe column layout system with TypeScript generics
- Stable value memoization to prevent unnecessary re-renders
- Suspense-based loading with skeleton UI
Confidence Score: 4/5
- This PR is safe to merge with careful testing of the new table behavior
- The code is well-structured and follows best practices including custom instructions. The implementation uses
Map/Setinstead of plain objects, and properly usesrunAsynchronouslyWithAlertfor error handling. However, the large scope of changes (1325+ lines) and complete rewrite of the user table warrants thorough testing of pagination, filtering, and URL state management before merging. - apps/dashboard/src/components/data-table/user-table.tsx requires thorough testing of pagination and URL state edge cases
Important Files Changed
File Analysis
| Filename | Score | Overview |
|---|---|---|
| apps/dashboard/src/components/data-table/common/cursor-pagination.tsx | 5/5 | Custom hook for managing cursor-based pagination cache using Map and Set refs |
| apps/dashboard/src/components/data-table/common/table.tsx | 5/5 | Core table component using TanStack Table with typed column layout system |
| apps/dashboard/src/components/data-table/common/url-query-state.tsx | 5/5 | Custom hook for syncing state with URL query parameters using yup validation |
| apps/dashboard/src/components/data-table/user-table.tsx | 4/5 | Complete rewrite: replaced old table with new custom implementation featuring URL state management, cursor pagination, and improved UI |
Sequence Diagram
sequenceDiagram
participant User
participant UserTable
participant URLQueryState
participant CursorCache
participant StackAdminApp
participant TableContent
User->>UserTable: Loads page
UserTable->>URLQueryState: useUrlQueryState()
URLQueryState->>URLQueryState: Parse URL params with yup
URLQueryState-->>UserTable: Return query state
UserTable->>CursorCache: useCursorPaginationCache()
CursorCache-->>UserTable: Return cache methods
UserTable->>UserTable: useEffect (reset cache on filter change)
UserTable->>CursorCache: resetCache()
UserTable->>StackAdminApp: useUsers(listOptions)
StackAdminApp-->>UserTable: Return users + nextCursor
UserTable->>UserTable: useStableValue() to memoize
UserTable->>CursorCache: recordPageCursor()
UserTable->>CursorCache: recordNextCursor()
UserTable->>StackAdminApp: Prefetch next page
StackAdminApp-->>CursorCache: Cache prefetched data
UserTable->>TableContent: Render table with data
TableContent-->>User: Display table
User->>UserTable: Changes search filter
UserTable->>URLQueryState: setState({search, page: 1})
URLQueryState->>URLQueryState: router.replace() with new params
Note over UserTable,CursorCache: Cache reset triggers
User->>UserTable: Clicks next page
UserTable->>CursorCache: readCursorForPage(page+1)
CursorCache-->>UserTable: Return cached cursor
UserTable->>URLQueryState: setState({page, cursor})
URLQueryState->>URLQueryState: Update URL
UserTable->>StackAdminApp: useUsers() with cached cursor
StackAdminApp-->>UserTable: Return next page data
9 files reviewed, no comments
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
apps/dashboard/src/components/data-table/common/cursor-pagination.tsx (1)
31-40: Wrap the prefetch task before passing torunAsynchronously.If
taskthrows synchronously (before returning a Promise), the exception will bubble out ofprefetchCursorbecause we calltask()beforerunAsynchronouslycan attach its safety net. Passing a thunk (() => task()) letsrunAsynchronouslyhandle both sync and async errors consistently.- runAsynchronously(task()); + runAsynchronously(() => task());
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
apps/dashboard/src/components/data-table/common/cursor-pagination.tsx(1 hunks)apps/dashboard/src/components/data-table/common/pagination.tsx(1 hunks)apps/dashboard/src/components/data-table/common/stable-value.tsx(1 hunks)apps/dashboard/src/components/data-table/common/table-skeleton.tsx(1 hunks)apps/dashboard/src/components/data-table/common/table.tsx(1 hunks)apps/dashboard/src/components/data-table/common/url-query-state.tsx(1 hunks)apps/dashboard/src/components/data-table/team-member-search-table.tsx(1 hunks)apps/dashboard/src/components/data-table/team-member-table.tsx(1 hunks)apps/dashboard/src/components/data-table/user-table.tsx(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
apps/dashboard/src/components/data-table/common/pagination.tsx (1)
apps/dashboard/src/components/data-table/common/table.tsx (1)
combineClassNames(27-29)
apps/dashboard/src/components/data-table/common/cursor-pagination.tsx (1)
packages/stack-shared/src/utils/promises.tsx (1)
runAsynchronously(343-366)
apps/dashboard/src/components/data-table/common/table-skeleton.tsx (1)
apps/dashboard/src/components/data-table/common/table.tsx (5)
ColumnLayout(15-15)getRowHeightStyle(23-25)DEFAULT_ROW_HEIGHT_PX(21-21)combineClassNames(27-29)getColumnStyles(31-40)
apps/dashboard/src/components/data-table/common/url-query-state.tsx (1)
apps/dashboard/src/components/router.tsx (1)
useRouter(15-33)
apps/dashboard/src/components/data-table/user-table.tsx (11)
apps/dashboard/src/components/data-table/common/table.tsx (3)
ColumnLayout(15-15)ColumnMeta(17-19)TableContent(49-119)apps/dashboard/src/components/data-table/common/cursor-pagination.tsx (1)
useCursorPaginationCache(6-49)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/use-admin-app.tsx (1)
useAdminApp(29-44)apps/dashboard/src/components/data-table/common/stable-value.tsx (1)
useStableValue(5-12)packages/stack-shared/src/utils/promises.tsx (2)
runAsynchronously(343-366)runAsynchronouslyWithAlert(312-328)apps/dashboard/src/components/data-table/common/pagination.tsx (1)
PaginationControls(26-76)apps/dashboard/src/components/user-dialogs.tsx (2)
DeleteUserDialog(6-31)ImpersonateUserDialog(34-57)apps/dashboard/src/components/payments/create-checkout-dialog.tsx (1)
CreateCheckoutDialog(24-81)packages/stack-shared/src/utils/strings.tsx (1)
deindent(235-238)apps/dashboard/src/components/data-table/common/url-query-state.tsx (1)
useUrlQueryState(23-93)packages/stack-shared/src/utils/dates.tsx (1)
fromNow(28-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Vercel Agent Review
- GitHub Check: Security Check
N2D4
left a comment
There was a problem hiding this comment.
Can we have the email status just be an icon on the Email column like in the opld table to save some space? Otherwise LGTM!
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
apps/dashboard/src/components/data-table/user-table.tsx (1)
202-204: Critical: Pagination cache wiped on every render.The
cursorPaginationCacheobject reference changes on every render (even though the functions inside areuseCallback'd), causing this effect to fire continuously and clear all stored cursors. This breaks back/forward pagination.The fix (as noted in past reviews) is to destructure
resetCacheoutside the effect:const cursorPaginationCache = useCursorPaginationCache(); + const { resetCache } = cursorPaginationCache; useEffect(() => { - cursorPaginationCache.resetCache(); - }, [cursorPaginationCache, query.search, query.includeAnonymous, query.pageSize, query.signedUpOrder]); + resetCache(); + }, [resetCache, query.search, query.includeAnonymous, query.pageSize, query.signedUpOrder]);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
apps/dashboard/src/components/data-table/user-table.tsx(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apps/dashboard/src/components/data-table/user-table.tsx (11)
packages/template/src/lib/stack-app/users/index.ts (1)
ServerUser(415-415)apps/dashboard/src/components/data-table/common/table.tsx (3)
ColumnLayout(15-15)ColumnMeta(17-19)TableContent(49-119)apps/dashboard/src/components/data-table/common/cursor-pagination.tsx (1)
useCursorPaginationCache(6-49)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/use-admin-app.tsx (1)
useAdminApp(29-44)apps/dashboard/src/components/data-table/common/stable-value.tsx (1)
useStableValue(5-12)packages/stack-shared/src/utils/promises.tsx (2)
runAsynchronously(343-366)runAsynchronouslyWithAlert(312-328)apps/dashboard/src/components/data-table/common/table-skeleton.tsx (1)
TableSkeleton(21-68)apps/dashboard/src/components/user-dialogs.tsx (2)
DeleteUserDialog(6-31)ImpersonateUserDialog(34-57)apps/dashboard/src/components/payments/create-checkout-dialog.tsx (1)
CreateCheckoutDialog(24-81)apps/dashboard/src/components/data-table/common/url-query-state.tsx (1)
useUrlQueryState(23-93)packages/stack-shared/src/utils/dates.tsx (1)
fromNow(28-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
- GitHub Check: build (22.x)
- GitHub Check: check_prisma_migrations (22.x)
- GitHub Check: all-good
- GitHub Check: Vercel Agent Review
- GitHub Check: lint_and_build (latest)
- GitHub Check: docker
- GitHub Check: restart-dev-and-test-with-custom-base-port
- GitHub Check: build (22.x)
- GitHub Check: build (22.x)
- GitHub Check: restart-dev-and-test
- GitHub Check: setup-tests
🔇 Additional comments (14)
apps/dashboard/src/components/data-table/user-table.tsx (14)
1-52: LGTM! Clean import structure.The imports are well-organized with proper type-only imports where needed.
53-119: LGTM! Well-structured type definitions.The type system is comprehensive and the responsive column layout using
clamp()is a good design choice.
120-172: LGTM! Robust validation and transformation logic.The schema validation with Yup and custom transforms properly handles edge cases and invalid inputs.
235-283: LGTM! Accessible and user-friendly header.The search input with debouncing, clear button, and proper ARIA labels follows best practices.
285-420: LGTM! Solid pagination and data fetching logic.The use of
useStableValuewith fingerprinting, proper cursor caching, and prefetching provides excellent UX. The empty state with reset functionality is well-designed.
422-519: LGTM! Complete skeleton implementation.The skeleton properly mirrors the table structure and the exhaustive switch with a default error case is good defensive programming.
521-572: LGTM! Type-safe column definitions with accessibility.The modular column construction and accessible sort toggle with dynamic ARIA labels demonstrate good practices.
574-639: LGTM! Comprehensive user actions with proper error handling.The action menu provides appropriate admin functionality with error handling via
runAsynchronouslyWithAlert. The impersonation flow requiring manual console paste is a reasonable security measure.
641-670: LGTM! Comprehensive fingerprinting for memoization.The fingerprint captures all relevant user fields, and
normalizeDateValuehandles edge cases defensively.
672-715: LGTM! Clean URL state management.The sanitization ensures valid states and the serialization keeps URLs minimal by excluding default values.
717-726: LGTM! Simple utility functions.These helpers serve their purpose well, and the ID formatting doesn't affect copying since the full ID is used in the copy action.
728-746: LGTM! Type-safe user data transformation.The overloads correctly handle different return types, and the auth type derivation logic is clear and comprehensive.
748-797: LGTM! Reusable column definitions.The generic type parameter and modular cell components make this function highly reusable for other table contexts.
799-834: LGTM! Well-structured cell components.The cell components are modular and handle edge cases defensively. The date formatting with tooltips and the auth method badge mapping are particularly well done.
Also applies to: 860-925
Summary by CodeRabbit