edit-member.js 4.8 KB

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