Skip to content

Commit 712aeec

Browse files
improvement(admin): refine add user action
1 parent 1e2bfb2 commit 712aeec

1 file changed

Lines changed: 89 additions & 87 deletions

File tree

  • apps/sim/app/workspace/[workspaceId]/settings/components/admin

apps/sim/app/workspace/[workspaceId]/settings/components/admin/admin.tsx

Lines changed: 89 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client'
22

33
import { useEffect, useMemo, useRef, useState } from 'react'
4-
import { Badge, Button, ChipInput, ChipSelect, cn, Label, Search, Switch } from '@sim/emcn'
4+
import { Badge, Button, Chip, ChipInput, ChipSelect, cn, Label, Search, Switch } from '@sim/emcn'
55
import { getErrorMessage } from '@sim/utils/errors'
66
import { useQueryStates } from 'nuqs'
77
import type { MothershipEnvironment } from '@/lib/api/contracts'
@@ -14,6 +14,7 @@ import {
1414
import { useRecentImpersonations } from '@/app/workspace/[workspaceId]/settings/components/admin/use-recent-impersonations'
1515
import { SettingsEmptyState } from '@/app/workspace/[workspaceId]/settings/components/settings-empty-state'
1616
import { SettingsPanel } from '@/app/workspace/[workspaceId]/settings/components/settings-panel'
17+
import { SettingsSection } from '@/app/workspace/[workspaceId]/settings/components/settings-section/settings-section'
1718
import {
1819
type AdminUser,
1920
useAdminUsers,
@@ -371,99 +372,100 @@ export function Admin() {
371372

372373
<div className='h-px bg-[var(--border)]' />
373374

374-
<div className='flex flex-col gap-3'>
375-
<div className='flex items-center justify-between gap-3'>
376-
<p className='font-medium text-[var(--text-muted)] text-small'>User Management</p>
377-
<Button variant='primary' onClick={() => setIsAddUserOpen(true)}>
378-
Add user
379-
</Button>
380-
</div>
381-
<div className='flex gap-2'>
382-
<ChipInput
383-
icon={Search}
384-
value={searchInput}
385-
onChange={(e) => setSearchInput(e.target.value)}
386-
onKeyDown={(e) => e.key === 'Enter' && handleSearch()}
387-
placeholder='Search by email or paste a user ID...'
388-
className='min-w-0 flex-1'
389-
/>
390-
<Button variant='primary' onClick={handleSearch} disabled={usersLoading}>
391-
{usersLoading ? 'Searching...' : 'Search'}
392-
</Button>
393-
</div>
394-
395-
{usersError && (
396-
<p className='text-[var(--text-error)] text-small'>
397-
{getErrorMessage(usersError, 'Failed to fetch users')}
398-
</p>
399-
)}
375+
<SettingsSection
376+
label='User management'
377+
action={<Chip onClick={() => setIsAddUserOpen(true)}>Add user</Chip>}
378+
>
379+
<div className='flex flex-col gap-3'>
380+
<div className='flex gap-2'>
381+
<ChipInput
382+
icon={Search}
383+
value={searchInput}
384+
onChange={(e) => setSearchInput(e.target.value)}
385+
onKeyDown={(e) => e.key === 'Enter' && handleSearch()}
386+
placeholder='Search by email or paste a user ID...'
387+
className='min-w-0 flex-1'
388+
/>
389+
<Button variant='primary' onClick={handleSearch} disabled={usersLoading}>
390+
{usersLoading ? 'Searching...' : 'Search'}
391+
</Button>
392+
</div>
393+
394+
{usersError && (
395+
<p className='text-[var(--text-error)] text-small'>
396+
{getErrorMessage(usersError, 'Failed to fetch users')}
397+
</p>
398+
)}
400399

401-
{(setUserRole.error ||
402-
banUser.error ||
403-
unbanUser.error ||
404-
impersonateUser.error ||
405-
impersonationGuardError) && (
406-
<p className='text-[var(--text-error)] text-small'>
407-
{impersonationGuardError ||
408-
(setUserRole.error || banUser.error || unbanUser.error || impersonateUser.error)
409-
?.message ||
410-
'Action failed. Please try again.'}
411-
</p>
412-
)}
400+
{(setUserRole.error ||
401+
banUser.error ||
402+
unbanUser.error ||
403+
impersonateUser.error ||
404+
impersonationGuardError) && (
405+
<p className='text-[var(--text-error)] text-small'>
406+
{impersonationGuardError ||
407+
(setUserRole.error || banUser.error || unbanUser.error || impersonateUser.error)
408+
?.message ||
409+
'Action failed. Please try again.'}
410+
</p>
411+
)}
413412

414-
{searchQuery.length > 0 && usersData ? (
415-
<>
416-
<div className='flex flex-col gap-0.5'>
417-
{USER_TABLE_HEADER}
413+
{searchQuery.length > 0 && usersData ? (
414+
<>
415+
<div className='flex flex-col gap-0.5'>
416+
{USER_TABLE_HEADER}
418417

419-
{usersData.users.length === 0 && (
420-
<SettingsEmptyState variant='inline'>No users found.</SettingsEmptyState>
421-
)}
418+
{usersData.users.length === 0 && (
419+
<SettingsEmptyState variant='inline'>No users found.</SettingsEmptyState>
420+
)}
422421

423-
{usersData.users.map((u) => renderUserRow(u))}
424-
</div>
422+
{usersData.users.map((u) => renderUserRow(u))}
423+
</div>
425424

426-
{totalPages > 1 && (
427-
<div className='flex items-center justify-between text-[var(--text-secondary)] text-small'>
428-
<span>
429-
Page {currentPage} of {totalPages} ({usersData.total} users)
430-
</span>
431-
<div className='flex gap-1'>
432-
<Button
433-
variant='active'
434-
className='h-[28px] px-2 text-caption'
435-
onClick={() =>
436-
setAdminParams((prev) => ({
437-
offset: Math.max(0, prev.offset - PAGE_SIZE),
438-
}))
439-
}
440-
disabled={usersOffset === 0 || usersLoading}
441-
>
442-
Previous
443-
</Button>
444-
<Button
445-
variant='active'
446-
className='h-[28px] px-2 text-caption'
447-
onClick={() => setAdminParams((prev) => ({ offset: prev.offset + PAGE_SIZE }))}
448-
disabled={usersOffset + PAGE_SIZE >= (usersData?.total ?? 0) || usersLoading}
449-
>
450-
Next
451-
</Button>
425+
{totalPages > 1 && (
426+
<div className='flex items-center justify-between text-[var(--text-secondary)] text-small'>
427+
<span>
428+
Page {currentPage} of {totalPages} ({usersData.total} users)
429+
</span>
430+
<div className='flex gap-1'>
431+
<Button
432+
variant='active'
433+
className='h-[28px] px-2 text-caption'
434+
onClick={() =>
435+
setAdminParams((prev) => ({
436+
offset: Math.max(0, prev.offset - PAGE_SIZE),
437+
}))
438+
}
439+
disabled={usersOffset === 0 || usersLoading}
440+
>
441+
Previous
442+
</Button>
443+
<Button
444+
variant='active'
445+
className='h-[28px] px-2 text-caption'
446+
onClick={() =>
447+
setAdminParams((prev) => ({ offset: prev.offset + PAGE_SIZE }))
448+
}
449+
disabled={usersOffset + PAGE_SIZE >= (usersData?.total ?? 0) || usersLoading}
450+
>
451+
Next
452+
</Button>
453+
</div>
452454
</div>
455+
)}
456+
</>
457+
) : (
458+
searchQuery.length === 0 &&
459+
recentUsers &&
460+
recentUsers.length > 0 && (
461+
<div className='flex flex-col gap-0.5'>
462+
{USER_TABLE_HEADER}
463+
{recentUsers.map((u) => renderUserRow(u))}
453464
</div>
454-
)}
455-
</>
456-
) : (
457-
searchQuery.length === 0 &&
458-
recentUsers &&
459-
recentUsers.length > 0 && (
460-
<div className='flex flex-col gap-0.5'>
461-
{USER_TABLE_HEADER}
462-
{recentUsers.map((u) => renderUserRow(u))}
463-
</div>
464-
)
465-
)}
466-
</div>
465+
)
466+
)}
467+
</div>
468+
</SettingsSection>
467469
<AddUserModal
468470
open={isAddUserOpen}
469471
onOpenChange={setIsAddUserOpen}

0 commit comments

Comments
 (0)