diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/admin/admin.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/admin/admin.tsx index 2e856c39f9..5161bb3c5d 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/admin/admin.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/admin/admin.tsx @@ -31,7 +31,8 @@ export function Admin() { const [workflowId, setWorkflowId] = useState('') const [usersOffset, setUsersOffset] = useState(0) - const [usersEnabled, setUsersEnabled] = useState(false) + const [searchInput, setSearchInput] = useState('') + const [searchQuery, setSearchQuery] = useState('') const [banUserId, setBanUserId] = useState(null) const [banReason, setBanReason] = useState('') @@ -39,8 +40,12 @@ export function Admin() { data: usersData, isLoading: usersLoading, error: usersError, - refetch: refetchUsers, - } = useAdminUsers(usersOffset, PAGE_SIZE, usersEnabled) + } = useAdminUsers(usersOffset, PAGE_SIZE, searchQuery) + + const handleSearch = () => { + setUsersOffset(0) + setSearchQuery(searchInput.trim()) + } const totalPages = useMemo( () => Math.ceil((usersData?.total ?? 0) / PAGE_SIZE), @@ -62,14 +67,6 @@ export function Admin() { ) } - const handleLoadUsers = () => { - if (usersEnabled) { - refetchUsers() - } else { - setUsersEnabled(true) - } - } - const pendingUserIds = useMemo(() => { const ids = new Set() if (setUserRole.isPending && (setUserRole.variables as { userId?: string })?.userId) @@ -136,10 +133,16 @@ export function Admin() {
-
-

User Management

-
@@ -164,9 +167,9 @@ export function Admin() {
)} - {usersData && ( + {searchQuery.length > 0 && usersData && ( <> -
+
Name Email @@ -176,7 +179,7 @@ export function Admin() {
{usersData.users.length === 0 && ( -
+
No users found.
)} diff --git a/apps/sim/hooks/queries/admin-users.ts b/apps/sim/hooks/queries/admin-users.ts index 69b74dfbaa..6f9f6bba73 100644 --- a/apps/sim/hooks/queries/admin-users.ts +++ b/apps/sim/hooks/queries/admin-users.ts @@ -7,7 +7,8 @@ const logger = createLogger('AdminUsersQuery') export const adminUserKeys = { all: ['adminUsers'] as const, lists: () => [...adminUserKeys.all, 'list'] as const, - list: (offset: number, limit: number) => [...adminUserKeys.lists(), offset, limit] as const, + list: (offset: number, limit: number, searchQuery: string) => + [...adminUserKeys.lists(), offset, limit, searchQuery] as const, } interface AdminUser { @@ -24,31 +25,59 @@ interface AdminUsersResponse { total: number } -async function fetchAdminUsers(offset: number, limit: number): Promise { +const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i + +function mapUser(u: { + id: string + name: string + email: string + role?: string | null + banned?: boolean | null + banReason?: string | null +}): AdminUser { + return { + id: u.id, + name: u.name || '', + email: u.email, + role: u.role ?? 'user', + banned: u.banned ?? false, + banReason: u.banReason ?? null, + } +} + +async function fetchAdminUsers( + offset: number, + limit: number, + searchQuery: string +): Promise { + if (UUID_REGEX.test(searchQuery.trim())) { + const { data, error } = await client.admin.getUser({ query: { id: searchQuery.trim() } }) + if (error) throw new Error(error.message ?? 'Failed to fetch user') + if (!data) return { users: [], total: 0 } + return { users: [mapUser(data)], total: 1 } + } + const { data, error } = await client.admin.listUsers({ - query: { limit, offset }, + query: { + limit, + offset, + searchField: 'email', + searchValue: searchQuery, + searchOperator: 'contains', + }, }) - if (error) { - throw new Error(error.message ?? 'Failed to fetch users') - } + if (error) throw new Error(error.message ?? 'Failed to fetch users') return { - users: (data?.users ?? []).map((u) => ({ - id: u.id, - name: u.name || '', - email: u.email, - role: u.role ?? 'user', - banned: u.banned ?? false, - banReason: u.banReason ?? null, - })), + users: (data?.users ?? []).map(mapUser), total: data?.total ?? 0, } } -export function useAdminUsers(offset: number, limit: number, enabled: boolean) { +export function useAdminUsers(offset: number, limit: number, searchQuery: string) { return useQuery({ - queryKey: adminUserKeys.list(offset, limit), - queryFn: () => fetchAdminUsers(offset, limit), - enabled, + queryKey: adminUserKeys.list(offset, limit, searchQuery), + queryFn: () => fetchAdminUsers(offset, limit, searchQuery), + enabled: searchQuery.length > 0, staleTime: 30 * 1000, placeholderData: keepPreviousData, })