edit-member.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import { useState, useEffect } from 'react'
  2. import PropTypes from 'prop-types'
  3. import { Trans, useTranslation } from 'react-i18next'
  4. import { useShareProjectContext } from './share-project-modal'
  5. import TransferOwnershipModal from './transfer-ownership-modal'
  6. import { removeMemberFromProject, updateMember } from '../utils/api'
  7. import { Button, Col, Form, FormControl, FormGroup } from 'react-bootstrap'
  8. import Tooltip from '../../../shared/components/tooltip'
  9. import Icon from '../../../shared/components/icon'
  10. import { useProjectContext } from '../../../shared/context/project-context'
  11. import { sendMB } from '../../../infrastructure/event-tracking'
  12. export default function EditMember({ member }) {
  13. const [privileges, setPrivileges] = useState(member.privileges)
  14. const [confirmingOwnershipTransfer, setConfirmingOwnershipTransfer] =
  15. useState(false)
  16. // update the local state if the member's privileges change externally
  17. useEffect(() => {
  18. setPrivileges(member.privileges)
  19. }, [member.privileges])
  20. const { updateProject, monitorRequest } = useShareProjectContext()
  21. const { _id: projectId, members } = useProjectContext()
  22. function handleSubmit(event) {
  23. event.preventDefault()
  24. if (privileges === 'owner') {
  25. setConfirmingOwnershipTransfer(true)
  26. } else {
  27. monitorRequest(() =>
  28. updateMember(projectId, member, {
  29. privilegeLevel: privileges,
  30. })
  31. ).then(() => {
  32. updateProject({
  33. members: members.map(item =>
  34. item._id === member._id ? { ...item, privileges } : item
  35. ),
  36. })
  37. })
  38. }
  39. }
  40. if (confirmingOwnershipTransfer) {
  41. return (
  42. <TransferOwnershipModal
  43. member={member}
  44. cancel={() => setConfirmingOwnershipTransfer(false)}
  45. />
  46. )
  47. }
  48. return (
  49. <Form horizontal id="share-project-form" onSubmit={handleSubmit}>
  50. <FormGroup className="project-member">
  51. <Col xs={7}>
  52. <FormControl.Static>{member.email}</FormControl.Static>
  53. </Col>
  54. <Col xs={3}>
  55. <SelectPrivilege
  56. value={privileges}
  57. handleChange={event => setPrivileges(event.target.value)}
  58. />
  59. </Col>
  60. <Col xs={2}>
  61. {privileges === member.privileges ? (
  62. <RemoveMemberAction member={member} />
  63. ) : (
  64. <ChangePrivilegesActions
  65. handleReset={() => setPrivileges(member.privileges)}
  66. />
  67. )}
  68. </Col>
  69. </FormGroup>
  70. </Form>
  71. )
  72. }
  73. EditMember.propTypes = {
  74. member: PropTypes.shape({
  75. _id: PropTypes.string.isRequired,
  76. email: PropTypes.string.isRequired,
  77. privileges: PropTypes.string.isRequired,
  78. }),
  79. }
  80. function SelectPrivilege({ value, handleChange }) {
  81. const { t } = useTranslation()
  82. return (
  83. <FormControl
  84. componentClass="select"
  85. className="privileges"
  86. bsSize="sm"
  87. value={value}
  88. onChange={handleChange}
  89. >
  90. <option value="owner">{t('owner')}</option>
  91. <option value="readAndWrite">{t('can_edit')}</option>
  92. <option value="readOnly">{t('read_only')}</option>
  93. </FormControl>
  94. )
  95. }
  96. SelectPrivilege.propTypes = {
  97. value: PropTypes.string.isRequired,
  98. handleChange: PropTypes.func.isRequired,
  99. }
  100. function RemoveMemberAction({ member }) {
  101. const { t } = useTranslation()
  102. const { updateProject, monitorRequest } = useShareProjectContext()
  103. const { _id: projectId, members, invites } = useProjectContext()
  104. function handleClick(event) {
  105. event.preventDefault()
  106. monitorRequest(() => removeMemberFromProject(projectId, member)).then(
  107. () => {
  108. const updatedMembers = members.filter(existing => existing !== member)
  109. updateProject({
  110. members: updatedMembers,
  111. })
  112. sendMB('collaborator-removed', {
  113. project_id: projectId,
  114. current_collaborators_amount: updatedMembers.length,
  115. current_invites_amount: invites.length,
  116. })
  117. }
  118. )
  119. }
  120. return (
  121. <FormControl.Static className="text-center">
  122. <Tooltip
  123. id="remove-collaborator"
  124. description={<Trans i18nKey="remove_collaborator" />}
  125. overlayProps={{ placement: 'bottom' }}
  126. >
  127. <Button
  128. type="button"
  129. bsStyle="link"
  130. onClick={handleClick}
  131. className="remove-button"
  132. aria-label={t('remove_collaborator')}
  133. >
  134. <Icon type="times" />
  135. </Button>
  136. </Tooltip>
  137. </FormControl.Static>
  138. )
  139. }
  140. RemoveMemberAction.propTypes = {
  141. member: PropTypes.shape({
  142. _id: PropTypes.string.isRequired,
  143. email: PropTypes.string.isRequired,
  144. privileges: PropTypes.string.isRequired,
  145. }),
  146. }
  147. function ChangePrivilegesActions({ handleReset }) {
  148. return (
  149. <div className="text-center">
  150. <Button type="submit" bsSize="sm" bsStyle="primary">
  151. <Trans i18nKey="change_or_cancel-change" />
  152. </Button>
  153. <div className="text-sm">
  154. <Trans i18nKey="change_or_cancel-or" />
  155. &nbsp;
  156. <Button type="button" className="btn-inline-link" onClick={handleReset}>
  157. <Trans i18nKey="change_or_cancel-cancel" />
  158. </Button>
  159. </div>
  160. </div>
  161. )
  162. }
  163. ChangePrivilegesActions.propTypes = {
  164. handleReset: PropTypes.func.isRequired,
  165. }