clone-project-modal-content.tsx 4.3 KB

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