Skip to content

Manage User#6

Merged
chakrihacker merged 33 commits into
mainfrom
feat/manage-user
Aug 4, 2025
Merged

Manage User#6
chakrihacker merged 33 commits into
mainfrom
feat/manage-user

Conversation

@chakrihacker

@chakrihacker chakrihacker commented Aug 4, 2025

Copy link
Copy Markdown
Contributor

PR Type

Enhancement


Description

  • Add comprehensive user detail management screen with CRUD operations

  • Implement user session management and revocation functionality

  • Add UI components (Badge, Separator) and enhance table navigation

  • Restructure admin components and update dependencies


Diagram Walkthrough

flowchart LR
  A["User List"] --> B["User Detail Screen"]
  B --> C["User Info Management"]
  B --> D["Session Management"]
  C --> E["Ban/Unban User"]
  C --> F["Role Management"]
  D --> G["Revoke Sessions"]
  B --> H["Email Actions"]
Loading

File Walkthrough

Relevant files
Enhancement
9 files
user-detail.model.ts
Add comprehensive user detail management model                     
+764/-0 
manage-users.tsx
Update prop name for UsersList component                                 
+1/-1     
[id].tsx
Add user detail screen route                                                         
+5/-0     
badge.tsx
Add Badge component with variants                                               
+52/-0   
separator.tsx
Add Separator component for UI dividers                                   
+27/-0   
user-table-row.tsx
Add view details button with navigation                                   
+14/-1   
users-list-filters.tsx
Restructure filters component with add user navigation     
+136/-0 
manage-users-list.container.tsx
Add focus refresh and restructure component imports           
+60/-46 
user-detail.container.tsx
Implement comprehensive user detail management interface 
+538/-0 
Documentation
1 files
copilot-instructions.md
Add method parameter guidelines for object destructuring 
+16/-0   
Dependencies
1 files
package.json
Update Better Auth and add Separator dependency                   
+3/-2     
Miscellaneous
1 files
users-list-filters.tsx
Remove old filters component file                                               
+0/-128 
Additional files
1 files
user-table-header.tsx [link]   

Summary by CodeRabbit

  • New Features

    • Added a detailed admin user detail view, allowing admins to view, edit, ban/unban, and delete users, manage user sessions, and send verification or password reset emails.
    • Introduced new UI components: Badge and Separator for enhanced interface styling.
    • Added a "View User" action button to each row in the admin user table for quick access to user details.
    • Added a new user list filter component with email and role filtering, clear filters, and add user actions.
  • Improvements

    • Refined user list filtering with a new, more maintainable filter component and improved method parameter usage for better maintainability.
    • Updated dependencies and added a new UI primitive for separators.
    • Implemented a naming convention for model instances using a $ suffix for clarity.
    • Enhanced user list to refresh data on screen focus for up-to-date information.
    • Renamed user list model prop for clearer code semantics.
  • Bug Fixes

    • None noted.
  • Chores

    • Updated documentation to clarify best practices for method parameters.

refactor: restructure layout components for public and protected routes

fix: update HeaderUser component to include admin options and improve navigation

chore: update auth-client to include admin client plugin
…een; add toast notification for login errors; implement session-based redirection in LandingScreen
…g; update user management model to support filters
…rch criteria to email; refactor UsersList component for improved loading and error handling
…us filter from user management model and related components
…ing state; update container guidelines in documentation
… file paths to align with Expo Router structure
…agement; update imports in manage users list container
…etail screen and update navigation for user details
…n 1.3.4; refactor UserDetailContainer and UserDetailModel for improved state management and form handling
…tModel$ prop for improved consistency and functionality
…Model; update UI handling in UserDetailContainer
@sourcery-ai

sourcery-ai Bot commented Aug 4, 2025

Copy link
Copy Markdown

Reviewer's Guide

This PR refactors the 'Manage Users' feature by moving list subcomponents into a container structure, renaming the model prop, and integrating expo-router hooks for data refresh; enhances table rows with navigable action buttons; updates dependencies; and introduces a comprehensive user detail screen backed by a new UserDetailModel and corresponding UI components, as well as new generic Badge and Separator primitives.

Sequence diagram for navigating from user list to user detail

sequenceDiagram
    actor Admin
    participant UsersList
    participant UserTableRow
    participant Router
    participant UserDetailContainer
    participant UserDetailModel

    Admin->>UsersList: View list of users
    UsersList->>UserTableRow: Render each user row
    Admin->>UserTableRow: Tap 'View User' button
    UserTableRow->>Router: router.push('/admin/user-detail/:id')
    Router->>UserDetailContainer: Mount with user id
    UserDetailContainer->>UserDetailModel: fetchUserById({ id })
    UserDetailContainer->>UserDetailModel: fetchUserSessionsById({ id })
    UserDetailModel-->>UserDetailContainer: Provide user data and sessions
    UserDetailContainer-->>Admin: Display user detail screen
