PutbackPageModal.tsx 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. import type { FC } from 'react';
  2. import { useCallback, useMemo, useState } from 'react';
  3. import { useTranslation } from 'next-i18next';
  4. import { Modal, ModalBody, ModalFooter, ModalHeader } from 'reactstrap';
  5. import { apiPost } from '~/client/util/apiv1-client';
  6. import type { PutBackPageModalStatus } from '~/states/ui/modal/put-back-page';
  7. import {
  8. usePutBackPageModalActions,
  9. usePutBackPageModalStatus,
  10. } from '~/states/ui/modal/put-back-page';
  11. import { mutateAllPageInfo } from '~/stores/page';
  12. import ApiErrorMessageList from '../PageManagement/ApiErrorMessageList';
  13. type ApiError = {
  14. data?: string;
  15. };
  16. type ApiResponse = {
  17. page: {
  18. path: string;
  19. };
  20. };
  21. type PutBackPageModalSubstanceProps = {
  22. pageDataToRevert: PutBackPageModalStatus & {
  23. page: NonNullable<PutBackPageModalStatus['page']>;
  24. };
  25. closePutBackPageModal: () => void;
  26. };
  27. const PutBackPageModalSubstance: FC<PutBackPageModalSubstanceProps> = ({
  28. pageDataToRevert,
  29. closePutBackPageModal,
  30. }) => {
  31. const { t } = useTranslation();
  32. const { page } = pageDataToRevert;
  33. const { pageId, path } = page;
  34. const onPutBacked = pageDataToRevert.opts?.onPutBacked;
  35. const [errs, setErrs] = useState<ApiError[] | null>(null);
  36. const [targetPath, setTargetPath] = useState<string | null>(null);
  37. const [isPutbackRecursively, setIsPutbackRecursively] = useState(true);
  38. const changeIsPutbackRecursivelyHandler = useCallback(() => {
  39. setIsPutbackRecursively(!isPutbackRecursively);
  40. }, [isPutbackRecursively]);
  41. const putbackPageButtonHandler = useCallback(async () => {
  42. setErrs(null);
  43. try {
  44. // control flag
  45. // If is it not true, Request value must be `null`.
  46. const recursively = isPutbackRecursively ? true : null;
  47. const response = await apiPost<ApiResponse>('/pages.revertRemove', {
  48. page_id: pageId,
  49. recursively,
  50. });
  51. mutateAllPageInfo();
  52. if (onPutBacked != null) {
  53. onPutBacked(response.page.path);
  54. }
  55. closePutBackPageModal();
  56. } catch (err) {
  57. setTargetPath((err as ApiError).data ?? null);
  58. setErrs([err as ApiError]);
  59. }
  60. }, [pageId, isPutbackRecursively, onPutBacked, closePutBackPageModal]);
  61. const closeModalHandler = useCallback(() => {
  62. closePutBackPageModal();
  63. setErrs(null);
  64. }, [closePutBackPageModal]);
  65. const headerContent = useMemo(
  66. () => (
  67. <>
  68. <span className="material-symbols-outlined" aria-hidden="true">
  69. undo
  70. </span>{' '}
  71. {t('modal_putback.label.Put Back Page')}
  72. </>
  73. ),
  74. [t],
  75. );
  76. const bodyContent = useMemo(
  77. () => (
  78. <>
  79. <div>
  80. <span className="form-label">
  81. {t('modal_putback.label.Put Back Page')}:
  82. </span>
  83. <br />
  84. <code>{path}</code>
  85. </div>
  86. <div className="form-check form-check-warning">
  87. <input
  88. className="form-check-input"
  89. id="cbPutBackRecursively"
  90. type="checkbox"
  91. checked={isPutbackRecursively}
  92. onChange={changeIsPutbackRecursivelyHandler}
  93. />
  94. <label
  95. htmlFor="cbPutBackRecursively"
  96. className="form-label form-check-label"
  97. >
  98. {t('modal_putback.label.recursively')}
  99. </label>
  100. <p className="form-text text-muted mt-0">
  101. <code>{path}</code>
  102. {t('modal_putback.help.recursively')}
  103. </p>
  104. </div>
  105. </>
  106. ),
  107. [t, path, isPutbackRecursively, changeIsPutbackRecursivelyHandler],
  108. );
  109. const footerContent = useMemo(
  110. () => (
  111. <>
  112. <ApiErrorMessageList errs={errs} targetPath={targetPath} />
  113. <button
  114. type="button"
  115. className="btn btn-info"
  116. onClick={putbackPageButtonHandler}
  117. data-testid="put-back-execution-button"
  118. >
  119. <span className="material-symbols-outlined" aria-hidden="true">
  120. undo
  121. </span>{' '}
  122. {t('Put Back')}
  123. </button>
  124. </>
  125. ),
  126. [errs, targetPath, putbackPageButtonHandler, t],
  127. );
  128. return (
  129. <>
  130. <ModalHeader tag="h4" toggle={closeModalHandler} className="text-info">
  131. {headerContent}
  132. </ModalHeader>
  133. <ModalBody>{bodyContent}</ModalBody>
  134. <ModalFooter>{footerContent}</ModalFooter>
  135. </>
  136. );
  137. };
  138. const PutBackPageModal: FC = () => {
  139. const pageDataToRevert = usePutBackPageModalStatus();
  140. const { close: closePutBackPageModal } = usePutBackPageModalActions();
  141. const { isOpened, page } = pageDataToRevert;
  142. const closeModalHandler = useCallback(() => {
  143. closePutBackPageModal();
  144. }, [closePutBackPageModal]);
  145. if (page == null) {
  146. return <></>;
  147. }
  148. return (
  149. <Modal
  150. isOpen={isOpened}
  151. toggle={closeModalHandler}
  152. data-testid="put-back-page-modal"
  153. >
  154. {isOpened && (
  155. <PutBackPageModalSubstance
  156. pageDataToRevert={{ ...pageDataToRevert, page }}
  157. closePutBackPageModal={closePutBackPageModal}
  158. />
  159. )}
  160. </Modal>
  161. );
  162. };
  163. export { PutBackPageModal };