| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- import { PermissionAction } from '@supabase/shared-types/out/constants'
- import {
- parseAsArrayOf,
- parseAsBoolean,
- parseAsString,
- parseAsStringEnum,
- useQueryState,
- } from 'nuqs'
- import { Dispatch, RefObject, SetStateAction, useEffect, useState } from 'react'
- import type { CellKeyboardEvent, DataGridHandle } from 'react-data-grid'
- import { MAX_BULK_DELETE } from './Users.constants'
- import type { User } from '@/data/auth/users-infinite-query'
- import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
- import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
- import { SHORTCUT_IDS } from '@/state/shortcuts/registry'
- import { useShortcut } from '@/state/shortcuts/useShortcut'
- interface UseAuthUsersShortcutsParams {
- gridRef: RefObject<DataGridHandle | null>
- searchInputRef: RefObject<HTMLInputElement | null>
- users: User[]
- selectedUsers: Set<unknown>
- setSelectedUsers: Dispatch<SetStateAction<Set<unknown>>>
- setSearch: Dispatch<SetStateAction<string>>
- onRefresh: () => void
- }
- interface GridCellKeyDownArgs {
- mode: 'SELECT' | 'EDIT'
- row: unknown
- rowIdx: number
- }
- interface UseAuthUsersShortcutsResult {
- onCellKeyDown: (args: GridCellKeyDownArgs, event: CellKeyboardEvent) => void
- showDeleteModal: boolean
- setShowDeleteModal: Dispatch<SetStateAction<boolean>>
- }
- export function useAuthUsersShortcuts({
- gridRef,
- searchInputRef,
- users,
- selectedUsers,
- setSelectedUsers,
- setSearch,
- onRefresh,
- }: UseAuthUsersShortcutsParams): UseAuthUsersShortcutsResult {
- const [hasCellSelected, setHasCellSelected] = useState(false)
- const [showDeleteModal, setShowDeleteModal] = useState(false)
- const [selectedId, setSelectedId] = useQueryState(
- 'show',
- parseAsString.withOptions({ history: 'push', clearOnDefault: true })
- )
- const [, setFilterKeywords] = useQueryState('keywords', { defaultValue: '' })
- const [, setFilterUserType] = useQueryState(
- 'userType',
- parseAsStringEnum(['all', 'verified', 'unverified', 'anonymous']).withDefault('all')
- )
- const [, setSelectedProviders] = useQueryState(
- 'providers',
- parseAsArrayOf(parseAsString, ',').withDefault([])
- )
- const [sortByValue, setSortByValue] = useQueryState('sortBy', {
- defaultValue: 'created_at:desc',
- })
- const [, setInviteVisible] = useQueryState(
- 'invite',
- parseAsBoolean.withDefault(false).withOptions({ history: 'push', clearOnDefault: true })
- )
- const [, setCreateVisible] = useQueryState(
- 'new',
- parseAsBoolean.withDefault(false).withOptions({ history: 'push', clearOnDefault: true })
- )
- const { can: canCreateUsers } = useAsyncCheckPermissions(
- PermissionAction.AUTH_EXECUTE,
- 'create_user'
- )
- const { can: canInviteUsers } = useAsyncCheckPermissions(
- PermissionAction.AUTH_EXECUTE,
- 'invite_user'
- )
- const showSendInvitation = useIsFeatureEnabled('authentication:show_send_invitation')
- useEffect(() => {
- const el = gridRef.current?.element
- if (!el) return
- const onFocusIn = () => setHasCellSelected(true)
- const onFocusOut = (event: FocusEvent) => {
- if (!el.contains(event.relatedTarget as Node | null)) {
- setHasCellSelected(false)
- }
- }
- el.addEventListener('focusin', onFocusIn)
- el.addEventListener('focusout', onFocusOut)
- return () => {
- el.removeEventListener('focusin', onFocusIn)
- el.removeEventListener('focusout', onFocusOut)
- }
- }, [gridRef])
- useShortcut(
- SHORTCUT_IDS.LIST_PAGE_FOCUS_SEARCH,
- () => {
- searchInputRef.current?.focus()
- searchInputRef.current?.select()
- },
- { label: 'Search users' }
- )
- useShortcut(SHORTCUT_IDS.LIST_PAGE_RESET_FILTERS, () => {
- setSearch('')
- setFilterKeywords('')
- setFilterUserType('all')
- setSelectedProviders([])
- })
- useShortcut(SHORTCUT_IDS.AUTH_USERS_REFRESH, onRefresh)
- useShortcut(SHORTCUT_IDS.AUTH_USERS_CLEAR_SORT, () => setSortByValue('created_at:desc'), {
- enabled: sortByValue !== 'created_at:desc',
- })
- useShortcut(
- SHORTCUT_IDS.AUTH_USERS_TOGGLE_ALL_SELECTION,
- () => {
- if (selectedUsers.size === users.length) {
- setSelectedUsers(new Set([]))
- } else {
- setSelectedUsers(new Set(users.map((u) => u.id)))
- }
- },
- { enabled: users.length > 0 && users.length <= MAX_BULK_DELETE }
- )
- useShortcut(SHORTCUT_IDS.AUTH_USERS_DELETE_SELECTED, () => setShowDeleteModal(true), {
- enabled: selectedUsers.size > 0,
- })
- useShortcut(
- SHORTCUT_IDS.AUTH_USERS_EXIT_SELECTION,
- () => {
- setSelectedUsers(new Set([]))
- setHasCellSelected(false)
- ;(document.activeElement as HTMLElement | null)?.blur()
- },
- { enabled: !selectedId && (selectedUsers.size > 0 || hasCellSelected) }
- )
- useShortcut(SHORTCUT_IDS.AUTH_USERS_CLOSE_PANEL, () => setSelectedId(null), {
- enabled: !!selectedId,
- })
- const startGridNavigation = () => {
- if (users.length === 0) return
- gridRef.current?.selectCell({ idx: 1, rowIdx: 0 })
- }
- useShortcut(SHORTCUT_IDS.AUTH_USERS_START_NAV_DOWN, startGridNavigation, {
- enabled: !hasCellSelected && users.length > 0,
- })
- useShortcut(SHORTCUT_IDS.AUTH_USERS_START_NAV_UP, startGridNavigation, {
- enabled: !hasCellSelected && users.length > 0,
- })
- useShortcut(SHORTCUT_IDS.AUTH_USERS_CREATE_USER, () => setCreateVisible(true), {
- enabled: canCreateUsers,
- })
- useShortcut(SHORTCUT_IDS.AUTH_USERS_INVITE_USER, () => setInviteVisible(true), {
- enabled: canInviteUsers && showSendInvitation,
- })
- const onCellKeyDown = (args: GridCellKeyDownArgs, event: CellKeyboardEvent) => {
- if (args.mode !== 'SELECT' || event.key !== 'Enter') return
- const id = (args.row as { id?: unknown } | null | undefined)?.id
- if (typeof id !== 'string') return
- setSelectedId(id)
- gridRef.current?.scrollToCell({ idx: 0, rowIdx: args.rowIdx })
- }
- return { onCellKeyDown, showDeleteModal, setShowDeleteModal }
- }
|