Loading

Class diagram for new Badge and Separator UI primitives

classDiagram
    class Badge {
        +variant: 'default' | 'secondary' | 'destructive' | 'outline'
        +asChild: boolean
        +children
    }
    class Separator {
        +orientation: 'horizontal' | 'vertical'
        +decorative: boolean
    }
Loading

File-Level Changes

Change Details Files
Refactor UsersList container and props
  • rename prop 'model' to 'userListModel$' across UsersList and screens
  • relocate UserTableHeader, UserTableRow, UsersListFilters imports to container path
  • add useFocusEffect hook to refresh user list when screen gains focus
  • update component prop usage to match new model name
src/containers/admin/manage-users-list.container.tsx
src/app/(public)/(protected)/admin/manage-users.tsx
Enhance UserTableRow with navigation action
  • replace static 'Actions' label with a ghost Button
  • implement handleViewDetails to navigate to user-detail screen via router.push
src/containers/admin/components/user-table-row.tsx
Update project dependencies
  • bump @better-auth/expo and better-auth packages to version ^1.3.4
  • add @rn-primitives/separator dependency
package.json
Introduce UserDetail feature
  • add UserDetailModel with observables and methods for fetch/update/delete, session and email operations
  • create UserDetailContainer with cards, forms, dialogs and bind model methods
  • add new route for user-detail screen under admin path
src/models/admin/user-detail.model.ts
src/containers/admin/user-detail.container.tsx
src/app/(public)/(protected)/admin/user-detail/[id].tsx
Add generic UI primitives: Badge and Separator
  • create Badge component with CVA-based variant styling
  • wrap rn-primitives Separator into a reusable Separator component
src/components/ui/badge.tsx
src/components/ui/separator.tsx

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@qodo-code-review

Copy link
Copy Markdown

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
🧪 No relevant tests
🔒 Security concerns

Sensitive information exposure:
The user detail screen displays IP addresses and user agent strings in the sessions list, which could be considered sensitive information. Additionally, the admin functionality allows viewing and manipulating user data including email addresses and session information without apparent additional authorization checks beyond the route protection.

⚡ Recommended focus areas for review

Error Handling

The error handling in several methods catches errors but then re-throws them after showing toast messages, which could lead to unhandled promise rejections if callers don't properly handle the re-thrown errors.

} catch (error) {
	const errorMessage = this.getErrorMessage(error, "Failed to update user");
	toast.error(errorMessage);
	throw error;
}
Type Safety

The user agent parsing logic uses unsafe string operations and type assertions without proper validation. The ExtendedBetterAuthSession interface extends BetterAuthSession but the casting may not be safe if the actual session object doesn't contain the expected fields.

device: this.parseUserAgent(
	(session as ExtendedBetterAuthSession).userAgent || "",
),
location:
	(session as ExtendedBetterAuthSession).ipAddress ||
	"Unknown Location",
Performance Issue

The component fetches user sessions on every render when the id changes, but there's no cleanup or cancellation mechanism for ongoing requests, which could lead to race conditions or memory leaks.

useEffect(() => {
	if (id) {
		userDetailModel$.fetchUserById({ id });
		userDetailModel$.fetchUserSessionsById({ id });
	}
}, [id, userDetailModel$]);

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey @chakrihacker - I've reviewed your changes - here's some feedback:

  • The UserDetailModel class is quite large (~760 lines); consider splitting it into smaller modules (e.g. separate API service, form state, and utility helpers) to improve readability and maintainability.
  • The $ suffix on userListModel$ (and similar model props) is a new convention—make sure it’s documented and applied consistently so other developers immediately recognize these as observables.
  • The useFocusEffect hook will re‐fetch users on every focus if any data exists; you may want to add a more precise condition (e.g. compare a last‐updated timestamp or track navigation params) to avoid redundant API calls.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The UserDetailModel class is quite large (~760 lines); consider splitting it into smaller modules (e.g. separate API service, form state, and utility helpers) to improve readability and maintainability.
- The `$` suffix on `userListModel$` (and similar model props) is a new convention—make sure it’s documented and applied consistently so other developers immediately recognize these as observables.
- The useFocusEffect hook will re‐fetch users on every focus if any data exists; you may want to add a more precise condition (e.g. compare a last‐updated timestamp or track navigation params) to avoid redundant API calls.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@qodo-code-review

qodo-code-review Bot commented Aug 4, 2025

Copy link
Copy Markdown

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
General
Remove unnecessary effect dependency

The userDetailModel$ dependency in the useEffect could cause unnecessary
re-renders since it's a ref that doesn't change. Remove it from the dependency
array to prevent potential infinite loops.

src/containers/admin/user-detail.container.tsx [56-61]

 useEffect(() => {
   if (id) {
     userDetailModel$.fetchUserById({ id });
     userDetailModel$.fetchUserSessionsById({ id });
   }
-}, [id, userDetailModel$]);
+}, [id]);
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that userDetailModel$ is a stable reference from useRef and should not be in the useEffect dependency array, preventing potential re-runs of the effect.

