Implemented export users functionality#1026
Conversation
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughThis PR adds a runtime dependency, introduces a new ExportUsersDialog component for exporting project users (CSV/JSON) with batched fetching and selectable fields, and extends UserTable to emit filter changes via an optional onFilterChange callback consumed by the users page. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant Page as "Users Page\n(page-client)"
participant Table as "UserTable"
participant Dialog as "ExportUsersDialog"
participant API as "User API"
participant ExportLib as "export-to-csv / JSON"
Note over Page,Table: Page mounts and wires exportOptions ↔ Table via onFilterChange
User->>Page: Click Export (trigger)
Page->>Dialog: Open ExportUsersDialog (pass exportOptions)
User->>Dialog: Choose format/scope/fields and click Export
Dialog->>Dialog: Validate selection
alt scope = filtered
Dialog->>API: Fetch users with filters (batched)
else scope = all
Dialog->>API: Fetch all users (batched)
end
API-->>Dialog: Return batched user data
Dialog->>Dialog: Transform users to flat records (selected fields)
Dialog->>ExportLib: Generate CSV or JSON
ExportLib-->>Dialog: File ready
Dialog->>User: Trigger download and show toast
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20–30 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ 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 |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
I'll analyze this and get back to you. |
Greptile OverviewGreptile SummaryImplemented user export functionality with CSV and JSON format support, field selection, and the ability to export all users or only filtered results. Key Changes:
Issue Found:
Confidence Score: 4/5
Important Files ChangedFile Analysis
Sequence DiagramsequenceDiagram
participant User
participant PageClient
participant ExportDialog
participant UserTable
participant AdminApp
participant Browser
User->>PageClient: Click "Export" button
PageClient->>ExportDialog: Open dialog
ExportDialog->>User: Show export options
Note over UserTable,PageClient: Filter changes propagate
User->>UserTable: Apply filters (search, includeAnonymous)
UserTable->>PageClient: onFilterChange callback
PageClient->>ExportDialog: Update exportOptions state
Note over ExportDialog: User configures export
User->>ExportDialog: Select format (CSV/JSON)
User->>ExportDialog: Select scope (all/filtered)
User->>ExportDialog: Select fields to export
User->>ExportDialog: Click "Export Users"
ExportDialog->>ExportDialog: Validate fields selected
alt Export filtered users
ExportDialog->>AdminApp: fetchAllUsers(with exportOptions)
else Export all users
ExportDialog->>AdminApp: fetchAllUsers(no options)
end
loop Pagination
AdminApp->>AdminApp: listUsers(limit: 100, cursor)
AdminApp-->>ExportDialog: Batch of users + nextCursor
end
ExportDialog->>ExportDialog: Transform user data
alt CSV format
ExportDialog->>Browser: Generate CSV + download
else JSON format
ExportDialog->>Browser: Generate JSON blob + download
end
Browser->>User: File downloaded
ExportDialog->>User: Show success toast
ExportDialog->>ExportDialog: Close dialog
|
There was a problem hiding this comment.
Pull Request Overview
This PR implements comprehensive user export functionality allowing dashboard administrators to export user data in both CSV and JSON formats. The feature includes configurable field selection, pagination-based fetching for large datasets, and the ability to export either all users or filtered results.
Key changes:
- Added new
ExportUsersDialogcomponent with field selection, format options, and export scope configuration - Integrated export functionality into the users page with proper filter state management
- Added
export-to-csvdependency for CSV export capabilities
Reviewed Changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| apps/dashboard/package.json | Added export-to-csv dependency |
| pnpm-lock.yaml | Updated lock file with new dependency |
| apps/dashboard/src/components/export-users-dialog.tsx | New component implementing export dialog with CSV/JSON export logic |
| apps/dashboard/src/components/data-table/user-table.tsx | Added filter change callback to propagate filter state |
| apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/users/page-client.tsx | Integrated export dialog with filter state management |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/users/page-client.tsx
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
apps/dashboard/src/components/export-users-dialog.tsx (1)
366-376: Type assertion used for CSV generation.Line 374 uses
as anyto bypass type checking. This suggests a potential type mismatch between the data structure and whatgenerateCsvexpects.Consider investigating the correct types for the
export-to-csvlibrary or adding a type guard to ensure data compatibility:function exportToCsv(data: Record<string, unknown>[]) { const csvConfig = mkConfig({ fieldSeparator: ",", filename: `stack-users-export-${new Date().toISOString().split("T")[0]}`, decimalSeparator: ".", useKeysAsHeaders: true, }); // Ensure data conforms to expected type const csvData = data as Array<Record<string, string | number | boolean>>; const csv = generateCsv(csvConfig)(csvData); download(csvConfig)(csv); }apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/users/page-client.tsx (1)
36-36: Type casting indicates missing type definitions.Line 36 uses
(stackAdminApp as any).useUsers({ limit: 1 }), which suggests theuseUsersmethod is not properly typed on the admin app interface. While this works, it bypasses type safety.Consider adding proper type definitions for the admin app methods to avoid type casting. If
useUsersis an internal method, consider adding it to the public API or using an alternative approach to check for first user.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (4)
apps/dashboard/package.json(1 hunks)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/users/page-client.tsx(3 hunks)apps/dashboard/src/components/data-table/user-table.tsx(2 hunks)apps/dashboard/src/components/export-users-dialog.tsx(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-11T04:13:19.308Z
Learnt from: N2D4
Repo: stack-auth/stack-auth PR: 943
File: examples/convex/app/action/page.tsx:23-28
Timestamp: 2025-10-11T04:13:19.308Z
Learning: In the stack-auth codebase, use `runAsynchronouslyWithAlert` from `stackframe/stack-shared/dist/utils/promises` for async button click handlers and form submissions instead of manual try/catch blocks. This utility automatically handles errors and shows alerts to users.
Applied to files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/users/page-client.tsx
🧬 Code graph analysis (2)
apps/dashboard/src/components/export-users-dialog.tsx (2)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/use-admin-app.tsx (1)
useAdminApp(29-44)packages/stack-shared/src/utils/promises.tsx (1)
runAsynchronouslyWithAlert(312-328)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/users/page-client.tsx (2)
apps/dashboard/src/components/export-users-dialog.tsx (1)
ExportUsersDialog(61-261)apps/dashboard/src/components/data-table/user-table.tsx (1)
UserTable(176-244)
⏰ 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). (4)
- GitHub Check: CodeQL analysis (javascript-typescript)
- GitHub Check: Agent
- GitHub Check: Vercel Agent Review
- GitHub Check: claude-review
🔇 Additional comments (6)
apps/dashboard/src/components/data-table/user-table.tsx (2)
176-178: LGTM: Clean API extension.The optional
onFilterChangecallback prop is a good design that maintains backward compatibility while enabling external components to react to filter changes.
214-221: LGTM: Proper effect implementation.The effect correctly emits filter changes with appropriate dependencies and optional chaining.
apps/dashboard/src/components/export-users-dialog.tsx (1)
61-147: Good implementation of export functionality.The component provides a comprehensive export experience with:
- Proper loading states and user feedback
- Validation (no fields selected, no users to export)
- Error handling with toast notifications
- Flexible field selection
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/users/page-client.tsx (2)
37-40: LGTM: Clean integration of export functionality.The export options state and its connection to the UserTable's
onFilterChangecallback creates a clean data flow that keeps the export dialog synchronized with table filters.Also applies to: 76-76
52-68: Good UX: Export button placement and styling.The export button with the Download icon is well-positioned next to the "Create User" button and uses appropriate variant styling (
outline) to indicate it's a secondary action.apps/dashboard/package.json (1)
45-45: No security concerns identified; package is current.The npm package export-to-csv v1.4.0 has no publicly reported direct vulnerabilities in major vulnerability databases (Snyk and npm), and the specified version
^1.4.0matches the latest release. No further action required.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (3)
apps/dashboard/src/components/export-users-dialog.tsx (3)
370-379: Avoidas anywhen generating CSV rowsCasting
datatoanyweakens type safety around what is passed intogenerateCsv:const csv = generateCsv(csvConfig)(data as any);It would be better to define a concrete row type (e.g.
Record<string, string | number | boolean | null>) and have bothtransformUserDataandexportToCsvuse that type so you can callgenerateCsv(csvConfig)(data)without ananycast. Please cross-check theexport-to-csvtypings for the expected row shape and adjust accordingly.
149-153: Fix non-semantic clickable wrapper for accessibilityUsing a bare
<div onClick>makes the trigger non-focusable and not keyboard-activatable. Wrap the trigger in a real<button>so keyboard and screen-reader users can open the dialog.You can apply something like:
- <div onClick={() => setOpen(true)}> - {trigger} - </div> + <button + type="button" + onClick={() => setOpen(true)} + className="contents" + aria-label="Open export dialog" + > + {trigger} + </button>
263-283: AlignincludeAnonymousdefault with filter behavior
fetchAllUserscurrently defaultsincludeAnonymoustotrue:includeAnonymous: options?.includeAnonymous ?? true,When
scope === "filtered"andexportOptionsis undefined or omits this flag, this changes semantics compared to the users page, where anonymous users are excluded by default.To keep behavior consistent with the filter UI:
- includeAnonymous: options?.includeAnonymous ?? true, + includeAnonymous: options?.includeAnonymous ?? false,
🧹 Nitpick comments (2)
apps/dashboard/src/components/export-users-dialog.tsx (2)
31-40: TightenExportField.keytyping to avoid driftRight now
ExportField.keyis juststring, whileDEFAULT_FIELDSandtransformUserDataassume a fixed set of keys in the bigswitch. A typo or future refactor could silently create fields that are never handled.Consider introducing a union for the allowed keys and reusing it:
type ExportFieldKey = | "id" | "displayName" | "primaryEmail" | "primaryEmailVerified" | "signedUpAt" | "lastActiveAt" | "isAnonymous" | "hasPassword" | "otpAuthEnabled" | "passkeyAuthEnabled" | "isMultiFactorRequired" | "oauthProviders" | "profileImageUrl" | "clientMetadata" | "clientReadOnlyMetadata" | "serverMetadata"; type ExportField = { key: ExportFieldKey; label: string; enabled: boolean; };This will give you exhaustiveness checking in
transformUserDataif a new key is added.Also applies to: 42-59, 288-365
288-365: Safe date handling looks good; optionally guardoauthProvidersThe updated handling of
signedUpAtandlastActiveAtcorrectly copes withnull/undefinedand pre-constructedDateinstances, returning ISO strings or an empty string, which should prevent previous runtime issues.One small extra hardening you might consider is guarding
oauthProvidersin case it can ever beundefined:data["OAuth Providers"] = (user.oauthProviders ?? []).map(p => p.id).join(", ");
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
apps/dashboard/src/components/export-users-dialog.tsx(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apps/dashboard/src/components/export-users-dialog.tsx (2)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/use-admin-app.tsx (1)
useAdminApp(29-44)packages/stack-shared/src/utils/promises.tsx (1)
runAsynchronouslyWithAlert(312-328)
⏰ 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: lint_and_build (latest)
- GitHub Check: check_prisma_migrations (22.x)
- GitHub Check: restart-dev-and-test-with-custom-base-port
- GitHub Check: setup-tests
- GitHub Check: restart-dev-and-test
- GitHub Check: docker
- GitHub Check: all-good
- GitHub Check: build (22.x)
- GitHub Check: build (22.x)
- GitHub Check: Vercel Agent Review
🔇 Additional comments (2)
apps/dashboard/src/components/export-users-dialog.tsx (2)
61-71: Overall export dialog flow and UX look solidState management for format/scope/fields, validation toasts (no fields / no users), and success/error handling are all coherent and user-friendly. The separation into helper functions keeps the component readable.
Also applies to: 89-147, 163-257
382-393: JSON export helper is correct and cleans up resourcesThe JSON export implementation (Blob + temporary anchor +
URL.revokeObjectURL) is correct and ensures no lingering object URLs.
650a43a to
d0b1235
Compare

Features:
Export ALL users (with pagination fetching)
Support both CSV and JSON formats with user selection
Allow field selection in export dialog
Button placement next to "Create User" in page header
Option to export all users or only filtered results
Summary by CodeRabbit
New Features
Chores
✏️ Tip: You can customize this high-level summary in your review settings.