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