Medium
Validate required parameters

The method doesn't validate the id parameter before making the API call. This
could lead to unnecessary API requests with invalid data and potential errors.

src/models/admin/user-detail.model.ts [131-143]

 async fetchUserById({ id }: { id: string }): Promise<void> {
+  if (!id?.trim()) {
+    this.obs.status.set("error");
+    this.obs.error.set("User ID is required");
+    return;
+  }
+
   this.obs.status.set("loading");
   this.obs.error.set(null);
 
   try {
     const { data, error } = await authClient.admin.listUsers({
       query: {
         filterField: "id",
         filterValue: id,
         filterOperator: "eq",
         limit: 1,
       },
     });
  • Apply / Chat
Suggestion importance[1-10]: 5

__

Why: The suggestion correctly identifies a missing validation check in the model method, and adding it improves the model's robustness by making it less dependent on callers for validation.

Low
Possible issue
Add parameter validation check

The id parameter could be undefined, which would cause issues when passed to API
calls. Add proper validation to ensure id exists before proceeding with data
fetching operations.

src/containers/admin/user-detail.container.tsx [35-36]

 const { id } = useLocalSearchParams<{ id: string }>();
 const userDetailModel$ = useRef(new UserDetailModel()).current;
 
+if (!id) {
+  return (
+    <View className="flex-1 items-center justify-center p-4">
+      <Text className="text-destructive text-center mb-4">
+        Invalid user ID
+      </Text>
+      <Button onPress={() => router.back()}>
+        <Text>Go Back</Text>
+      </Button>
+    </View>
+  );
+}
+
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why: This is a valid suggestion that improves robustness and user experience by handling cases where the id parameter is missing, providing a clearer error message to the user.

Low
  • Update

@coderabbitai

coderabbitai Bot commented Aug 4, 2025

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

This update introduces a detailed admin user detail feature, including new UI components, a stateful model for user/session management, and a full user detail container. It refactors admin user management components, adds reusable UI primitives (badge, separator), updates dependencies, and improves documentation with method parameter guidelines and naming conventions. The codebase now supports user detail viewing, editing, session management, ban/delete flows, and enhanced user list filtering.

Changes

Cohort / File(s) Change Summary
Admin User Detail Feature
src/containers/admin/user-detail.container.tsx, src/models/admin/user-detail.model.ts, src/models/admin/user-detail/email.model.ts, src/models/admin/user-detail/form.model.ts, src/models/admin/user-detail/moderation.model.ts, src/models/admin/user-detail/session.model.ts, src/models/admin/user-detail/user.model.ts
Introduces a comprehensive user detail container and a stateful model composed of sub-models for user, session, moderation, email, and form management. Supports user editing, banning, deletion, session revocation, email verification, and password reset with observable state and API integration.
Admin User Management Refactor
src/app/(public)/(protected)/admin/manage-users.tsx, src/containers/admin/manage-users-list.container.tsx, src/containers/admin/components/user-table-row.tsx, src/containers/admin/components/users-list-filters.tsx
Refactors prop names and component imports, adds a "View User" action button in the user table rows, and reimplements user list filters as an observer component with debounced search, role filtering, and improved state management and UI.
Removed Old Filters Component
src/components/admin/users-list-filters.tsx
Deletes the old users list filters component, replaced by a new implementation in containers.
New UI Primitives
src/components/ui/badge.tsx, src/components/ui/separator.tsx
Adds reusable Badge and Separator UI components with variant-based styling, context-driven text styling, orientation support, and ref forwarding.
User Detail Screen Route
src/app/(public)/(protected)/admin/user-detail/[id].tsx
Adds a new route component that renders the user detail container for a given user ID as a simple wrapper.
Documentation and Dependency Updates
.github/copilot-instructions.md, package.json
Adds method parameter best practices and naming conventions to documentation; updates dependencies including bumping versions for @better-auth/expo and better-auth, and adding @rn-primitives/separator.

Sequence Diagram(s)

sequenceDiagram
  participant Admin as Admin User
  participant Screen as UserDetailScreen
  participant Container as UserDetailContainer
  participant Model as UserDetailModel
  participant API as Backend API

  Admin->>Screen: Navigates to /admin/user-detail/[id]
  Screen->>Container: Render UserDetailContainer
  Container->>Model: Initialize, fetchUserById(id)
  Model->>API: GET /users/:id
  API-->>Model: User data
  Model->>Container: Update observable state
  Container->>Admin: Display user info, sessions, actions

  Admin->>Container: Edit user fields / trigger actions
  Container->>Model: Call update/ban/delete/session methods
  Model->>API: Perform corresponding API calls
  API-->>Model: Response
  Model->>Container: Update state, show feedback
  Container->>Admin: Update UI, modals, notifications
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related PRs

Poem

A rabbit hops through code anew,
With badges bright and separators true.
Admins now can see each user’s tale,
Edit, ban, or send an email.
With models keen and UI neat,
This update makes the dashboard sweet!
🐇✨

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b6f1663 and 4af400c.

📒 Files selected for processing (4)
  • src/containers/admin/components/user-table-row.tsx (3 hunks)
  • src/containers/admin/user-detail.container.tsx (1 hunks)
  • src/models/admin/user-detail.model.ts (1 hunks)
  • src/models/admin/user-detail/session.model.ts (1 hunks)
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/manage-user

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (5)
src/models/admin/user-detail.model.ts (2)

136-143: Consider using a dedicated endpoint for fetching a single user if available.

Using listUsers with a filter to fetch a single user may be less efficient than a dedicated endpoint. If Better Auth provides a getUser or similar method, consider using that instead.


464-465: Add radix parameter to parseInt for clarity.

Always specify the radix parameter when using parseInt to avoid potential parsing issues.

-				const days = parseInt(banDuration);
+				const days = parseInt(banDuration, 10);
src/containers/admin/user-detail.container.tsx (3)

6-27: Use the prescribed path-alias for UI imports

Coding guidelines specify importing UI primitives via @/src/components/ui/*, whereas this file uses ~/components/ui/*.
Please align the alias to avoid fragmented import styles across the codebase.


56-62: Trim unnecessary dependencies in useEffect

userDetailModel$ is a stable ref; keeping it in the dependency array causes the effect to re-register needlessly on every render (eslint will still flag but the value is constant).

-}, [id, userDetailModel$]);
+}, [id]);

This avoids superfluous executions while preserving correctness.


63-65: Avoid repeated date parsing on every render

formatDate is recreated each render and each call instantiates Date, which can be hot when many rows re-render (e.g. long session lists). Consider:

const formatDate = useCallback(
  (d: string | Date) => dayjs(d).format('LLL'),
  [],
);

or memoise pre-formatted fields in the model.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6e8bb28 and 08b788d.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (12)
  • .github/copilot-instructions.md (1 hunks)
  • package.json (2 hunks)
  • src/app/(public)/(protected)/admin/manage-users.tsx (1 hunks)
  • src/app/(public)/(protected)/admin/user-detail/[id].tsx (1 hunks)
  • src/components/admin/users-list-filters.tsx (0 hunks)
  • src/components/ui/badge.tsx (1 hunks)
  • src/components/ui/separator.tsx (1 hunks)
  • src/containers/admin/components/user-table-row.tsx (3 hunks)
  • src/containers/admin/components/users-list-filters.tsx (1 hunks)
  • src/containers/admin/manage-users-list.container.tsx (2 hunks)
  • src/containers/admin/user-detail.container.tsx (1 hunks)
  • src/models/admin/user-detail.model.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • src/components/admin/users-list-filters.tsx
🧰 Additional context used
📓 Path-based instructions (4)
src/app/**/*.tsx

📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)

src/app/**/*.tsx: src/app/ contains all routes using Expo Router file-based routing
Expo Router requires specific file naming conventions for routing

Files:

  • src/app/(public)/(protected)/admin/manage-users.tsx
  • src/app/(public)/(protected)/admin/user-detail/[id].tsx
src/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)

