edit-member.js 4.8 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 [
  21. confirmingOwnershipTransfer,
  22. setConfirmingOwnershipTransfer,
  23. ] = useState(false)
  24. // update the local state if the member's privileges change externally
  25. useEffect(() => {
  26. setPrivileges(member.privileges)
  27. }, [member.privileges])
  28. const { updateProject, monitorRequest } = useShareProjectContext()
  29. const project = useProjectContext()
  30. function handleSubmit(event) {
  31. event.preventDefault()
  32. if (privileges === 'owner') {
  33. setConfirmingOwnershipTransfer(true)
  34. } else {
  35. monitorRequest(() =>
  36. updateMember(project, member, {
  37. privilegeLevel: privileges,
  38. })
  39. ).then(() => {
  40. updateProject({
  41. members: project.members.map(item =>
  42. item._id === member._id ? { ...item, privileges } : item
  43. ),
  44. })
  45. })
  46. }
  47. }
  48. if (confirmingOwnershipTransfer) {
  49. return (
  50. <TransferOwnershipModal
  51. member={member}
  52. cancel={() => setConfirmingOwnershipTransfer(false)}
  53. />
  54. )
  55. }
  56. return (
  57. <Form horizontal id="share-project-form" onSubmit={handleSubmit}>
  58. <FormGroup className="project-member">
  59. <Col xs={7}>
  60. <FormControl.Static>{member.email}</FormControl.Static>
  61. </Col>
  62. <Col xs={3}>
  63. <SelectPrivilege
  64. value={privileges}
  65. handleChange={event => setPrivileges(event.target.value)}
  66. />
  67. </Col>
  68. <Col xs={2}>
  69. {privileges === member.privileges ? (
  70. <RemoveMemberAction member={member} />
  71. ) : (
  72. <ChangePrivilegesActions
  73. handleReset={() => setPrivileges(member.privileges)}
  74. />
  75. )}
  76. </Col>
  77. </FormGroup>
  78. </Form>
  79. )
  80. }
  81. EditMember.propTypes = {
  82. member: PropTypes.shape({
  83. _id: PropTypes.string.isRequired,
  84. email: PropTypes.string.isRequired,
  85. privileges: PropTypes.string.isRequired,
  86. }),
  87. }
  88. function SelectPrivilege({ value, handleChange }) {
  89. const { t } = useTranslation()
  90. return (
  91. <FormControl
  92. componentClass="select"
  93. className="privileges"
  94. bsSize="sm"
  95. value={value}
  96. onChange={handleChange}
  97. >
  98. <option value="owner">{t('owner')}</option>
  99. <option value="readAndWrite">{t('can_edit')}</option>
  100. <option value="readOnly">{t('read_only')}</option>
  101. </FormControl>
  102. )
  103. }
  104. SelectPrivilege.propTypes = {
  105. value: PropTypes.string.isRequired,
  106. handleChange: PropTypes.func.isRequired,
  107. }
  108. function RemoveMemberAction({ member }) {
  109. const { t } = useTranslation()
  110. const { updateProject, monitorRequest } = useShareProjectContext()
  111. const project = useProjectContext()
  112. function handleClick(event) {
  113. event.preventDefault()
  114. monitorRequest(() => removeMemberFromProject(project, member)).then(() => {
  115. updateProject({
  116. members: project.members.filter(existing => existing !== member),
  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. }