PageDuplicateModal.jsx 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. import React, { useState, useEffect, useCallback } from 'react';
  2. import PropTypes from 'prop-types';
  3. import {
  4. Modal, ModalHeader, ModalBody, ModalFooter,
  5. } from 'reactstrap';
  6. import { withTranslation } from 'react-i18next';
  7. import { withUnstatedContainers } from './UnstatedUtils';
  8. import AppContainer from '../services/AppContainer';
  9. import PageContainer from '../services/PageContainer';
  10. import PagePathAutoComplete from './PagePathAutoComplete';
  11. import ApiErrorMessageList from './PageManagement/ApiErrorMessageList';
  12. const PageDuplicateModal = (props) => {
  13. const { t, appContainer, pageContainer } = props;
  14. const config = appContainer.getConfig();
  15. const isReachable = config.isSearchServiceReachable;
  16. const { pageId, path } = pageContainer.state;
  17. const { crowi } = appContainer.config;
  18. const [pageNameInput, setPageNameInput] = useState(path);
  19. const [errs, setErrs] = useState(null);
  20. const [subordinatedPaths, setSubordinatedPaths] = useState([]);
  21. const [getSubordinatedError, setGetSuborinatedError] = useState(null);
  22. const [isDuplicateRecursively, setIsDuplicateRecursively] = useState(true);
  23. const [isDuplicateRecursivelyWithoutExistPath, setIsDuplicateRecursivelyWithoutExistPath] = useState(true);
  24. const [isExist, setIsExist] = useState(false);
  25. const [existPaths, setExistPaths] = useState([]);
  26. const dummyExistPaths = ['/test146'];
  27. /**
  28. * change pageNameInput for PagePathAutoComplete
  29. * @param {string} value
  30. */
  31. function ppacInputChangeHandler(value) {
  32. setPageNameInput(value);
  33. }
  34. /**
  35. * change pageNameInput
  36. * @param {string} value
  37. */
  38. function inputChangeHandler(value) {
  39. setPageNameInput(value);
  40. }
  41. function changeIsDuplicateRecursivelyHandler() {
  42. setIsDuplicateRecursively(!isDuplicateRecursively);
  43. }
  44. function changeIsDuplicateRecursivelyWithoutExistPathHandler() {
  45. setIsDuplicateRecursivelyWithoutExistPath(!isDuplicateRecursivelyWithoutExistPath);
  46. }
  47. function changeIsExistHandler() {
  48. setIsExist(true);
  49. }
  50. function createSubordinatedList() {
  51. return subordinatedPaths.map((duplicatedNewPath) => {
  52. const isExistPath = dummyExistPaths.includes(duplicatedNewPath);
  53. let result;
  54. if (isExistPath) {
  55. result = <li className="duplicate-exist" key={duplicatedNewPath}> {duplicatedNewPath}: { t('modal_duplicate.label.Same page already exists') } </li>;
  56. }
  57. else {
  58. result = <li key={duplicatedNewPath}>{duplicatedNewPath}</li>;
  59. }
  60. return result;
  61. });
  62. }
  63. const getSubordinatedList = useCallback(async() => {
  64. try {
  65. const res = await appContainer.apiv3Get('/pages/subordinated-list', { path });
  66. setSubordinatedPaths(res.data.resultPaths);
  67. }
  68. catch (err) {
  69. setGetSuborinatedError(t('modal_duplicate.label.Fail to get subordinated pages'));
  70. }
  71. }, [appContainer, path, t]);
  72. useEffect(() => {
  73. if (props.isOpen) {
  74. getSubordinatedList();
  75. }
  76. }, [props.isOpen, getSubordinatedList]);
  77. const checkExistPath = useCallback(() => {
  78. subordinatedPaths.map((duplicatedNewPath) => {
  79. const existPath = dummyExistPaths.includes(duplicatedNewPath); // dummyExistPaths is dummy data
  80. if (existPath) {
  81. changeIsExistHandler();
  82. }
  83. return;
  84. });
  85. }, [dummyExistPaths, subordinatedPaths]);
  86. useEffect(() => {
  87. if (props.isOpen) {
  88. checkExistPath();
  89. }
  90. }, [props.isOpen, checkExistPath]);
  91. async function duplicate() {
  92. setErrs(null);
  93. try {
  94. const res = await appContainer.apiv3Post('/pages/duplicate', { pageId, pageNameInput });
  95. const { page } = res.data;
  96. window.location.href = encodeURI(`${page.path}?duplicated=${path}`);
  97. }
  98. catch (err) {
  99. setErrs(err);
  100. }
  101. }
  102. function ppacSubmitHandler() {
  103. duplicate();
  104. }
  105. return (
  106. <Modal size="lg" isOpen={props.isOpen} toggle={props.onClose} className="grw-duplicate-page">
  107. <ModalHeader tag="h4" toggle={props.onClose} className="bg-primary text-light">
  108. { t('modal_duplicate.label.Duplicate page') }
  109. </ModalHeader>
  110. <ModalBody>
  111. <div className="form-group"><label>{t('modal_duplicate.label.Current page name')}</label><br />
  112. <code>{path}</code>
  113. </div>
  114. <div className="form-group">
  115. <label htmlFor="duplicatePageName">{ t('modal_duplicate.label.New page name') }</label><br />
  116. <div className="input-group">
  117. <div className="input-group-prepend">
  118. <span className="input-group-text">{crowi.url}</span>
  119. </div>
  120. <div className="flex-fill">
  121. {isReachable
  122. ? (
  123. <PagePathAutoComplete
  124. initializedPath={path}
  125. onSubmit={ppacSubmitHandler}
  126. onInputChange={ppacInputChangeHandler}
  127. />
  128. )
  129. : (
  130. <input
  131. type="text"
  132. value={pageNameInput}
  133. className="form-control"
  134. onChange={e => inputChangeHandler(e.target.value)}
  135. required
  136. />
  137. )}
  138. </div>
  139. </div>
  140. </div>
  141. <div className="custom-control custom-checkbox custom-checkbox-warning">
  142. <ul>
  143. <input
  144. className="custom-control-input"
  145. name="recursively"
  146. id="cbDuplicateRecursively"
  147. type="checkbox"
  148. checked={isDuplicateRecursively}
  149. onChange={changeIsDuplicateRecursivelyHandler}
  150. />
  151. <label className="custom-control-label" htmlFor="cbDuplicateRecursively">
  152. { t('modal_duplicate.label.Duplicate with child') }
  153. </label>
  154. </ul>
  155. <ul>
  156. <input
  157. className="custom-control-input"
  158. name="withoutExistRecursively"
  159. id="cbDuplicatewithoutExistRecursively"
  160. type="checkbox"
  161. checked={isDuplicateRecursivelyWithoutExistPath}
  162. onChange={changeIsDuplicateRecursivelyWithoutExistPathHandler}
  163. />
  164. <label className="custom-control-label" htmlFor="cbDuplicatewithoutExistRecursively">
  165. { t('modal_duplicate.label.Duplicate without exist path') }
  166. </label>
  167. </ul>
  168. <div>
  169. <ul className="duplicate-name">
  170. {isDuplicateRecursively && subordinatedPaths.map((duplicatedNewPath) => {
  171. const existPath = existPaths.includes(duplicatedNewPath);
  172. let result;
  173. if (existPath) {
  174. result = <li className="text-danger">{duplicatedNewPath} (exist)</li>;
  175. }
  176. else {
  177. result = <li>{duplicatedNewPath}</li>;
  178. }
  179. return result;
  180. })}
  181. </ul>
  182. </div>
  183. <div> {getSubordinatedError} </div>
  184. </div>
  185. </ModalBody>
  186. <ModalFooter>
  187. <ApiErrorMessageList errs={errs} targetPath={pageNameInput} />
  188. <button type="button" className="btn btn-primary" onClick={duplicate}>Duplicate page</button>
  189. </ModalFooter>
  190. </Modal>
  191. );
  192. };
  193. /**
  194. * Wrapper component for using unstated
  195. */
  196. const PageDuplicateModallWrapper = withUnstatedContainers(PageDuplicateModal, [AppContainer, PageContainer]);
  197. PageDuplicateModal.propTypes = {
  198. t: PropTypes.func.isRequired, // i18next
  199. appContainer: PropTypes.instanceOf(AppContainer).isRequired,
  200. pageContainer: PropTypes.instanceOf(PageContainer).isRequired,
  201. isOpen: PropTypes.bool.isRequired,
  202. onClose: PropTypes.func.isRequired,
  203. };
  204. export default withTranslation()(PageDuplicateModallWrapper);