src/**/*.{ts,tsx}: Use Platform.OS and process.env.EXPO_OS for conditional logic
Consider all platforms (iOS/Android/Web) when using native APIs

Files:

  • src/app/(public)/(protected)/admin/manage-users.tsx
  • src/containers/admin/components/user-table-row.tsx
  • src/app/(public)/(protected)/admin/user-detail/[id].tsx
  • src/components/ui/badge.tsx
  • src/containers/admin/user-detail.container.tsx
  • src/containers/admin/components/users-list-filters.tsx
  • src/models/admin/user-detail.model.ts
  • src/containers/admin/manage-users-list.container.tsx
  • src/components/ui/separator.tsx
src/**/*.tsx

📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)

Import UI components from @/src/components/ui/ using path aliases

Files:

  • src/app/(public)/(protected)/admin/manage-users.tsx
  • src/containers/admin/components/user-table-row.tsx
  • src/app/(public)/(protected)/admin/user-detail/[id].tsx
  • src/components/ui/badge.tsx
  • src/containers/admin/user-detail.container.tsx
  • src/containers/admin/components/users-list-filters.tsx
  • src/containers/admin/manage-users-list.container.tsx
  • src/components/ui/separator.tsx
src/components/ui/**/*.tsx

📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)

src/components/ui/**/*.tsx: UI components use NativeWind's className prop with Tailwind utility classes
Use cva (class-variance-authority) for creating variant-based component APIs
Access theme values through CSS custom properties and Tailwind utilities
UI components in src/components/ui/ follow React.forwardRef pattern with displayName
Use Tailwind's responsive prefixes (sm:, md:, lg:) and NativeWind's responsive utilities
Use web: prefix for web-only styles, and conditional rendering for native-specific components
Place UI components in src/components/ui/ with proper TypeScript interfaces
Always use NativeWind's className prop with Tailwind utility classes
Define variants using cva (class-variance-authority) for type-safe component APIs
Use Tailwind's spacing utilities (p-4, m-2, gap-3, etc.)
Use Tailwind's typography utilities (text-lg, font-semibold, etc.)
Toggle between light/dark themes using className conditionals and CSS variables
Use platform-specific prefixes (web:, ios:, android:) for conditional styles

