managed-user-dropdown-button.tsx 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  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. import getMeta from '@/utils/meta'
  20. type resendInviteResponse = {
  21. success: boolean
  22. }
  23. type ManagedUserDropdownButtonProps = {
  24. user: User
  25. openOffboardingModalForUser: (user: User) => void
  26. groupId: string
  27. setManagedUserAlert: Dispatch<SetStateAction<ManagedUserAlert>>
  28. }
  29. export default function ManagedUserDropdownButton({
  30. user,
  31. openOffboardingModalForUser,
  32. groupId,
  33. setManagedUserAlert,
  34. }: ManagedUserDropdownButtonProps) {
  35. const { t } = useTranslation()
  36. const { removeMember } = useGroupMembersContext()
  37. const [isOpened, setIsOpened] = useState(false)
  38. const {
  39. runAsync: runResendManagedUserInviteAsync,
  40. isLoading: isResendingManagedUserInvite,
  41. } = useAsync<resendInviteResponse>()
  42. const groupSSOActive = getMeta('ol-groupSSOActive')
  43. const ssoEnabledButNotAccepted = groupSSOActive && !user.enrollment?.sso
  44. const {
  45. runAsync: runResendLinkSSOInviteAsync,
  46. isLoading: isResendingSSOLinkInvite,
  47. } = useAsync<resendInviteResponse>()
  48. const {
  49. runAsync: runResendGroupInviteAsync,
  50. isLoading: isResendingGroupInvite,
  51. } = useAsync<resendInviteResponse>()
  52. const userNotManaged =
  53. !user.isEntityAdmin && !user.invite && !user.enrollment?.managedBy
  54. const userPending = user.invite
  55. const handleResendManagedUserInvite = useCallback(
  56. async user => {
  57. try {
  58. const result = await runResendManagedUserInviteAsync(
  59. postJSON(
  60. `/manage/groups/${groupId}/resendManagedUserInvite/${user._id}`
  61. )
  62. )
  63. if (result.success) {
  64. setManagedUserAlert({
  65. variant: 'resendManagedUserInviteSuccess',
  66. email: user.email,
  67. })
  68. setIsOpened(false)
  69. }
  70. } catch (err) {
  71. if ((err as FetchError)?.response?.status === 429) {
  72. setManagedUserAlert({
  73. variant: 'resendInviteTooManyRequests',
  74. email: user.email,
  75. })
  76. } else {
  77. setManagedUserAlert({
  78. variant: 'resendManagedUserInviteFailed',
  79. email: user.email,
  80. })
  81. }
  82. setIsOpened(false)
  83. }
  84. },
  85. [setManagedUserAlert, groupId, runResendManagedUserInviteAsync]
  86. )
  87. const handleResendLinkSSOInviteAsync = useCallback(
  88. async user => {
  89. try {
  90. const result = await runResendLinkSSOInviteAsync(
  91. postJSON(`/manage/groups/${groupId}/resendSSOLinkInvite/${user._id}`)
  92. )
  93. if (result.success) {
  94. setManagedUserAlert({
  95. variant: 'resendSSOLinkInviteSuccess',
  96. email: user.email,
  97. })
  98. setIsOpened(false)
  99. }
  100. } catch (err) {
  101. if ((err as FetchError)?.response?.status === 429) {
  102. setManagedUserAlert({
  103. variant: 'resendInviteTooManyRequests',
  104. email: user.email,
  105. })
  106. } else {
  107. setManagedUserAlert({
  108. variant: 'resendSSOLinkInviteFailed',
  109. email: user.email,
  110. })
  111. }
  112. setIsOpened(false)
  113. }
  114. },
  115. [setManagedUserAlert, groupId, runResendLinkSSOInviteAsync]
  116. )
  117. const handleResendGroupInvite = useCallback(
  118. async user => {
  119. try {
  120. await runResendGroupInviteAsync(
  121. postJSON(`/manage/groups/${groupId}/resendInvite/`, {
  122. body: {
  123. email: user.email,
  124. },
  125. })
  126. )
  127. setManagedUserAlert({
  128. variant: 'resendGroupInviteSuccess',
  129. email: user.email,
  130. })
  131. setIsOpened(false)
  132. } catch (err) {
  133. if ((err as FetchError)?.response?.status === 429) {
  134. setManagedUserAlert({
  135. variant: 'resendInviteTooManyRequests',
  136. email: user.email,
  137. })
  138. } else {
  139. setManagedUserAlert({
  140. variant: 'resendGroupInviteFailed',
  141. email: user.email,
  142. })
  143. }
  144. setIsOpened(false)
  145. }
  146. },
  147. [setManagedUserAlert, groupId, runResendGroupInviteAsync]
  148. )
  149. const onResendManagedUserInviteClick = () => {
  150. handleResendManagedUserInvite(user)
  151. }
  152. const onResendSSOLinkInviteClick = () => {
  153. handleResendLinkSSOInviteAsync(user)
  154. }
  155. const onResendGroupInviteClick = () => {
  156. handleResendGroupInvite(user)
  157. }
  158. const onDeleteUserClick = () => {
  159. openOffboardingModalForUser(user)
  160. }
  161. const onRemoveFromGroup = () => {
  162. removeMember(user)
  163. }
  164. return (
  165. <span className="managed-user-actions">
  166. <Dropdown
  167. id={`managed-user-dropdown-${user.email}`}
  168. open={isOpened}
  169. onToggle={open => setIsOpened(open)}
  170. >
  171. <Dropdown.Toggle
  172. bsStyle={null}
  173. className="btn btn-link action-btn"
  174. noCaret
  175. >
  176. <i
  177. className="fa fa-ellipsis-v"
  178. aria-hidden="true"
  179. aria-label={t('actions')}
  180. />
  181. </Dropdown.Toggle>
  182. <Dropdown.Menu className="dropdown-menu-right managed-user-dropdown-menu">
  183. {userPending ? (
  184. <MenuItemButton
  185. onClick={onResendGroupInviteClick}
  186. data-testid="resend-group-invite-action"
  187. >
  188. {t('resend_group_invite')}
  189. {isResendingGroupInvite ? (
  190. <Icon type="spinner" spin style={{ marginLeft: '5px' }} />
  191. ) : null}
  192. </MenuItemButton>
  193. ) : null}
  194. {userNotManaged ? (
  195. <MenuItemButton
  196. onClick={onResendManagedUserInviteClick}
  197. data-testid="resend-managed-user-invite-action"
  198. >
  199. {t('resend_managed_user_invite')}
  200. {isResendingManagedUserInvite ? (
  201. <Icon type="spinner" spin style={{ marginLeft: '5px' }} />
  202. ) : null}
  203. </MenuItemButton>
  204. ) : null}
  205. {ssoEnabledButNotAccepted && (
  206. <MenuItemButton
  207. onClick={onResendSSOLinkInviteClick}
  208. data-testid="resend-sso-link-invite-action"
  209. >
  210. {t('resend_link_sso')}
  211. {isResendingSSOLinkInvite ? (
  212. <Icon type="spinner" spin style={{ marginLeft: '5px' }} />
  213. ) : null}
  214. </MenuItemButton>
  215. )}
  216. {user.isEntityAdmin ? (
  217. <MenuItem data-testid="no-actions-available">
  218. <span className="text-muted">{t('no_actions')}</span>
  219. </MenuItem>
  220. ) : user.enrollment?.managedBy ? (
  221. <MenuItemButton
  222. className="delete-user-action"
  223. data-testid="delete-user-action"
  224. onClick={onDeleteUserClick}
  225. >
  226. {t('delete_user')}
  227. </MenuItemButton>
  228. ) : (
  229. <MenuItemButton
  230. onClick={onRemoveFromGroup}
  231. className="delete-user-action"
  232. data-testid="remove-user-action"
  233. >
  234. {t('remove_from_group')}
  235. </MenuItemButton>
  236. )}
  237. </Dropdown.Menu>
  238. </Dropdown>
  239. </span>
  240. )
  241. }
  242. function MenuItemButton({
  243. children,
  244. onClick,
  245. className,
  246. ...buttonProps
  247. }: ComponentProps<'button'>) {
  248. return (
  249. <li role="presentation" className={className}>
  250. <button
  251. className="managed-user-menu-item-button"
  252. role="menuitem"
  253. onClick={onClick}
  254. {...buttonProps}
  255. >
  256. {children}
  257. </button>
  258. </li>
  259. )
  260. }