invite.jsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. import { useCallback } from 'react'
  2. import PropTypes from 'prop-types'
  3. import { useShareProjectContext } from './share-project-modal'
  4. import { useTranslation } from 'react-i18next'
  5. import MemberPrivileges from './member-privileges'
  6. import { resendInvite, revokeInvite } from '../utils/api'
  7. import { useProjectContext } from '@/shared/context/project-context'
  8. import { sendMB } from '@/infrastructure/event-tracking'
  9. import OLRow from '@/features/ui/components/ol/ol-row'
  10. import OLCol from '@/features/ui/components/ol/ol-col'
  11. import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
  12. import OLButton from '@/features/ui/components/ol/ol-button'
  13. import MaterialIcon from '@/shared/components/material-icon'
  14. export default function Invite({ invite, isProjectOwner }) {
  15. const { t } = useTranslation()
  16. return (
  17. <OLRow className="project-invite">
  18. <OLCol xs={8}>
  19. <div>{invite.email}</div>
  20. <div className="small">
  21. {t('invite_not_accepted')}
  22. .&nbsp;
  23. {isProjectOwner && <ResendInvite invite={invite} />}
  24. </div>
  25. </OLCol>
  26. <OLCol xs={3} className="text-end">
  27. <MemberPrivileges privileges={invite.privileges} />
  28. </OLCol>
  29. {isProjectOwner && (
  30. <OLCol xs={1} className="text-center">
  31. <RevokeInvite invite={invite} />
  32. </OLCol>
  33. )}
  34. </OLRow>
  35. )
  36. }
  37. Invite.propTypes = {
  38. invite: PropTypes.object.isRequired,
  39. isProjectOwner: PropTypes.bool.isRequired,
  40. }
  41. function ResendInvite({ invite }) {
  42. const { t } = useTranslation()
  43. const { monitorRequest, setError, inFlight } = useShareProjectContext()
  44. const { _id: projectId } = useProjectContext()
  45. // const buttonRef = useRef(null)
  46. //
  47. const handleClick = useCallback(
  48. () =>
  49. monitorRequest(() => resendInvite(projectId, invite))
  50. .catch(error => {
  51. if (error?.response?.status === 404) {
  52. setError('invite_expired')
  53. }
  54. if (error?.response?.status === 429) {
  55. setError('invite_resend_limit_hit')
  56. }
  57. })
  58. .finally(() => {
  59. // NOTE: disabled as react-bootstrap v0.33.1 isn't forwarding the ref to the `button`
  60. // if (buttonRef.current) {
  61. // buttonRef.current.blur()
  62. // }
  63. document.activeElement.blur()
  64. }),
  65. [invite, monitorRequest, projectId, setError]
  66. )
  67. return (
  68. <OLButton
  69. variant="link"
  70. className="btn-inline-link"
  71. onClick={handleClick}
  72. disabled={inFlight}
  73. // ref={buttonRef}
  74. >
  75. {t('resend')}
  76. </OLButton>
  77. )
  78. }
  79. ResendInvite.propTypes = {
  80. invite: PropTypes.object.isRequired,
  81. }
  82. function RevokeInvite({ invite }) {
  83. const { t } = useTranslation()
  84. const { updateProject, monitorRequest } = useShareProjectContext()
  85. const { _id: projectId, invites, members } = useProjectContext()
  86. function handleClick(event) {
  87. event.preventDefault()
  88. monitorRequest(() => revokeInvite(projectId, invite)).then(() => {
  89. const updatedInvites = invites.filter(existing => existing !== invite)
  90. updateProject({
  91. invites: updatedInvites,
  92. })
  93. sendMB('collaborator-invite-revoked', {
  94. project_id: projectId,
  95. current_invites_amount: updatedInvites.length,
  96. current_collaborators_amount: members.length,
  97. })
  98. })
  99. }
  100. return (
  101. <OLTooltip
  102. id="revoke-invite"
  103. description={t('revoke_invite')}
  104. overlayProps={{ placement: 'bottom' }}
  105. >
  106. <OLButton
  107. variant="link"
  108. onClick={handleClick}
  109. aria-label={t('revoke')}
  110. className="btn-inline-link text-decoration-none"
  111. >
  112. <MaterialIcon type="clear" />
  113. </OLButton>
  114. </OLTooltip>
  115. )
  116. }
  117. RevokeInvite.propTypes = {
  118. invite: PropTypes.object.isRequired,
  119. }