Files:

  • src/components/ui/badge.tsx
  • src/components/ui/separator.tsx
🧬 Code Graph Analysis (6)
src/app/(public)/(protected)/admin/manage-users.tsx (1)
src/containers/admin/manage-users-list.container.tsx (1)
  • UsersList (92-153)
src/containers/admin/components/user-table-row.tsx (2)
src/components/ui/button.tsx (1)
  • Button (80-80)
src/components/ui/text.tsx (1)
  • Text (26-26)
src/app/(public)/(protected)/admin/user-detail/[id].tsx (1)
src/containers/admin/user-detail.container.tsx (1)
  • UserDetailContainer (34-538)
src/components/ui/badge.tsx (2)
src/components/ui/text.tsx (1)
  • TextClassContext (26-26)
src/lib/utils.ts (1)
  • cn (4-6)
src/containers/admin/components/users-list-filters.tsx (5)
src/models/admin/manage-users-list.model.ts (1)
  • ManageUsersListModel (151-381)
src/components/ui/select.tsx (5)
  • SelectTrigger (188-188)
  • Select (180-180)
  • SelectValue (189-189)
  • SelectContent (181-181)
  • SelectItem (183-183)
src/components/ui/text.tsx (1)
  • Text (26-26)
src/components/ui/input.tsx (1)
  • Input (25-25)
src/components/ui/button.tsx (1)
  • Button (80-80)
src/components/ui/separator.tsx (1)
src/lib/utils.ts (1)
  • cn (4-6)
🔇 Additional comments (19)
package.json (1)

15-15: LGTM! Dependency updates are appropriate.

The minor version updates to better-auth packages and the addition of @rn-primitives/separator align well with the PR's admin user management enhancements.

Also applies to: 32-32, 38-38

.github/copilot-instructions.md (1)

8-23: Excellent addition to the coding guidelines!

The method parameters section clearly documents the object destructuring pattern that improves code maintainability and type safety. The examples effectively illustrate the preferred approach.

src/models/admin/user-detail.model.ts (1)

749-749: Good security practice!

Resetting emailVerified to false when the email is changed ensures users must re-verify their new email address.

src/app/(public)/(protected)/admin/manage-users.tsx (1)

16-16: LGTM! Prop name updated to match the component interface.

The prop name change to userListModel$ aligns with the updated UsersList component interface and follows the observable naming convention.

src/app/(public)/(protected)/admin/user-detail/[id].tsx (1)

1-5: LGTM! Clean implementation following Expo Router patterns.

The screen component correctly wraps the UserDetailContainer and follows the established pattern of separating route components from containers.

src/containers/admin/components/user-table-row.tsx (2)

1-2: LGTM! Import statements follow coding guidelines.

The imports correctly use path aliases and follow the established patterns for UI components and navigation.

Also applies to: 5-5


35-38: LGTM! Clean navigation handler implementation.

The handler function properly implements navigation to the user detail screen with a clear route structure.

src/containers/admin/manage-users-list.container.tsx (4)

4-4: LGTM! Import updates are consistent with the refactoring.

The addition of useFocusEffect and updated import paths align with the component reorganization and new data refresh requirements.

Also applies to: 11-13


17-17: LGTM! Prop naming follows observable conventions.

The rename from model to userListModel$ improves clarity and follows the observable naming pattern consistently throughout the component.

Also applies to: 93-93, 125-125


99-108: LGTM! Well-implemented focus effect for data freshness.

The conditional refresh logic prevents unnecessary API calls while ensuring data stays current when navigating back to the screen.


110-118: LGTM! Efficient column width calculation with proper memoization.

The column width logic is well-structured with appropriate performance optimizations and clear minimum width definitions.

src/containers/admin/components/users-list-filters.tsx (4)

42-50: LGTM! Well-implemented debouncing with proper cleanup.

The 300ms debounce delay and conditional search trigger provide good UX while preventing excessive API calls.


31-39: LGTM! Proper platform-specific safe area handling.

The different bottom inset calculations for iOS and Android appropriately handle platform-specific UI behaviors.


52-65: LGTM! Comprehensive role selection with good UX.

The select implementation provides clear options, proper value binding, and visual feedback when filters are active.

Also applies to: 87-115


67-134: LGTM! Well-structured responsive layout with clear separation of concerns.

