managed-user-dropdown-button.tsx 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. import {
  2. useState,
  3. type ComponentProps,
  4. useCallback,
  5. type Dispatch,
  6. type SetStateAction,
  7. } from 'react'
  8. import { useTranslation } from 'react-i18next'
  9. import { Dropdown, MenuItem } from 'react-bootstrap'
  10. import { User } from '../../../../../../types/group-management/user'
  11. import useAsync from '../../../../shared/hooks/use-async'
  12. import {
  13. type FetchError,
  14. postJSON,
  15. } from '../../../../infrastructure/fetch-json'
  16. import Icon from '../../../../shared/components/icon'
  17. import { ManagedUserAlert } from '../../utils/types'
  18. import { useGroupMembersContext } from '../../context/group-members-context'
  19. type resendInviteResponse = {
  20. success: boolean
  21. }
  22. type ManagedUserDropdownButtonProps = {
  23. user: User
  24. openOffboardingModalForUser: (user: User) => void
  25. groupId: string
  26. setManagedUserAlert: Dispatch<SetStateAction<ManagedUserAlert>>
  27. }
  28. export default function ManagedUserDropdownButton({
  29. user,
  30. openOffboardingModalForUser,
  31. groupId,
  32. setManagedUserAlert,
  33. }: ManagedUserDropdownButtonProps) {
  34. const { t } = useTranslation()
  35. const { removeMember } = useGroupMembersContext()
  36. const [isOpened, setIsOpened] = useState(false)
  37. const {
  38. runAsync: runResendManagedUserInviteAsync,
  39. isLoading: isResendingManagedUserInvite,
  40. } = useAsync<resendInviteResponse>()
  41. const {
  42. runAsync: runResendGroupInviteAsync,
  43. isLoading: isResendingGroupInvite,
  44. } = useAsync<resendInviteResponse>()
  45. const userNotManaged =
  46. !user.isEntityAdmin && !user.invite && !user.enrollment?.managedBy
  47. const userPending = user.invite
  48. const handleResendManagedUserInvite = useCallback(
  49. async user => {
  50. try {
  51. const result = await runResendManagedUserInviteAsync(
  52. postJSON(
  53. `/manage/groups/${groupId}/resendManagedUserInvite/${user._id}`
  54. )
  55. )
  56. if (result.success) {
  57. setManagedUserAlert({
  58. variant: 'resendManagedUserInviteSuccess',
  59. email: user.email,
  60. })
  61. setIsOpened(false)
  62. }
  63. } catch (err) {
  64. if ((err as FetchError)?.response?.status === 429) {
  65. setManagedUserAlert({
  66. variant: 'resendInviteTooManyRequests',
  67. email: user.email,
  68. })
  69. } else {
  70. setManagedUserAlert({
  71. variant: 'resendManagedUserInviteFailed',
  72. email: user.email,
  73. })
  74. }
  75. setIsOpened(false)
  76. }
  77. },
  78. [setManagedUserAlert, groupId, runResendManagedUserInviteAsync]
  79. )
  80. const handleResendGroupInvite = useCallback(
  81. async user => {
  82. try {
  83. await runResendGroupInviteAsync(
  84. postJSON(`/manage/groups/${groupId}/resendInvite/`, {
  85. body: {
  86. email: user.email,
  87. },
  88. })
  89. )
  90. setManagedUserAlert({
  91. variant: 'resendGroupInviteSuccess',
  92. email: user.email,
  93. })
  94. setIsOpened(false)
  95. } catch (err) {
  96. if ((err as FetchError)?.response?.status === 429) {
  97. setManagedUserAlert({
  98. variant: 'resendInviteTooManyRequests',
  99. email: user.email,
  100. })
  101. } else {
  102. setManagedUserAlert({
  103. variant: 'resendGroupInviteFailed',
  104. email: user.email,
  105. })
  106. }
  107. setIsOpened(false)
  108. }
  109. },
  110. [setManagedUserAlert, groupId, runResendGroupInviteAsync]
  111. )
  112. const onResendManagedUserInviteClick = () => {
  113. handleResendManagedUserInvite(user)
  114. }
  115. const onResendGroupInviteClick = () => {
  116. handleResendGroupInvite(user)
  117. }
  118. const onDeleteUserClick = () => {
  119. openOffboardingModalForUser(user)
  120. }
  121. const onRemoveFromGroup = () => {
  122. removeMember(user)
  123. }
  124. return (
  125. <span className="managed-user-actions">
  126. <Dropdown
  127. id={`managed-user-dropdown-${user.email}`}
  128. open={isOpened}
  129. onToggle={open => setIsOpened(open)}
  130. >
  131. <Dropdown.Toggle
  132. bsStyle={null}
  133. className="btn btn-link action-btn"
  134. noCaret
  135. >
  136. <i
  137. className="fa fa-ellipsis-v"
  138. aria-hidden="true"
  139. aria-label={t('actions')}
  140. />
  141. </Dropdown.Toggle>
  142. <Dropdown.Menu className="dropdown-menu-right managed-user-dropdown-menu">
  143. {userPending ? (
  144. <MenuItemButton
  145. onClick={onResendGroupInviteClick}
  146. data-testid="resend-group-invite-action"
  147. >
  148. {t('resend_group_invite')}
  149. {isResendingGroupInvite ? (
  150. <Icon type="spinner" spin style={{ marginLeft: '5px' }} />
  151. ) : null}
  152. </MenuItemButton>
  153. ) : null}
  154. {userNotManaged ? (
  155. <MenuItemButton
  156. onClick={onResendManagedUserInviteClick}
  157. data-testid="resend-managed-user-invite-action"
  158. >
  159. {t('resend_managed_user_invite')}
  160. {isResendingManagedUserInvite ? (
  161. <Icon type="spinner" spin style={{ marginLeft: '5px' }} />
  162. ) : null}
  163. </MenuItemButton>
  164. ) : null}
  165. {user.isEntityAdmin ? (
  166. <MenuItem data-testid="no-actions-available">
  167. <span className="text-muted">{t('no_actions')}</span>
  168. </MenuItem>
  169. ) : user.enrollment?.managedBy ? (
  170. <MenuItemButton
  171. className="delete-user-action"
  172. data-testid="delete-user-action"
  173. onClick={onDeleteUserClick}
  174. >
  175. {t('delete_user')}
  176. </MenuItemButton>
  177. ) : (
  178. <MenuItemButton
  179. onClick={onRemoveFromGroup}
  180. className="delete-user-action"
  181. data-testid="remove-user-action"
  182. >
  183. {t('remove_from_group')}
  184. </MenuItemButton>
  185. )}
  186. </Dropdown.Menu>
  187. </Dropdown>
  188. </span>
  189. )
  190. }
  191. function MenuItemButton({
  192. children,
  193. onClick,
  194. className,
  195. ...buttonProps
  196. }: ComponentProps<'button'>) {
  197. return (
  198. <li role="presentation" className={className}>
  199. <button
  200. className="managed-user-menu-item-button"
  201. role="menuitem"
  202. onClick={onClick}
  203. {...buttonProps}
  204. >
  205. {children}
  206. </button>
  207. </li>
  208. )
  209. }