Skip to main content

useAdminUsers

Overview

useAdminUsers is a React hook for managing user accounts in the admin panel. It provides paginated user listing with integrated search, role, and status filtering (powered by useAdminFilters), CRUD mutations, and user statistics. The hook manages its own pagination state internally and automatically resets to page 1 when filters change.

Source: template/hooks/use-admin-users.ts

Signature / Parameters

function useAdminUsers(options?: UseAdminUsersOptions): UseAdminUsersReturn

UseAdminUsersOptions

ParameterTypeDefaultDescription
pagenumber1Initial page number
limitnumber10Items per page
searchstring''Initial search term (not commonly used; prefer the returned setSearchTerm)
rolestring''Initial role filter
statusstring''Initial status filter

Return Values

Data

PropertyTypeDescription
usersUserWithCount[]Array of users with item/comment counts
stats{ total: number; active: number; inactive: number }User count statistics

UserWithCount

interface UserWithCount {
id: string;
name: string | null;
email: string | null;
username?: string;
title?: string;
avatar?: string;
status: 'active' | 'inactive';
role: string;
roleName?: string;
created_at: string;
updated_at: string;
created_by?: string;
itemCount: number;
commentCount: number;
}

Loading States

PropertyTypeDescription
isLoadingbooleantrue on initial load
isFetchingbooleantrue when fetching including background refetch
isFilteringbooleantrue when loading or searching on the first page
isSubmittingbooleantrue when any mutation is pending
isSearchingbooleantrue when debounced search is pending (from useAdminFilters)

Pagination

PropertyTypeDescription
currentPagenumberCurrent page number
totalPagesnumberTotal number of pages
totalUsersnumberTotal users matching filters

Filter State

PropertyTypeDescription
searchTermstringCurrent search input value
debouncedSearchTermstringDebounced search value (used for API calls)
roleFilterstringCurrent role filter value
statusFilterstringCurrent status filter value
hasActiveSearchbooleantrue when search term meets minimum length
activeFilterCountnumberNumber of currently active filters
hasActiveFiltersbooleantrue when any filter is active

Actions

MethodSignatureDescription
createUser(data: CreateUserRequest) => Promise<boolean>Create a new user
updateUser(id: string, data: UpdateUserRequest) => Promise<boolean>Update a user
deleteUser(id: string) => Promise<boolean>Delete a user

CreateUserRequest / UpdateUserRequest

interface CreateUserRequest {
username: string;
name: string;
email: string;
password: string;
role: string;
title?: string;
avatar?: string;
}

interface UpdateUserRequest {
username?: string;
name?: string;
email?: string;
role?: string;
status?: 'active' | 'inactive';
title?: string;
avatar?: string;
}

Pagination Actions

MethodSignatureDescription
setCurrentPage(page: number) => voidSet the current page
handlePageChange(page: number) => voidAlias for setCurrentPage

Filter Actions

MethodSignatureDescription
setSearchTerm(term: string) => voidUpdate the search input value
handleSearch(term: string) => voidAlias for setSearchTerm
setRoleFilter(role: string) => voidSet the role filter
handleRoleFilter(role: string) => voidAlias for setRoleFilter
setStatusFilter(status: string) => voidSet the status filter
handleStatusFilter(status: string) => voidAlias for setStatusFilter
clearAllFilters() => voidReset all filters and search to defaults

Utility

MethodSignatureDescription
refetch() => voidRe-execute the users list query
refreshData() => voidInvalidate all user queries for fresh data

Implementation Details

  • Integrated filtering: Internally uses useAdminFilters<'active' | 'inactive'> for unified search, status, and multi-select (role) filter management with debounced search.
  • Debounce: Search has a 300ms debounce delay with a minimum 2-character threshold via useAdminFilters.
  • Auto page reset: A useEffect monitors debouncedSearchTerm, statusFilter, and roleFilter and resets pagination to page 1 when any changes (skips the initial mount using a useRef guard).
  • Query caching: 5-minute staleTime, 10-minute gcTime, 5-minute refetchInterval, 3 retries.
  • Placeholder data: keepPreviousData for smooth pagination transitions.
  • Separate stats query: Stats are fetched from /api/admin/users/stats with their own query key.
  • Cache invalidation: Mutations invalidate the entire ['admin-users'] query family.
  • Toast notifications: sonner toasts fire automatically on mutation success and error.

Query Keys

const usersQueryKeys = {
all: ['admin-users'],
lists: () => ['admin-users', 'list'],
list: (params) => ['admin-users', 'list', params],
stats: () => ['admin-users', 'stats'],
details: () => ['admin-users', 'detail'],
detail: (id) => ['admin-users', 'detail', id],
};

Usage Examples

User management page with integrated filters

import { useAdminUsers } from '@/hooks/use-admin-users';

function UsersPage() {
const {
users,
stats,
isLoading,
searchTerm,
setSearchTerm,
roleFilter,
setRoleFilter,
statusFilter,
setStatusFilter,
currentPage,
totalPages,
handlePageChange,
clearAllFilters,
hasActiveFilters,
activeFilterCount,
} = useAdminUsers({ limit: 25 });

return (
<div>
<StatsBar total={stats.total} active={stats.active} inactive={stats.inactive} />

<div className="filters">
<SearchInput value={searchTerm} onChange={setSearchTerm} />
<RoleSelect value={roleFilter} onChange={setRoleFilter} />
<StatusSelect value={statusFilter} onChange={setStatusFilter} />
{hasActiveFilters && (
<Button onClick={clearAllFilters}>
Clear Filters ({activeFilterCount})
</Button>
)}
</div>

<UsersTable users={users} isLoading={isLoading} />
<Pagination current={currentPage} total={totalPages} onChange={handlePageChange} />
</div>
);
}

Creating a user

const { createUser, isSubmitting } = useAdminUsers();

const handleSubmit = async (formData: CreateUserRequest) => {
const success = await createUser(formData);
if (success) closeModal();
};

Updating user status

const { updateUser } = useAdminUsers();

const handleToggleStatus = async (user: UserWithCount) => {
await updateUser(user.id, {
status: user.status === 'active' ? 'inactive' : 'active',
});
};