overleaf/services/web/frontend/js/features/group-management/components/members-table/dropdown-button.tsx
Alexandre Bourdin 665f5e37c4 Merge pull request #15348 from overleaf/ab-decoupling-refactor-members-table
[web] Modular members management table refactoring

GitOrigin-RevId: 9a3a00a32970e78e5b43b3a68621a627c490c728
2023-10-30 09:04:43 +00:00

278 lines
7.8 KiB
TypeScript

import {
useState,
type ComponentProps,
useCallback,
type Dispatch,
type SetStateAction,
} from 'react'
import { useTranslation } from 'react-i18next'
import { Dropdown, MenuItem } from 'react-bootstrap'
import { User } from '../../../../../../types/group-management/user'
import useAsync from '@/shared/hooks/use-async'
import { type FetchError, postJSON } from '@/infrastructure/fetch-json'
import Icon from '@/shared/components/icon'
import { ManagedUserAlert } from '../../utils/types'
import { useGroupMembersContext } from '../../context/group-members-context'
import getMeta from '@/utils/meta'
type resendInviteResponse = {
success: boolean
}
type ManagedUserDropdownButtonProps = {
user: User
openOffboardingModalForUser: (user: User) => void
groupId: string
setManagedUserAlert: Dispatch<SetStateAction<ManagedUserAlert>>
}
export default function DropdownButton({
user,
openOffboardingModalForUser,
groupId,
setManagedUserAlert,
}: ManagedUserDropdownButtonProps) {
const { t } = useTranslation()
const { removeMember } = useGroupMembersContext()
const [isOpened, setIsOpened] = useState(false)
const {
runAsync: runResendManagedUserInviteAsync,
isLoading: isResendingManagedUserInvite,
} = useAsync<resendInviteResponse>()
const groupSSOActive = getMeta('ol-groupSSOActive')
const ssoEnabledButNotAccepted = groupSSOActive && !user.enrollment?.sso
const {
runAsync: runResendLinkSSOInviteAsync,
isLoading: isResendingSSOLinkInvite,
} = useAsync<resendInviteResponse>()
const {
runAsync: runResendGroupInviteAsync,
isLoading: isResendingGroupInvite,
} = useAsync<resendInviteResponse>()
const userNotManaged =
!user.isEntityAdmin && !user.invite && !user.enrollment?.managedBy
const userPending = user.invite
const handleResendManagedUserInvite = useCallback(
async user => {
try {
const result = await runResendManagedUserInviteAsync(
postJSON(
`/manage/groups/${groupId}/resendManagedUserInvite/${user._id}`
)
)
if (result.success) {
setManagedUserAlert({
variant: 'resendManagedUserInviteSuccess',
email: user.email,
})
setIsOpened(false)
}
} catch (err) {
if ((err as FetchError)?.response?.status === 429) {
setManagedUserAlert({
variant: 'resendInviteTooManyRequests',
email: user.email,
})
} else {
setManagedUserAlert({
variant: 'resendManagedUserInviteFailed',
email: user.email,
})
}
setIsOpened(false)
}
},
[setManagedUserAlert, groupId, runResendManagedUserInviteAsync]
)
const handleResendLinkSSOInviteAsync = useCallback(
async user => {
try {
const result = await runResendLinkSSOInviteAsync(
postJSON(`/manage/groups/${groupId}/resendSSOLinkInvite/${user._id}`)
)
if (result.success) {
setManagedUserAlert({
variant: 'resendSSOLinkInviteSuccess',
email: user.email,
})
setIsOpened(false)
}
} catch (err) {
if ((err as FetchError)?.response?.status === 429) {
setManagedUserAlert({
variant: 'resendInviteTooManyRequests',
email: user.email,
})
} else {
setManagedUserAlert({
variant: 'resendSSOLinkInviteFailed',
email: user.email,
})
}
setIsOpened(false)
}
},
[setManagedUserAlert, groupId, runResendLinkSSOInviteAsync]
)
const handleResendGroupInvite = useCallback(
async user => {
try {
await runResendGroupInviteAsync(
postJSON(`/manage/groups/${groupId}/resendInvite/`, {
body: {
email: user.email,
},
})
)
setManagedUserAlert({
variant: 'resendGroupInviteSuccess',
email: user.email,
})
setIsOpened(false)
} catch (err) {
if ((err as FetchError)?.response?.status === 429) {
setManagedUserAlert({
variant: 'resendInviteTooManyRequests',
email: user.email,
})
} else {
setManagedUserAlert({
variant: 'resendGroupInviteFailed',
email: user.email,
})
}
setIsOpened(false)
}
},
[setManagedUserAlert, groupId, runResendGroupInviteAsync]
)
const onResendManagedUserInviteClick = () => {
handleResendManagedUserInvite(user)
}
const onResendSSOLinkInviteClick = () => {
handleResendLinkSSOInviteAsync(user)
}
const onResendGroupInviteClick = () => {
handleResendGroupInvite(user)
}
const onDeleteUserClick = () => {
openOffboardingModalForUser(user)
}
const onRemoveFromGroup = () => {
removeMember(user)
}
return (
<span className="managed-user-actions">
<Dropdown
id={`managed-user-dropdown-${user.email}`}
open={isOpened}
onToggle={open => setIsOpened(open)}
>
<Dropdown.Toggle
bsStyle={null}
className="btn btn-link action-btn"
noCaret
>
<i
className="fa fa-ellipsis-v"
aria-hidden="true"
aria-label={t('actions')}
/>
</Dropdown.Toggle>
<Dropdown.Menu className="dropdown-menu-right managed-user-dropdown-menu">
{userPending ? (
<MenuItemButton
onClick={onResendGroupInviteClick}
data-testid="resend-group-invite-action"
>
{t('resend_group_invite')}
{isResendingGroupInvite ? (
<Icon type="spinner" spin style={{ marginLeft: '5px' }} />
) : null}
</MenuItemButton>
) : null}
{userNotManaged ? (
<MenuItemButton
onClick={onResendManagedUserInviteClick}
data-testid="resend-managed-user-invite-action"
>
{t('resend_managed_user_invite')}
{isResendingManagedUserInvite ? (
<Icon type="spinner" spin style={{ marginLeft: '5px' }} />
) : null}
</MenuItemButton>
) : null}
{ssoEnabledButNotAccepted && (
<MenuItemButton
onClick={onResendSSOLinkInviteClick}
data-testid="resend-sso-link-invite-action"
>
{t('resend_link_sso')}
{isResendingSSOLinkInvite ? (
<Icon type="spinner" spin style={{ marginLeft: '5px' }} />
) : null}
</MenuItemButton>
)}
{user.isEntityAdmin ? (
<MenuItem data-testid="no-actions-available">
<span className="text-muted">{t('no_actions')}</span>
</MenuItem>
) : user.enrollment?.managedBy ? (
<MenuItemButton
className="delete-user-action"
data-testid="delete-user-action"
onClick={onDeleteUserClick}
>
{t('delete_user')}
</MenuItemButton>
) : (
<MenuItemButton
onClick={onRemoveFromGroup}
className="delete-user-action"
data-testid="remove-user-action"
>
{t('remove_from_group')}
</MenuItemButton>
)}
</Dropdown.Menu>
</Dropdown>
</span>
)
}
function MenuItemButton({
children,
onClick,
className,
...buttonProps
}: ComponentProps<'button'>) {
return (
<li role="presentation" className={className}>
<button
className="managed-user-menu-item-button"
role="menuitem"
onClick={onClick}
{...buttonProps}
>
{children}
</button>
</li>
)
}