clone-project-modal-content.jsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. /* eslint-disable jsx-a11y/no-autofocus */
  2. import PropTypes from 'prop-types'
  3. import { useCallback, useMemo, useState } from 'react'
  4. import { useTranslation } from 'react-i18next'
  5. import { postJSON } from '../../../infrastructure/fetch-json'
  6. import { CloneProjectTag } from './clone-project-tag'
  7. import {
  8. OLModalBody,
  9. OLModalFooter,
  10. OLModalHeader,
  11. OLModalTitle,
  12. } from '@/features/ui/components/ol/ol-modal'
  13. import Notification from '@/shared/components/notification'
  14. import OLForm from '@/features/ui/components/ol/ol-form'
  15. import OLFormGroup from '@/features/ui/components/ol/ol-form-group'
  16. import OLFormControl from '@/features/ui/components/ol/ol-form-control'
  17. import OLFormLabel from '@/features/ui/components/ol/ol-form-label'
  18. import OLButton from '@/features/ui/components/ol/ol-button'
  19. export default function CloneProjectModalContent({
  20. handleHide,
  21. inFlight,
  22. setInFlight,
  23. handleAfterCloned,
  24. projectId,
  25. projectName,
  26. projectTags,
  27. }) {
  28. const { t } = useTranslation()
  29. const [error, setError] = useState()
  30. const [clonedProjectName, setClonedProjectName] = useState(
  31. `${projectName} (Copy)`
  32. )
  33. const [clonedProjectTags, setClonedProjectTags] = useState(projectTags)
  34. // valid if the cloned project has a name
  35. const valid = useMemo(
  36. () => clonedProjectName.trim().length > 0,
  37. [clonedProjectName]
  38. )
  39. // form submission: clone the project if the name is valid
  40. const handleSubmit = event => {
  41. event.preventDefault()
  42. if (!valid) {
  43. return
  44. }
  45. setError(false)
  46. setInFlight(true)
  47. // clone the project
  48. postJSON(`/project/${projectId}/clone`, {
  49. body: {
  50. projectName: clonedProjectName,
  51. tags: clonedProjectTags.map(tag => ({ id: tag._id })),
  52. },
  53. })
  54. .then(data => {
  55. // open the cloned project
  56. handleAfterCloned(data, clonedProjectTags)
  57. })
  58. .catch(({ response, data }) => {
  59. if (response?.status === 400) {
  60. setError(data.message)
  61. } else {
  62. setError(true)
  63. }
  64. })
  65. .finally(() => {
  66. setInFlight(false)
  67. })
  68. }
  69. const removeTag = useCallback(tag => {
  70. setClonedProjectTags(value => value.filter(item => item._id !== tag._id))
  71. }, [])
  72. return (
  73. <>
  74. <OLModalHeader closeButton>
  75. <OLModalTitle>{t('copy_project')}</OLModalTitle>
  76. </OLModalHeader>
  77. <OLModalBody>
  78. <OLForm id="clone-project-form" onSubmit={handleSubmit}>
  79. <OLFormGroup controlId="clone-project-form-name">
  80. <OLFormLabel>{t('new_name')}</OLFormLabel>
  81. <OLFormControl
  82. type="text"
  83. placeholder={t('new_project_name')}
  84. required
  85. value={clonedProjectName}
  86. onChange={event => setClonedProjectName(event.target.value)}
  87. autoFocus
  88. />
  89. </OLFormGroup>
  90. {clonedProjectTags.length > 0 && (
  91. <OLFormGroup
  92. controlId="clone-project-tags-list"
  93. className="clone-project-tag"
  94. >
  95. <OLFormLabel>{t('tags')}: </OLFormLabel>
  96. <div role="listbox" id="clone-project-tags-list">
  97. {clonedProjectTags.map(tag => (
  98. <CloneProjectTag
  99. key={tag._id}
  100. tag={tag}
  101. removeTag={removeTag}
  102. />
  103. ))}
  104. </div>
  105. </OLFormGroup>
  106. )}
  107. </OLForm>
  108. {error && (
  109. <Notification
  110. content={error.length ? error : t('generic_something_went_wrong')}
  111. type="error"
  112. />
  113. )}
  114. </OLModalBody>
  115. <OLModalFooter>
  116. <OLButton variant="secondary" disabled={inFlight} onClick={handleHide}>
  117. {t('cancel')}
  118. </OLButton>
  119. <OLButton
  120. variant="primary"
  121. disabled={inFlight || !valid}
  122. form="clone-project-form"
  123. type="submit"
  124. >
  125. {inFlight ? <>{t('copying')}…</> : t('copy')}
  126. </OLButton>
  127. </OLModalFooter>
  128. </>
  129. )
  130. }
  131. CloneProjectModalContent.propTypes = {
  132. handleHide: PropTypes.func.isRequired,
  133. inFlight: PropTypes.bool,
  134. setInFlight: PropTypes.func.isRequired,
  135. handleAfterCloned: PropTypes.func.isRequired,
  136. projectId: PropTypes.string,
  137. projectName: PropTypes.string,
  138. projectTags: PropTypes.arrayOf(
  139. PropTypes.shape({
  140. _id: PropTypes.string.isRequired,
  141. name: PropTypes.string.isRequired,
  142. color: PropTypes.string,
  143. })
  144. ),
  145. }