mirror of
https://github.com/supabase/supabase.git
synced 2026-06-11 06:19:22 +08:00
Closes [FE-3173](https://linear.app/supabase/issue/FE-3173/add-keyboard-shortcuts-to-auth-users-page) ## Shortcuts | Key | Action | |---|---| | `Shift+F` | Focus search input | | `F C` | Reset filters | | `Shift+R` | Refresh users | | `S C` | Reset sort to default | | `Mod+A` | Toggle selection on all loaded users | | `Mod+Backspace` | Open bulk-delete confirm modal | | `Esc` | Clear row selection + cell focus | | `Esc` (panel open) | Close user details panel | | `↑` / `↓` | Move focus into the grid; native arrow nav after | | `Enter` (row focused) | Open user details panel | | `I U` | Open Create user modal | | `I I` | Open Send invitation modal | ## Test plan - [ ] `Shift+F` focuses the search input - [ ] `F C` clears keywords, user type, providers - [ ] In the search input: Esc clears value, Esc again blurs - [ ] `Shift+R` refreshes - [ ] `S C` resets sort; no-op at default - [ ] `Mod+A` toggles all loaded users when ≤ 20 are loaded - [ ] `Mod+Backspace` opens the delete confirmation when a selection exists - [ ] `↑` / `↓` from cold load enters the grid; subsequent arrows navigate cells - [ ] `Enter` on a focused row opens the panel - [ ] `Esc` with panel open closes it; without panel, clears selection + cell focus - [ ] `I U` opens the Create user modal - [ ] `I I` opens the Send invitation modal - [ ] All shortcuts appear in `Cmd+K` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Comprehensive keyboard shortcuts for user management (focus search, refresh, reset filters, bulk select, open delete modal, close panel). * Improved keyboard navigation in the user list with cell-level movement and Enter-to-select behavior. * Search input: Escape clears search/keywords and it can be focused programmatically. * Shortcut hint badges added to "Send invitation" / "Create new user" dropdown items. * **Chores** * Centralized refresh behavior for consistent interaction. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
93 lines
3.4 KiB
TypeScript
93 lines
3.4 KiB
TypeScript
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
|
import { ChevronDown, Mail, UserPlus } from 'lucide-react'
|
|
import { parseAsBoolean, useQueryState } from 'nuqs'
|
|
import { Button, DropdownMenu, DropdownMenuContent, DropdownMenuTrigger } from 'ui'
|
|
|
|
import CreateUserModal from './CreateUserModal'
|
|
import InviteUserModal from './InviteUserModal'
|
|
import { DropdownMenuItemTooltip } from '@/components/ui/DropdownMenuItemTooltip'
|
|
import { ShortcutBadge } from '@/components/ui/ShortcutBadge'
|
|
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
|
|
import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
|
|
import { SHORTCUT_IDS } from '@/state/shortcuts/registry'
|
|
|
|
export const AddUserDropdown = () => {
|
|
const showSendInvitation = useIsFeatureEnabled('authentication:show_send_invitation')
|
|
|
|
const { can: canInviteUsers } = useAsyncCheckPermissions(
|
|
PermissionAction.AUTH_EXECUTE,
|
|
'invite_user'
|
|
)
|
|
const { can: canCreateUsers } = useAsyncCheckPermissions(
|
|
PermissionAction.AUTH_EXECUTE,
|
|
'create_user'
|
|
)
|
|
|
|
const [inviteVisible, setInviteVisible] = useQueryState(
|
|
'invite',
|
|
parseAsBoolean.withDefault(false).withOptions({ history: 'push', clearOnDefault: true })
|
|
)
|
|
const [createVisible, setCreateVisible] = useQueryState(
|
|
'new',
|
|
parseAsBoolean.withDefault(false).withOptions({ history: 'push', clearOnDefault: true })
|
|
)
|
|
|
|
return (
|
|
<>
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button type="primary" iconRight={<ChevronDown size={14} strokeWidth={1.5} />}>
|
|
Add user
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent side="bottom" align="end" className="w-52">
|
|
{showSendInvitation && (
|
|
<DropdownMenuItemTooltip
|
|
className="gap-x-2"
|
|
disabled={!canInviteUsers}
|
|
onClick={() => {
|
|
if (canInviteUsers) setInviteVisible(true)
|
|
}}
|
|
tooltip={{
|
|
content: { side: 'left', text: 'You need additional permissions to invite users' },
|
|
}}
|
|
>
|
|
<Mail size={14} className="shrink-0" />
|
|
<p className="flex-1 min-w-0">Send invitation</p>
|
|
{canInviteUsers && (
|
|
<ShortcutBadge
|
|
shortcutId={SHORTCUT_IDS.AUTH_USERS_INVITE_USER}
|
|
className="shrink-0"
|
|
/>
|
|
)}
|
|
</DropdownMenuItemTooltip>
|
|
)}
|
|
|
|
<DropdownMenuItemTooltip
|
|
className="gap-x-2 pointer-events-auto!"
|
|
disabled={!canCreateUsers}
|
|
onClick={() => {
|
|
if (canCreateUsers) setCreateVisible(true)
|
|
}}
|
|
tooltip={{
|
|
content: { side: 'left', text: 'You need additional permissions to create users' },
|
|
}}
|
|
>
|
|
<UserPlus size={14} className="shrink-0" />
|
|
<p className="flex-1 min-w-0">Create new user</p>
|
|
{canCreateUsers && (
|
|
<ShortcutBadge
|
|
shortcutId={SHORTCUT_IDS.AUTH_USERS_CREATE_USER}
|
|
className="shrink-0"
|
|
/>
|
|
)}
|
|
</DropdownMenuItemTooltip>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
|
|
<InviteUserModal visible={inviteVisible} setVisible={setInviteVisible} />
|
|
<CreateUserModal visible={createVisible} setVisible={setCreateVisible} />
|
|
</>
|
|
)
|
|
}
|