offboard-managed-user-modal.tsx 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import { User } from '../../../../../../types/group-management/user'
  2. import {
  3. Alert,
  4. Button,
  5. ControlLabel,
  6. Form,
  7. FormControl,
  8. FormGroup,
  9. Modal,
  10. } from 'react-bootstrap'
  11. import AccessibleModal from '../../../../shared/components/accessible-modal'
  12. import Icon from '../../../../shared/components/icon'
  13. import { useState } from 'react'
  14. import useAsync from '../../../../shared/hooks/use-async'
  15. import { useTranslation } from 'react-i18next'
  16. import { useLocation } from '../../../../shared/hooks/use-location'
  17. import { FetchError, postJSON } from '../../../../infrastructure/fetch-json'
  18. type OffboardManagedUserModalProps = {
  19. user: User
  20. allMembers: User[]
  21. groupId: string
  22. onClose: () => void
  23. }
  24. export default function OffboardManagedUserModal({
  25. user,
  26. allMembers,
  27. groupId,
  28. onClose,
  29. }: OffboardManagedUserModalProps) {
  30. const { t } = useTranslation()
  31. const location = useLocation()
  32. const [selectedRecipientId, setSelectedRecipientId] = useState<string>()
  33. const [suppliedEmail, setSuppliedEmail] = useState<string>()
  34. const [error, setError] = useState<string>()
  35. const { isLoading, isSuccess, runAsync } = useAsync()
  36. const otherMembers = allMembers.filter(u => u._id !== user._id && !u.invite)
  37. const userFullName = user.last_name
  38. ? `${user.first_name || ''} ${user.last_name || ''}`
  39. : user.first_name
  40. const shouldEnableDeleteUserButton =
  41. suppliedEmail === user.email && !!selectedRecipientId
  42. const handleDeleteUserSubmit = (event: any) => {
  43. event.preventDefault()
  44. runAsync(
  45. postJSON(`/manage/groups/${groupId}/offboardManagedUser/${user._id}`, {
  46. body: {
  47. verificationEmail: suppliedEmail,
  48. transferProjectsToUserId: selectedRecipientId,
  49. },
  50. })
  51. .then(() => {
  52. location.reload()
  53. })
  54. .catch(err => {
  55. console.error(err)
  56. setError(
  57. err instanceof FetchError ? err.getUserFacingMessage() : err.message
  58. )
  59. })
  60. )
  61. }
  62. return (
  63. <AccessibleModal id={`delete-user-modal-${user._id}`} show onHide={onClose}>
  64. <Form id="delete-user-form" onSubmit={handleDeleteUserSubmit}>
  65. <Modal.Header>
  66. <Modal.Title>{t('delete_user')}</Modal.Title>
  67. </Modal.Header>
  68. <Modal.Body>
  69. <p>
  70. {t('about_to_delete_user_preamble', {
  71. userName: userFullName,
  72. userEmail: user.email,
  73. })}
  74. </p>
  75. <ul>
  76. <li>{t('they_lose_access_to_account')}</li>
  77. <li>{t('their_projects_will_be_transferred_to_another_user')}</li>
  78. <li>{t('you_will_be_able_to_reassign_subscription')}</li>
  79. </ul>
  80. <p>
  81. <span>{t('this_action_cannot_be_reversed')}</span>
  82. &nbsp;
  83. <a href="/learn/how-to/User_Management_in_Overleaf">
  84. {t('learn_more_about_managed_users')}
  85. </a>
  86. </p>
  87. <strong>{t('transfer_this_users_projects')}</strong>
  88. <p>{t('transfer_this_users_projects_description')}</p>
  89. <FormGroup>
  90. <ControlLabel htmlFor="recipient-select-input">
  91. {t('select_a_new_owner_for_projects')}
  92. </ControlLabel>
  93. <FormControl
  94. id="recipient-select-input"
  95. className="form-control"
  96. componentClass="select"
  97. aria-label={t('select_user')}
  98. required
  99. placeholder={t('choose_from_group_members')}
  100. value={selectedRecipientId || ''}
  101. onChange={(e: React.ChangeEvent<HTMLFormElement & FormControl>) =>
  102. setSelectedRecipientId(e.target.value)
  103. }
  104. >
  105. <option hidden disabled value="">
  106. {t('choose_from_group_members')}
  107. </option>
  108. {otherMembers.map(member => (
  109. <option value={member._id} key={member.email}>
  110. {member.email}
  111. </option>
  112. ))}
  113. </FormControl>
  114. </FormGroup>
  115. <p>
  116. <span>{t('all_projects_will_be_transferred_immediately')}</span>
  117. </p>
  118. <FormGroup>
  119. <ControlLabel htmlFor="supplied-email-input">
  120. {t('confirm_delete_user_type_email_address', {
  121. userName: userFullName,
  122. })}
  123. </ControlLabel>
  124. <FormControl
  125. id="supplied-email-input"
  126. type="email"
  127. aria-label={t('email')}
  128. onChange={(e: React.ChangeEvent<HTMLFormElement & FormControl>) =>
  129. setSuppliedEmail(e.target.value)
  130. }
  131. />
  132. </FormGroup>
  133. {error && <Alert bsStyle="danger">{error}</Alert>}
  134. </Modal.Body>
  135. <Modal.Footer>
  136. <FormGroup>
  137. <Button onClick={onClose}>{t('cancel')}</Button>
  138. <Button
  139. type="submit"
  140. bsStyle="danger"
  141. disabled={isLoading || isSuccess || !shouldEnableDeleteUserButton}
  142. >
  143. {isLoading ? (
  144. <>
  145. <Icon type="refresh" fw spin /> {t('deleting')}…
  146. </>
  147. ) : (
  148. t('delete_user')
  149. )}
  150. </Button>
  151. </FormGroup>
  152. </Modal.Footer>
  153. </Form>
  154. </AccessibleModal>
  155. )
  156. }