PutbackPageModal.tsx 4.7 KB

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