2021-06-23 05:37:08 -04:00
|
|
|
import { useCallback } from 'react'
|
2021-03-12 05:23:46 -05:00
|
|
|
import PropTypes from 'prop-types'
|
2021-06-25 04:13:17 -04:00
|
|
|
import { useShareProjectContext } from './share-project-modal'
|
2021-03-12 05:23:46 -05:00
|
|
|
import Icon from '../../../shared/components/icon'
|
2022-05-18 09:46:10 -04:00
|
|
|
import { Button, Col, Row } from 'react-bootstrap'
|
|
|
|
import Tooltip from '../../../shared/components/tooltip'
|
2023-10-19 04:33:26 -04:00
|
|
|
import { useTranslation } from 'react-i18next'
|
2021-03-12 05:23:46 -05:00
|
|
|
import MemberPrivileges from './member-privileges'
|
|
|
|
import { resendInvite, revokeInvite } from '../utils/api'
|
2021-06-25 04:13:17 -04:00
|
|
|
import { useProjectContext } from '../../../shared/context/project-context'
|
2023-07-31 11:27:12 -04:00
|
|
|
import { sendMB } from '../../../infrastructure/event-tracking'
|
2021-03-12 05:23:46 -05:00
|
|
|
|
2022-07-08 01:03:14 -04:00
|
|
|
export default function Invite({ invite, isProjectOwner }) {
|
2023-10-19 04:33:26 -04:00
|
|
|
const { t } = useTranslation()
|
2021-03-12 05:23:46 -05:00
|
|
|
return (
|
|
|
|
<Row className="project-invite">
|
|
|
|
<Col xs={7}>
|
|
|
|
<div>{invite.email}</div>
|
|
|
|
|
|
|
|
<div className="small">
|
2023-10-19 04:33:26 -04:00
|
|
|
{t('invite_not_accepted')}
|
2021-03-12 05:23:46 -05:00
|
|
|
.
|
2022-07-08 01:03:14 -04:00
|
|
|
{isProjectOwner && <ResendInvite invite={invite} />}
|
2021-03-12 05:23:46 -05:00
|
|
|
</div>
|
|
|
|
</Col>
|
|
|
|
|
|
|
|
<Col xs={3} className="text-left">
|
|
|
|
<MemberPrivileges privileges={invite.privileges} />
|
|
|
|
</Col>
|
|
|
|
|
2022-07-08 01:03:14 -04:00
|
|
|
{isProjectOwner && (
|
2021-03-12 05:23:46 -05:00
|
|
|
<Col xs={2} className="text-center">
|
|
|
|
<RevokeInvite invite={invite} />
|
|
|
|
</Col>
|
|
|
|
)}
|
|
|
|
</Row>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
Invite.propTypes = {
|
|
|
|
invite: PropTypes.object.isRequired,
|
2022-07-08 01:03:14 -04:00
|
|
|
isProjectOwner: PropTypes.bool.isRequired,
|
2021-03-12 05:23:46 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
function ResendInvite({ invite }) {
|
2023-10-19 04:33:26 -04:00
|
|
|
const { t } = useTranslation()
|
2024-07-12 05:00:26 -04:00
|
|
|
const { monitorRequest, setError, inFlight } = useShareProjectContext()
|
|
|
|
const { _id: projectId } = useProjectContext()
|
2021-03-12 05:23:46 -05:00
|
|
|
|
|
|
|
// const buttonRef = useRef(null)
|
|
|
|
//
|
|
|
|
const handleClick = useCallback(
|
|
|
|
() =>
|
2024-07-03 04:54:42 -04:00
|
|
|
monitorRequest(() => resendInvite(projectId, invite))
|
2024-07-12 05:00:26 -04:00
|
|
|
.catch(error => {
|
|
|
|
if (error?.response?.status === 404) {
|
|
|
|
setError('invite_expired')
|
|
|
|
}
|
|
|
|
if (error?.response?.status === 429) {
|
|
|
|
setError('invite_resend_limit_hit')
|
|
|
|
}
|
2024-07-03 04:54:42 -04:00
|
|
|
})
|
|
|
|
.finally(() => {
|
|
|
|
// NOTE: disabled as react-bootstrap v0.33.1 isn't forwarding the ref to the `button`
|
|
|
|
// if (buttonRef.current) {
|
|
|
|
// buttonRef.current.blur()
|
|
|
|
// }
|
|
|
|
document.activeElement.blur()
|
|
|
|
}),
|
2024-07-12 05:00:26 -04:00
|
|
|
[invite, monitorRequest, projectId, setError]
|
2021-03-12 05:23:46 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Button
|
|
|
|
bsStyle="link"
|
|
|
|
className="btn-inline-link"
|
|
|
|
onClick={handleClick}
|
2024-07-12 05:00:26 -04:00
|
|
|
disabled={inFlight}
|
2021-03-12 05:23:46 -05:00
|
|
|
// ref={buttonRef}
|
|
|
|
>
|
2023-10-19 04:33:26 -04:00
|
|
|
{t('resend')}
|
2021-03-12 05:23:46 -05:00
|
|
|
</Button>
|
|
|
|
)
|
|
|
|
}
|
2022-05-18 09:46:10 -04:00
|
|
|
|
2021-03-12 05:23:46 -05:00
|
|
|
ResendInvite.propTypes = {
|
2021-04-27 03:52:58 -04:00
|
|
|
invite: PropTypes.object.isRequired,
|
2021-03-12 05:23:46 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
function RevokeInvite({ invite }) {
|
2021-05-12 06:28:03 -04:00
|
|
|
const { t } = useTranslation()
|
2021-03-12 05:23:46 -05:00
|
|
|
const { updateProject, monitorRequest } = useShareProjectContext()
|
2023-07-31 11:27:12 -04:00
|
|
|
const { _id: projectId, invites, members } = useProjectContext()
|
2021-03-12 05:23:46 -05:00
|
|
|
|
|
|
|
function handleClick(event) {
|
|
|
|
event.preventDefault()
|
|
|
|
|
2022-01-10 10:47:10 -05:00
|
|
|
monitorRequest(() => revokeInvite(projectId, invite)).then(() => {
|
2023-07-31 11:27:12 -04:00
|
|
|
const updatedInvites = invites.filter(existing => existing !== invite)
|
2021-03-12 05:23:46 -05:00
|
|
|
updateProject({
|
2023-07-31 11:27:12 -04:00
|
|
|
invites: updatedInvites,
|
|
|
|
})
|
|
|
|
sendMB('collaborator-invite-revoked', {
|
|
|
|
project_id: projectId,
|
|
|
|
current_invites_amount: updatedInvites.length,
|
|
|
|
current_collaborators_amount: members.length,
|
2021-03-12 05:23:46 -05:00
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
2022-05-18 09:46:10 -04:00
|
|
|
<Tooltip
|
|
|
|
id="revoke-invite"
|
2023-10-19 04:33:26 -04:00
|
|
|
description={t('revoke_invite')}
|
2022-05-18 09:46:10 -04:00
|
|
|
overlayProps={{ placement: 'bottom' }}
|
2021-03-12 05:23:46 -05:00
|
|
|
>
|
|
|
|
<Button
|
|
|
|
type="button"
|
|
|
|
bsStyle="link"
|
|
|
|
onClick={handleClick}
|
2021-05-12 06:28:03 -04:00
|
|
|
aria-label={t('revoke')}
|
2021-03-12 05:23:46 -05:00
|
|
|
className="btn-inline-link"
|
|
|
|
>
|
|
|
|
<Icon type="times" />
|
|
|
|
</Button>
|
2022-05-18 09:46:10 -04:00
|
|
|
</Tooltip>
|
2021-03-12 05:23:46 -05:00
|
|
|
)
|
|
|
|
}
|
2022-05-18 09:46:10 -04:00
|
|
|
|
2021-03-12 05:23:46 -05:00
|
|
|
RevokeInvite.propTypes = {
|
2021-04-27 03:52:58 -04:00
|
|
|
invite: PropTypes.object.isRequired,
|
2021-03-12 05:23:46 -05:00
|
|
|
}
|