The filter and action button layout provides good UX with proper spacing, responsive behavior, and logical grouping.

src/components/ui/badge.tsx (3)

7-36: LGTM! Comprehensive variant system with proper web accessibility.

The badge and text variants provide good coverage with appropriate hover states, focus rings, and consistent color schemes.


45-47: LGTM! Proper TextClassContext integration for automatic text styling.

The context provider ensures child Text components automatically receive appropriate styling based on the badge variant.


38-49: LGTM! Well-structured component with proper TypeScript typing and composition support.

The component follows established patterns with good asChild support, proper exports, and clean implementation.

Also applies to: 51-52

src/containers/admin/user-detail.container.tsx (1)

174-202: Confirm Select value shape aligns with component API

value is passed an { value, label } object, but later SelectItem supplies only string values.
If the control expects a primitive (common in our UI kit), this will break selection & controlled-value behaviour. Double-check the prop contract or cast to string.

Comment on lines +5 to +25
function Separator({
className,
orientation = 'horizontal',
decorative = true,
...props
}: SeparatorPrimitive.RootProps & {
ref?: React.RefObject<SeparatorPrimitive.RootRef>;
}) {
return (
<SeparatorPrimitive.Root
decorative={decorative}
orientation={orientation}
className={cn(
'shrink-0 bg-border',
orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',
className
)}
{...props}
/>
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using React.forwardRef for proper ref handling.

The component accepts a ref prop but doesn't use React.forwardRef, which may not forward the ref correctly to the underlying primitive.

Apply this diff to properly handle ref forwarding:

-function Separator({
-  className,
-  orientation = 'horizontal',
-  decorative = true,
-  ...props
-}: SeparatorPrimitive.RootProps & {
-  ref?: React.RefObject<SeparatorPrimitive.RootRef>;
-}) {
+const Separator = React.forwardRef<
+  SeparatorPrimitive.RootRef,
+  SeparatorPrimitive.RootProps
+>(({ className, orientation = 'horizontal', decorative = true, ...props }, ref) => {
   return (
     <SeparatorPrimitive.Root
+      ref={ref}
       decorative={decorative}
       orientation={orientation}
       className={cn(
         'shrink-0 bg-border',
         orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',
         className
       )}
       {...props}
     />
   );
-}
+});
+
+Separator.displayName = 'Separator';
🤖 Prompt for AI Agents
In src/components/ui/separator.tsx around lines 5 to 25, the Separator component
accepts a ref prop but does not use React.forwardRef, so the ref is not
forwarded to the underlying SeparatorPrimitive.Root element. To fix this, wrap
the Separator function with React.forwardRef and pass the ref as the second
argument, then forward this ref to SeparatorPrimitive.Root via its ref prop.
Adjust the component signature accordingly to support ref forwarding properly.

Comment thread src/containers/admin/components/user-table-row.tsx Outdated
Comment thread src/containers/admin/user-detail.container.tsx
Comment on lines +441 to +450
<Label>Ban Reason *</Label>
<Input
value={formData.banReason}
onChangeText={(text) =>
userDetailModel$.setFormData({ banReason: text })
}
placeholder="Enter reason for ban"
multiline
numberOfLines={3}
/>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Enforce non-empty “Ban Reason” before submission

The dialog labels the field as mandatory (*), yet handleBanUser can be triggered while it’s empty. Validate in the model or disable the action until a reason is provided.

🤖 Prompt for AI Agents
In src/containers/admin/user-detail.container.tsx around lines 441 to 450, the
"Ban Reason" input is marked mandatory but the submission handler handleBanUser
allows empty values. To fix this, add validation in the userDetailModel$ to
check that banReason is non-empty before allowing submission, or disable the ban
action button until formData.banReason contains text. This ensures the user
cannot submit without providing a ban reason.

- Implement EmailModel for handling email verification and password reset functionalities.
- Create FormModel to manage user detail editing forms and track changes.
- Develop ModerationModel for user moderation actions including ban, unban, and delete operations.
- Introduce SessionModel for managing user sessions, including fetching, revoking, and displaying session details.
- Add UserModel for core user data operations, including fetching and updating user information and roles.
- Implement utility function getErrorMessage for standardized error handling across models.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (12)
src/utils/error.ts (1)

7-9: Consider enhancing error type handling.

The current implementation handles Error instances well, but could be more robust by also handling string errors and objects with message properties.

Consider this enhanced version:

 export function getErrorMessage(error: unknown, fallback: string): string {
-	return error instanceof Error ? error.message : fallback;
+	if (error instanceof Error) {
+		return error.message;
+	}
+	if (typeof error === 'string') {
+		return error;
+	}
+	if (error && typeof error === 'object' && 'message' in error && typeof error.message === 'string') {
+		return error.message;
+	}
+	return fallback;
 }
src/models/admin/user-detail/form.model.ts (2)

14-20: Use consistent role constants.

The default role value "user" is hardcoded but should use a constant for consistency with role validation elsewhere in the codebase.

+import { ROLE_OPTIONS } from "./user.model";
+
 constructor() {
 	this.obs = observable({
 		name: "",
 		email: "",
-		role: "user",
+		role: ROLE_OPTIONS[0].value,
 	});
 }

22-30: Consider adding input validation.

The setFormData method accepts any partial updates without validation. Consider adding validation for email format and role values to prevent invalid states.

 setFormData(
 	updates: Partial<{
 		name: string;
 		email: string;
 		role: string;
 	}>,
 ): void {
+	// Validate email format if provided
+	if (updates.email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(updates.email)) {
+		throw new Error('Invalid email format');
+	}
+	
+	// Validate role if provided
+	if (updates.role && !ROLE_OPTIONS.some(option => option.value === updates.role)) {
+		throw new Error('Invalid role value');
+	}
+	
 	this.obs.assign(updates);
 }
src/models/admin/user-detail/email.model.ts (3)

26-26: Simplify parameter destructuring.

The method uses object destructuring for a single parameter. Consider using a direct parameter for simplicity.

-async resendVerificationEmail({ email }: { email: string }): Promise<void> {
+async resendVerificationEmail(email: string): Promise<void> {

This would require updating the caller as well.


51-51: Simplify parameter destructuring.

Same suggestion as above - consider using a direct parameter for consistency and simplicity.

-async resetPassword({ email }: { email: string }): Promise<void> {
+async resetPassword(email: string): Promise<void> {

29-46: Consider extracting common error handling pattern.

Both async methods follow an identical error handling pattern. Consider extracting this to reduce code duplication.

You could create a private helper method:

private async executeEmailOperation<T>(
	statusKey: 'resendVerificationStatus' | 'resetPasswordStatus',
	operation: () => Promise<T>,
	successMessage: string,
	errorMessage: string
): Promise<void> {
	this.obs[statusKey].set("loading");
	
	try {
		const result = await operation();
		this.obs[statusKey].set("success");
		toast.success(successMessage);
		return result;
	} catch (error) {
		const errorMsg = getErrorMessage(error, errorMessage);
		this.obs[statusKey].set("error");
		toast.error(errorMsg);
		throw error;
	}
}
src/models/admin/user-detail/user.model.ts (3)

8-20: Enhance User interface type safety.

Consider making some fields more type-safe and adding JSDoc comments for clarity.

+/**
+ * Extended user interface with admin-specific fields
+ */
 export interface User {
 	id: string;
 	email: string;
 	name?: string;
 	banned?: boolean;
 	banReason?: string;
 	banExpires?: string;
 	lastLogin?: string;
-	role?: string;
+	role?: "user" | "admin";
 	emailVerified?: boolean;
 	createdAt: string;
 	updatedAt?: string;
 }

75-79: Improve type safety in user data transformation.

The type assertion as User could be avoided with better typing.

 this.obs.user.set({
 	...user,
 	createdAt: user.createdAt.toISOString(),
 	updatedAt: user.updatedAt?.toISOString(),
-} as User);
+});

The type assertion isn't needed if the User interface properly matches the API response structure.


129-132: Remove unnecessary type casting.

The role parameter is already typed as string in the method signature, so the type assertion is redundant.

 const { error } = await authClient.admin.setRole({
 	userId,
-	role: role as "user" | "admin",
+	role,
 });

If type safety is needed, validate the role value instead of casting:

if (role !== "user" && role !== "admin") {
	throw new Error("Invalid role value");
}
src/models/admin/user-detail/moderation.model.ts (3)

82-85: Extract magic numbers to constants.

The duration conversion uses magic numbers that could be extracted for better maintainability.

+const SECONDS_PER_DAY = 60 * 60 * 24;
+
 if (banDuration && banDuration !== "permanent") {
 	const days = parseInt(banDuration);
-	banExpiresIn = 60 * 60 * 24 * days; // Convert days to seconds
+	banExpiresIn = SECONDS_PER_DAY * days;
 }

70-76: Add validation for ban duration.

Consider validating that the ban duration is a valid option from BAN_DURATIONS.

 const { banReason, banDuration } = this.banFormData$.peek();

 if (!banReason.trim()) {
 	toast.error("Ban reason is required");
 	return;
 }

+if (banDuration && !BAN_DURATIONS.some(d => d.value === banDuration)) {
+	toast.error("Invalid ban duration selected");
+	return;
+}

157-161: Consider making resetBanForm public.

The resetBanForm method is private but might be useful for external components to reset the form state without performing a ban operation.

-private resetBanForm(): void {
+resetBanForm(): void {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 08b788d and b6f1663.

📒 Files selected for processing (10)
  • .github/copilot-instructions.md (3 hunks)
  • src/containers/admin/components/user-table-row.tsx (3 hunks)
  • src/containers/admin/user-detail.container.tsx (1 hunks)
  • src/models/admin/user-detail.model.ts (1 hunks)
  • src/models/admin/user-detail/email.model.ts (1 hunks)
  • src/models/admin/user-detail/form.model.ts (1 hunks)
  • src/models/admin/user-detail/moderation.model.ts (1 hunks)
  • src/models/admin/user-detail/session.model.ts (1 hunks)
  • src/models/admin/user-detail/user.model.ts (1 hunks)
  • src/utils/error.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/containers/admin/components/user-table-row.tsx
  • .github/copilot-instructions.md
  • src/containers/admin/user-detail.container.tsx
🧰 Additional context used
📓 Path-based instructions (1)
src/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)

src/**/*.{ts,tsx}: Use Platform.OS and process.env.EXPO_OS for conditional logic
Consider all platforms (iOS/Android/Web) when using native APIs

Files:

  • src/utils/error.ts
  • src/models/admin/user-detail/email.model.ts
  • src/models/admin/user-detail.model.ts
  • src/models/admin/user-detail/form.model.ts
  • src/models/admin/user-detail/moderation.model.ts
  • src/models/admin/user-detail/user.model.ts
  • src/models/admin/user-detail/session.model.ts
🧠 Learnings (4)
📚 Learning: applies to {src/lib/auth-client.ts,src/app/(auth)/**/*.tsx} : use authclient hooks for auth state ma...
Learnt from: CR
PR: fyndx/expo-universal-starter#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-28T12:52:53.959Z
Learning: Applies to {src/lib/auth-client.ts,src/app/(auth)/**/*.tsx} : Use authClient hooks for auth state management

Applied to files:

  • src/models/admin/user-detail/email.model.ts
  • src/models/admin/user-detail/user.model.ts
  • src/models/admin/user-detail/session.model.ts
📚 Learning: applies to {src/lib/auth-client.ts,src/app/(auth)/**/*.tsx} : use authclient.usesession() hook for s...
Learnt from: CR
PR: fyndx/expo-universal-starter#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-28T12:52:53.959Z
Learning: Applies to {src/lib/auth-client.ts,src/app/(auth)/**/*.tsx} : Use authClient.useSession() hook for session state

Applied to files:

  • src/models/admin/user-detail/session.model.ts
📚 Learning: applies to src/lib/auth-client.ts : auth client in src/lib/auth-client.ts with platform-specific plu...
Learnt from: CR
PR: fyndx/expo-universal-starter#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-28T12:52:53.959Z
Learning: Applies to src/lib/auth-client.ts : Auth client in src/lib/auth-client.ts with platform-specific plugins

Applied to files:

  • src/models/admin/user-detail/session.model.ts
📚 Learning: applies to src/lib/auth-client.ts : better auth requires platform-specific plugin setup for native v...
Learnt from: CR
PR: fyndx/expo-universal-starter#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-28T12:52:53.959Z
Learning: Applies to src/lib/auth-client.ts : Better Auth requires platform-specific plugin setup for native vs web storage

Applied to files:

  • src/models/admin/user-detail/session.model.ts
🧬 Code Graph Analysis (4)
src/models/admin/user-detail/email.model.ts (1)
src/utils/error.ts (1)
  • getErrorMessage (7-9)
src/models/admin/user-detail/moderation.model.ts (1)
src/utils/error.ts (1)
  • getErrorMessage (7-9)
src/models/admin/user-detail/user.model.ts (1)
src/utils/error.ts (1)
  • getErrorMessage (7-9)
src/models/admin/user-detail/session.model.ts (1)
src/utils/error.ts (1)
  • getErrorMessage (7-9)
🔇 Additional comments (4)
src/models/admin/user-detail/form.model.ts (1)

53-77: Excellent change detection logic.

The getChanges method correctly handles:

  • Trimming whitespace for accurate comparison
  • Handling optional/nullable fields with fallbacks
  • Automatically setting emailVerified to false when email changes
  • Returning structured data for different types of changes
src/models/admin/user-detail/user.model.ts (1)

51-87: Robust user fetching implementation.

The fetchUserById method correctly:

  • Sets loading states
  • Handles API errors appropriately
  • Validates that a user was found
  • Transforms date fields to ISO strings
  • Provides proper error feedback
src/models/admin/user-detail/moderation.model.ts (2)

9-17: Well-structured ban duration options.

The BAN_DURATIONS constant provides a good range of options with clear labels and values.


133-152: Excellent user deletion implementation.

The deleteUser method correctly:

  • Sets appropriate loading states
  • Handles API errors properly
  • Provides user feedback via toast
  • Navigates back after successful deletion
  • Follows consistent error handling patterns

Comment thread src/models/admin/user-detail.model.ts
Comment thread src/models/admin/user-detail/session.model.ts
@chakrihacker chakrihacker merged commit 392d955 into main Aug 4, 2025
1 check was pending
@chakrihacker chakrihacker deleted the feat/manage-user branch August 4, 2025 17:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant