PageDeleteModal.tsx 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import React, { useState, FC } from 'react';
  2. import toastr from 'toastr';
  3. import {
  4. Modal, ModalHeader, ModalBody, ModalFooter,
  5. } from 'reactstrap';
  6. import { useTranslation } from 'react-i18next';
  7. import { apiPost } from '~/client/util/apiv1-client';
  8. import { usePageDeleteModalStatus, usePageDeleteModalOpened } from '~/stores/ui';
  9. import { IPageApiv1Result } from '~/interfaces/page';
  10. import ApiErrorMessageList from './PageManagement/ApiErrorMessageList';
  11. const deleteIconAndKey = {
  12. completely: {
  13. color: 'danger',
  14. icon: 'fire',
  15. translationKey: 'completely',
  16. },
  17. temporary: {
  18. color: 'primary',
  19. icon: 'trash',
  20. translationKey: 'page',
  21. },
  22. };
  23. type Props = {
  24. isOpen: boolean,
  25. isDeleteCompletelyModal: boolean,
  26. isAbleToDeleteCompletely: boolean,
  27. onClose?: () => void,
  28. }
  29. const PageDeleteModal: FC<Props> = (props: Props) => {
  30. const { t } = useTranslation('');
  31. const {
  32. isDeleteCompletelyModal, isAbleToDeleteCompletely,
  33. } = props;
  34. const { data: pagesDataToDelete, close: closeDeleteModal } = usePageDeleteModalStatus();
  35. const { data: isOpened } = usePageDeleteModalOpened();
  36. const [isDeleteRecursively, setIsDeleteRecursively] = useState(true);
  37. const [isDeleteCompletely, setIsDeleteCompletely] = useState(isDeleteCompletelyModal && isAbleToDeleteCompletely);
  38. const deleteMode = isDeleteCompletely ? 'completely' : 'temporary';
  39. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  40. const [errs, setErrs] = useState(null);
  41. function changeIsDeleteRecursivelyHandler() {
  42. setIsDeleteRecursively(!isDeleteRecursively);
  43. }
  44. function changeIsDeleteCompletelyHandler() {
  45. if (!isAbleToDeleteCompletely) {
  46. return;
  47. }
  48. setIsDeleteCompletely(!isDeleteCompletely);
  49. }
  50. async function deletePage() {
  51. // toastr.warning(t('search_result.currently_not_implemented'));
  52. // Todo implement page delete function at https://redmine.weseek.co.jp/issues/82222
  53. // setErrs(null);
  54. if (pagesDataToDelete?.pages != null && (pagesDataToDelete.pages.length > 0)) {
  55. const pageToDelete = pagesDataToDelete?.pages[0];
  56. try {
  57. // control flag
  58. // If is it not true, Request value must be `null`.
  59. const recursively = isDeleteRecursively != true ? true : null;
  60. const completely = isDeleteCompletely != true  ? true : null;
  61. const result = await apiPost('/pages.remove', {
  62. page_id: pageToDelete.pageId,
  63. revision_id: pageToDelete.revisionId,
  64. recursively,
  65. completely,
  66. }) as IPageApiv1Result;
  67. const trashPagePath = result.page.path;
  68. window.location.href = encodeURI(trashPagePath);
  69. }
  70. catch (err) {
  71. setErrs(err);
  72. }
  73. }
  74. }
  75. async function deleteButtonHandler() {
  76. deletePage();
  77. }
  78. function renderDeleteRecursivelyForm() {
  79. return (
  80. <div className="custom-control custom-checkbox custom-checkbox-warning">
  81. <input
  82. className="custom-control-input"
  83. id="deleteRecursively"
  84. type="checkbox"
  85. checked={isDeleteRecursively}
  86. onChange={changeIsDeleteRecursivelyHandler}
  87. // disabled // Todo: enable this at https://redmine.weseek.co.jp/issues/82222
  88. />
  89. <label className="custom-control-label" htmlFor="deleteRecursively">
  90. { t('modal_delete.delete_recursively') }
  91. </label>
  92. </div>
  93. );
  94. }
  95. // DeleteCompletely is currently disabled
  96. // TODO1 : Retrive isAbleToDeleteCompleltly state everywhere in the system via swr.
  97. // Story: https://redmine.weseek.co.jp/issues/82222
  98. // TODO2 : use toaster
  99. // TASK : https://redmine.weseek.co.jp/issues/82299
  100. function renderDeleteCompletelyForm() {
  101. return (
  102. <div className="custom-control custom-checkbox custom-checkbox-danger">
  103. <input
  104. className="custom-control-input"
  105. name="completely"
  106. id="deleteCompletely"
  107. type="checkbox"
  108. // disabled={!isAbleToDeleteCompletely}
  109. // disabled // Todo: will be implemented at https://redmine.weseek.co.jp/issues/82222
  110. checked={isDeleteCompletely}
  111. onChange={changeIsDeleteCompletelyHandler}
  112. />
  113. {/* ↓↓ undo this comment out at https://redmine.weseek.co.jp/issues/82222 ↓↓ */}
  114. {/* <label className="custom-control-label text-danger" htmlFor="deleteCompletely"> */}
  115. <label className="custom-control-label" htmlFor="deleteCompletely">
  116. { t('modal_delete.delete_completely')}
  117. <p className="form-text text-muted mt-0"> { t('modal_delete.completely') }</p>
  118. </label>
  119. {!isAbleToDeleteCompletely
  120. && (
  121. <p className="alert alert-warning p-2 my-0">
  122. <i className="icon-ban icon-fw"></i>{ t('modal_delete.delete_completely_restriction') }
  123. </p>
  124. )}
  125. </div>
  126. );
  127. }
  128. const renderPagePathsToDelete = () => {
  129. if (pagesDataToDelete != null && pagesDataToDelete.pages != null) {
  130. return pagesDataToDelete.pages.map(page => <div key={page.pageId}><code>{ page.path }</code></div>);
  131. }
  132. return <></>;
  133. };
  134. return (
  135. <Modal size="lg" isOpen={isOpened} toggle={closeDeleteModal} className="grw-create-page">
  136. <ModalHeader tag="h4" toggle={closeDeleteModal} className={`bg-${deleteIconAndKey[deleteMode].color} text-light`}>
  137. <i className={`icon-fw icon-${deleteIconAndKey[deleteMode].icon}`}></i>
  138. { t(`modal_delete.delete_${deleteIconAndKey[deleteMode].translationKey}`) }
  139. </ModalHeader>
  140. <ModalBody>
  141. <div className="form-group grw-scrollable-modal-body pb-1">
  142. <label>{ t('modal_delete.deleting_page') }:</label><br />
  143. {/* Todo: change the way to show path on modal when too many pages are selected */}
  144. {/* https://redmine.weseek.co.jp/issues/82787 */}
  145. {renderPagePathsToDelete()}
  146. </div>
  147. {renderDeleteRecursivelyForm()}
  148. {!isDeleteCompletelyModal && renderDeleteCompletelyForm()}
  149. </ModalBody>
  150. <ModalFooter>
  151. <ApiErrorMessageList errs={errs} />
  152. <button type="button" className={`btn btn-${deleteIconAndKey[deleteMode].color}`} onClick={deleteButtonHandler}>
  153. <i className={`icon-${deleteIconAndKey[deleteMode].icon}`} aria-hidden="true"></i>
  154. { t(`modal_delete.delete_${deleteIconAndKey[deleteMode].translationKey}`) }
  155. </button>
  156. </ModalFooter>
  157. </Modal>
  158. );
  159. };
  160. export default PageDeleteModal;