diff --git a/services/web/frontend/js/features/share-project-modal/components/add-collaborators.js b/services/web/frontend/js/features/share-project-modal/components/add-collaborators.js index a0684a6b4a..b99859b5e6 100644 --- a/services/web/frontend/js/features/share-project-modal/components/add-collaborators.js +++ b/services/web/frontend/js/features/share-project-modal/components/add-collaborators.js @@ -8,6 +8,7 @@ import { resendInvite, sendInvite } from '../utils/api' import { useUserContacts } from '../hooks/use-user-contacts' import useIsMounted from '../../../shared/hooks/use-is-mounted' import { useProjectContext } from '../../../shared/context/project-context' +import { sendMB } from '../../../infrastructure/event-tracking' export default function AddCollaborators() { const [privileges, setPrivileges] = useState('readAndWrite') @@ -82,6 +83,15 @@ export default function AddCollaborators() { } else { data = await sendInvite(projectId, email, privileges) } + + sendMB('collaborator-invited', { + project_id: projectId, + // invitation is only populated on successful invite, meaning that for paywall and other cases this will be null + successful_invite: !!data.invite, + users_updated: !!(data.users || data.user), + current_collaborators_amount: members.length, + current_invites_amount: invites.length, + }) } catch (error) { setInFlight(false) setError( diff --git a/services/web/frontend/js/features/share-project-modal/components/edit-member.js b/services/web/frontend/js/features/share-project-modal/components/edit-member.js index 00d82e94eb..205d55c7a6 100644 --- a/services/web/frontend/js/features/share-project-modal/components/edit-member.js +++ b/services/web/frontend/js/features/share-project-modal/components/edit-member.js @@ -8,6 +8,7 @@ import { Button, Col, Form, FormControl, FormGroup } from 'react-bootstrap' import Tooltip from '../../../shared/components/tooltip' import Icon from '../../../shared/components/icon' import { useProjectContext } from '../../../shared/context/project-context' +import { sendMB } from '../../../infrastructure/event-tracking' export default function EditMember({ member }) { const [privileges, setPrivileges] = useState(member.privileges) @@ -112,15 +113,21 @@ SelectPrivilege.propTypes = { function RemoveMemberAction({ member }) { const { t } = useTranslation() const { updateProject, monitorRequest } = useShareProjectContext() - const { _id: projectId, members } = useProjectContext() + const { _id: projectId, members, invites } = useProjectContext() function handleClick(event) { event.preventDefault() monitorRequest(() => removeMemberFromProject(projectId, member)).then( () => { + const updatedMembers = members.filter(existing => existing !== member) updateProject({ - members: members.filter(existing => existing !== member), + members: updatedMembers, + }) + sendMB('collaborator-removed', { + project_id: projectId, + current_collaborators_amount: updatedMembers.length, + current_invites_amount: invites.length, }) } ) diff --git a/services/web/frontend/js/features/share-project-modal/components/invite.js b/services/web/frontend/js/features/share-project-modal/components/invite.js index 03e009bebe..42c1fc8a74 100644 --- a/services/web/frontend/js/features/share-project-modal/components/invite.js +++ b/services/web/frontend/js/features/share-project-modal/components/invite.js @@ -8,6 +8,7 @@ import { Trans, useTranslation } from 'react-i18next' import MemberPrivileges from './member-privileges' import { resendInvite, revokeInvite } from '../utils/api' import { useProjectContext } from '../../../shared/context/project-context' +import { sendMB } from '../../../infrastructure/event-tracking' export default function Invite({ invite, isProjectOwner }) { return ( @@ -77,14 +78,20 @@ ResendInvite.propTypes = { function RevokeInvite({ invite }) { const { t } = useTranslation() const { updateProject, monitorRequest } = useShareProjectContext() - const { _id: projectId, invites } = useProjectContext() + const { _id: projectId, invites, members } = useProjectContext() function handleClick(event) { event.preventDefault() monitorRequest(() => revokeInvite(projectId, invite)).then(() => { + const updatedInvites = invites.filter(existing => existing !== invite) updateProject({ - invites: invites.filter(existing => existing !== invite), + invites: updatedInvites, + }) + sendMB('collaborator-invite-revoked', { + project_id: projectId, + current_invites_amount: updatedInvites.length, + current_collaborators_amount: members.length, }) }) } diff --git a/services/web/frontend/js/features/share-project-modal/components/link-sharing.js b/services/web/frontend/js/features/share-project-modal/components/link-sharing.js index 512c1154e7..3fd796494c 100644 --- a/services/web/frontend/js/features/share-project-modal/components/link-sharing.js +++ b/services/web/frontend/js/features/share-project-modal/components/link-sharing.js @@ -10,6 +10,7 @@ import CopyLink from '../../../shared/components/copy-link' import { useProjectContext } from '../../../shared/context/project-context' import * as eventTracking from '../../../infrastructure/event-tracking' import { useUserContext } from '../../../shared/context/user-context' +import { sendMB } from '../../../infrastructure/event-tracking' import { getJSON } from '../../../infrastructure/fetch-json' import useAbortController from '../../../shared/hooks/use-abort-controller' @@ -24,6 +25,9 @@ export default function LinkSharing({ canAddCollaborators }) { const setAccessLevel = useCallback( newPublicAccessLevel => { setInflight(true) + sendMB('link-sharing-click-off', { + project_id: projectId, + }) monitorRequest(() => setProjectAccessLevel(projectId, newPublicAccessLevel) ) @@ -43,7 +47,11 @@ export default function LinkSharing({ canAddCollaborators }) { // Private (with token-access available) case 'private': return ( - + ) // Token-based access @@ -76,7 +84,7 @@ LinkSharing.propTypes = { canAddCollaborators: PropTypes.bool, } -function PrivateSharing({ setAccessLevel, inflight }) { +function PrivateSharing({ setAccessLevel, inflight, projectId }) { return ( @@ -88,7 +96,7 @@ function PrivateSharing({ setAccessLevel, inflight }) { className="btn-inline-link" onClick={() => { setAccessLevel('tokenBased') - eventTracking.sendMB('link-sharing-click') + eventTracking.sendMB('link-sharing-click', { projectId }) }} disabled={inflight} > @@ -104,6 +112,7 @@ function PrivateSharing({ setAccessLevel, inflight }) { PrivateSharing.propTypes = { setAccessLevel: PropTypes.func.isRequired, inflight: PropTypes.bool, + projectId: PropTypes.string, } function TokenBasedSharing({ setAccessLevel, inflight, canAddCollaborators }) { diff --git a/services/web/frontend/js/features/share-project-modal/components/share-project-modal.js b/services/web/frontend/js/features/share-project-modal/components/share-project-modal.js index 45c1186bf7..c11ed8785d 100644 --- a/services/web/frontend/js/features/share-project-modal/components/share-project-modal.js +++ b/services/web/frontend/js/features/share-project-modal/components/share-project-modal.js @@ -58,9 +58,10 @@ const ShareProjectModal = React.memo(function ShareProjectModal({ if (show) { sendMB('share-modal-opened', { splitTestVariant: splitTestVariants['null-test-share-modal'], + project_id: project._id, }) } - }, [splitTestVariants, show]) + }, [splitTestVariants, project._id, show]) // reset error when the modal is opened useEffect(() => {