edit-member.js 4.9 KB

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