managed-user-dropdown-button.tsx 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  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 onClick={onResendGroupInviteClick}>
  145. {t('resend_group_invite')}
  146. {isResendingGroupInvite ? (
  147. <Icon type="spinner" spin style={{ marginLeft: '5px' }} />
  148. ) : null}
  149. </MenuItemButton>
  150. ) : null}
  151. {userNotManaged ? (
  152. <MenuItemButton onClick={onResendManagedUserInviteClick}>
  153. {t('resend_managed_user_invite')}
  154. {isResendingManagedUserInvite ? (
  155. <Icon type="spinner" spin style={{ marginLeft: '5px' }} />
  156. ) : null}
  157. </MenuItemButton>
  158. ) : null}
  159. {user.enrollment?.managedBy ? (
  160. <MenuItemButton
  161. className="delete-user-action"
  162. data-testid="delete-user-action"
  163. onClick={onDeleteUserClick}
  164. >
  165. {t('delete_user')}
  166. </MenuItemButton>
  167. ) : user.isEntityAdmin ? (
  168. <MenuItem data-testid="no-actions-available">
  169. <span className="text-muted">{t('no_actions')}</span>
  170. </MenuItem>
  171. ) : (
  172. <MenuItemButton
  173. onClick={onRemoveFromGroup}
  174. className="delete-user-action"
  175. data-testid="remove-user-action"
  176. >
  177. {t('remove_from_group')}
  178. </MenuItemButton>
  179. )}
  180. </Dropdown.Menu>
  181. </Dropdown>
  182. </span>
  183. )
  184. }
  185. function MenuItemButton({
  186. children,
  187. onClick,
  188. className,
  189. ...buttonProps
  190. }: ComponentProps<'button'>) {
  191. return (
  192. <li role="presentation" className={className}>
  193. <button
  194. className="managed-user-menu-item-button"
  195. role="menuitem"
  196. onClick={onClick}
  197. {...buttonProps}
  198. >
  199. {children}
  200. </button>
  201. </li>
  202. )
  203. }