Răsfoiți Sursa

Merge pull request #4833 from weseek/feat/show-delete-modal

feat: Show delete modal
Yuki Takei 4 ani în urmă
părinte
comite
3546b7d97a

+ 1 - 1
packages/app/src/components/Common/Dropdown/PageItemControl.tsx

@@ -7,7 +7,7 @@ import { IPageHasId } from '~/interfaces/page';
 
 
 type PageItemControlProps = {
 type PageItemControlProps = {
   page: Partial<IPageHasId>,
   page: Partial<IPageHasId>,
-  onClickDeleteButton?: (pageId: string)=>void,
+  onClickDeleteButton?: (pageId: string) => void,
 }
 }
 
 
 const PageItemControl: FC<PageItemControlProps> = (props: PageItemControlProps) => {
 const PageItemControl: FC<PageItemControlProps> = (props: PageItemControlProps) => {

+ 26 - 2
packages/app/src/components/Sidebar/PageTree.tsx

@@ -1,4 +1,4 @@
-import React, { FC, memo } from 'react';
+import React, { FC, memo, useState } from 'react';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
 
 
 import { useSWRxV5MigrationStatus } from '~/stores/page-listing';
 import { useSWRxV5MigrationStatus } from '~/stores/page-listing';
@@ -6,6 +6,7 @@ import { useCurrentPagePath, useCurrentPageId, useTargetAndAncestors } from '~/s
 
 
 import ItemsTree from './PageTree/ItemsTree';
 import ItemsTree from './PageTree/ItemsTree';
 import PrivateLegacyPages from './PageTree/PrivateLegacyPages';
 import PrivateLegacyPages from './PageTree/PrivateLegacyPages';
+import { IPageForPageDeleteModal } from '../PageDeleteModal';
 
 
 
 
 const PageTree: FC = memo(() => {
 const PageTree: FC = memo(() => {
@@ -17,6 +18,19 @@ const PageTree: FC = memo(() => {
 
 
   const { data: migrationStatus } = useSWRxV5MigrationStatus();
   const { data: migrationStatus } = useSWRxV5MigrationStatus();
 
 
+  // for delete modal
+  const [isDeleteModalOpen, setDeleteModalOpen] = useState(false);
+  const [pagesToDelete, setPagesToDelete] = useState<IPageForPageDeleteModal[]>([]);
+
+  const onClickDeleteByPage = (page: IPageForPageDeleteModal) => {
+    setDeleteModalOpen(true);
+    setPagesToDelete([page]);
+  };
+
+  const onCloseDelete = () => {
+    setDeleteModalOpen(false);
+  };
+
   const path = currentPath || '/';
   const path = currentPath || '/';
 
 
   return (
   return (
@@ -26,7 +40,17 @@ const PageTree: FC = memo(() => {
       </div>
       </div>
 
 
       <div className="grw-sidebar-content-body">
       <div className="grw-sidebar-content-body">
-        <ItemsTree targetPath={path} targetId={targetId} targetAndAncestorsData={targetAndAncestorsData} />
+        <ItemsTree
+          targetPath={path}
+          targetId={targetId}
+          targetAndAncestorsData={targetAndAncestorsData}
+          isDeleteModalOpen={isDeleteModalOpen}
+          pagesToDelete={pagesToDelete}
+          isAbleToDeleteCompletely={false} // TODO: pass isAbleToDeleteCompletely
+          isDeleteCompletelyModal={false} // TODO: pass isDeleteCompletelyModal
+          onCloseDelete={onCloseDelete}
+          onClickDeleteByPage={onClickDeleteByPage}
+        />
       </div>
       </div>
 
 
       <div className="grw-sidebar-content-footer">
       <div className="grw-sidebar-content-footer">

+ 24 - 3
packages/app/src/components/Sidebar/PageTree/Item.tsx

@@ -9,12 +9,14 @@ import { IPageHasId } from '~/interfaces/page';
 import { useSWRxPageChildren } from '../../../stores/page-listing';
 import { useSWRxPageChildren } from '../../../stores/page-listing';
 import ClosableTextInput, { AlertInfo, AlertType } from '../../Common/ClosableTextInput';
 import ClosableTextInput, { AlertInfo, AlertType } from '../../Common/ClosableTextInput';
 import PageItemControl from '../../Common/Dropdown/PageItemControl';
 import PageItemControl from '../../Common/Dropdown/PageItemControl';
+import { IPageForPageDeleteModal } from '~/components/PageDeleteModal';
 
 
 
 
 interface ItemProps {
 interface ItemProps {
   itemNode: ItemNode
   itemNode: ItemNode
   targetId?: string
   targetId?: string
   isOpen?: boolean
   isOpen?: boolean
+  onClickDeleteByPage?(page: IPageForPageDeleteModal): void
 }
 }
 
 
 // Utility to mark target
 // Utility to mark target
@@ -84,7 +86,9 @@ const ItemCount: FC = () => {
 
 
 const Item: FC<ItemProps> = (props: ItemProps) => {
 const Item: FC<ItemProps> = (props: ItemProps) => {
   const { t } = useTranslation();
   const { t } = useTranslation();
-  const { itemNode, targetId, isOpen: _isOpen = false } = props;
+  const {
+    itemNode, targetId, isOpen: _isOpen = false, onClickDeleteByPage,
+  } = props;
 
 
   const { page, children } = itemNode;
   const { page, children } = itemNode;
 
 
@@ -104,8 +108,24 @@ const Item: FC<ItemProps> = (props: ItemProps) => {
   }, [isOpen]);
   }, [isOpen]);
 
 
   const onClickDeleteButtonHandler = useCallback(() => {
   const onClickDeleteButtonHandler = useCallback(() => {
-    console.log('Show delete modal');
-  }, []);
+    if (onClickDeleteByPage == null) {
+      return;
+    }
+
+    const { _id: pageId, revision: revisionId, path } = page;
+
+    if (pageId == null || revisionId == null || path == null) {
+      throw Error('Any of _id, revision, and path must not be null.');
+    }
+
+    const pageToDelete: IPageForPageDeleteModal = {
+      pageId,
+      revisionId: revisionId as string,
+      path,
+    };
+
+    onClickDeleteByPage(pageToDelete);
+  }, [page, onClickDeleteByPage]);
 
 
   const inputValidator = (title: string | null): AlertInfo | null => {
   const inputValidator = (title: string | null): AlertInfo | null => {
     if (title == null || title === '') {
     if (title == null || title === '') {
@@ -193,6 +213,7 @@ const Item: FC<ItemProps> = (props: ItemProps) => {
             key={node.page._id}
             key={node.page._id}
             itemNode={node}
             itemNode={node}
             isOpen={false}
             isOpen={false}
+            onClickDeleteByPage={onClickDeleteByPage}
           />
           />
         ))
         ))
       }
       }

+ 30 - 6
packages/app/src/components/Sidebar/PageTree/ItemsTree.tsx

@@ -6,7 +6,7 @@ import Item from './Item';
 import { useSWRxPageAncestorsChildren, useSWRxRootPage } from '../../../stores/page-listing';
 import { useSWRxPageAncestorsChildren, useSWRxRootPage } from '../../../stores/page-listing';
 import { TargetAndAncestors } from '~/interfaces/page-listing-results';
 import { TargetAndAncestors } from '~/interfaces/page-listing-results';
 import { toastError } from '~/client/util/apiNotification';
 import { toastError } from '~/client/util/apiNotification';
-
+import PageDeleteModal, { IPageForPageDeleteModal } from '~/components/PageDeleteModal';
 
 
 /*
 /*
  * Utility to generate initial node
  * Utility to generate initial node
@@ -46,12 +46,23 @@ type ItemsTreeProps = {
   targetPath: string
   targetPath: string
   targetId?: string
   targetId?: string
   targetAndAncestorsData?: TargetAndAncestors
   targetAndAncestorsData?: TargetAndAncestors
+
+  // for deleteModal
+  isDeleteModalOpen: boolean
+  pagesToDelete: IPageForPageDeleteModal[]
+  isAbleToDeleteCompletely: boolean
+  isDeleteCompletelyModal: boolean
+  onCloseDelete(): void
+  onClickDeleteByPage(page: IPageForPageDeleteModal): void
 }
 }
 
 
-const renderByInitialNode = (initialNode: ItemNode, targetId?: string): JSX.Element => {
+const renderByInitialNode = (
+    initialNode: ItemNode, DeleteModal: JSX.Element, targetId?: string, onClickDeleteByPage?: (page: IPageForPageDeleteModal) => void,
+): JSX.Element => {
   return (
   return (
     <div className="grw-pagetree p-3">
     <div className="grw-pagetree p-3">
-      <Item key={initialNode.page.path} targetId={targetId} itemNode={initialNode} isOpen />
+      <Item key={initialNode.page.path} targetId={targetId} itemNode={initialNode} isOpen onClickDeleteByPage={onClickDeleteByPage} />
+      {DeleteModal}
     </div>
     </div>
   );
   );
 };
 };
@@ -61,11 +72,24 @@ const renderByInitialNode = (initialNode: ItemNode, targetId?: string): JSX.Elem
  * ItemsTree
  * ItemsTree
  */
  */
 const ItemsTree: FC<ItemsTreeProps> = (props: ItemsTreeProps) => {
 const ItemsTree: FC<ItemsTreeProps> = (props: ItemsTreeProps) => {
-  const { targetPath, targetId, targetAndAncestorsData } = props;
+  const {
+    targetPath, targetId, targetAndAncestorsData, isDeleteModalOpen, pagesToDelete, isAbleToDeleteCompletely, isDeleteCompletelyModal, onCloseDelete,
+    onClickDeleteByPage,
+  } = props;
 
 
   const { data: ancestorsChildrenData, error: error1 } = useSWRxPageAncestorsChildren(targetPath);
   const { data: ancestorsChildrenData, error: error1 } = useSWRxPageAncestorsChildren(targetPath);
   const { data: rootPageData, error: error2 } = useSWRxRootPage();
   const { data: rootPageData, error: error2 } = useSWRxRootPage();
 
 
+  const DeleteModal = (
+    <PageDeleteModal
+      isOpen={isDeleteModalOpen}
+      pages={pagesToDelete}
+      isAbleToDeleteCompletely={isAbleToDeleteCompletely}
+      isDeleteCompletelyModal={isDeleteCompletelyModal}
+      onClose={onCloseDelete}
+    />
+  );
+
   if (error1 != null || error2 != null) {
   if (error1 != null || error2 != null) {
     // TODO: improve message
     // TODO: improve message
     toastError('Error occurred while fetching pages to render PageTree');
     toastError('Error occurred while fetching pages to render PageTree');
@@ -77,7 +101,7 @@ const ItemsTree: FC<ItemsTreeProps> = (props: ItemsTreeProps) => {
    */
    */
   if (ancestorsChildrenData != null && rootPageData != null) {
   if (ancestorsChildrenData != null && rootPageData != null) {
     const initialNode = generateInitialNodeAfterResponse(ancestorsChildrenData.ancestorsChildren, new ItemNode(rootPageData.rootPage));
     const initialNode = generateInitialNodeAfterResponse(ancestorsChildrenData.ancestorsChildren, new ItemNode(rootPageData.rootPage));
-    return renderByInitialNode(initialNode, targetId);
+    return renderByInitialNode(initialNode, DeleteModal, targetId, onClickDeleteByPage);
   }
   }
 
 
   /*
   /*
@@ -85,7 +109,7 @@ const ItemsTree: FC<ItemsTreeProps> = (props: ItemsTreeProps) => {
    */
    */
   if (targetAndAncestorsData != null) {
   if (targetAndAncestorsData != null) {
     const initialNode = generateInitialNodeBeforeResponse(targetAndAncestorsData.targetAndAncestors);
     const initialNode = generateInitialNodeBeforeResponse(targetAndAncestorsData.targetAndAncestors);
-    return renderByInitialNode(initialNode, targetId);
+    return renderByInitialNode(initialNode, DeleteModal, targetId, onClickDeleteByPage);
   }
   }
 
 
   return null;
   return null;

+ 1 - 1
packages/app/src/server/models/obsolete-page.js

@@ -256,7 +256,7 @@ export class PageQueryBuilder {
   }
   }
 
 
   addConditionToMinimizeDataForRendering() {
   addConditionToMinimizeDataForRendering() {
-    this.query = this.query.select('_id path isEmpty grant');
+    this.query = this.query.select('_id path isEmpty grant revision');
 
 
     return this;
     return this;
   }
   }