Kaynağa Gözat

open duplicate modal

kaori 4 yıl önce
ebeveyn
işleme
49692bc89f

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

@@ -23,6 +23,7 @@ type CommonProps = {
   isEnableActions?: boolean,
   isEnableActions?: boolean,
   hideBookmarkMenuItem?: boolean,
   hideBookmarkMenuItem?: boolean,
   onClickBookmarkMenuItem?: (pageId: string, newValue?: boolean) => Promise<void>,
   onClickBookmarkMenuItem?: (pageId: string, newValue?: boolean) => Promise<void>,
+  onClickDuplicateMenuItem?: (pageId: string, path: string) => void,
   onClickRenameMenuItem?: (pageId: string) => void,
   onClickRenameMenuItem?: (pageId: string) => void,
   onClickDeleteMenuItem?: (pageId: string) => void,
   onClickDeleteMenuItem?: (pageId: string) => void,
 
 
@@ -32,14 +33,15 @@ type CommonProps = {
 
 
 type DropdownMenuProps = CommonProps & {
 type DropdownMenuProps = CommonProps & {
   pageId: string,
   pageId: string,
+  path: string,
 }
 }
 
 
 const PageItemControlDropdownMenu = React.memo((props: DropdownMenuProps): JSX.Element => {
 const PageItemControlDropdownMenu = React.memo((props: DropdownMenuProps): JSX.Element => {
   const { t } = useTranslation('');
   const { t } = useTranslation('');
 
 
   const {
   const {
-    pageId, pageInfo, isEnableActions, hideBookmarkMenuItem,
-    onClickBookmarkMenuItem, onClickRenameMenuItem, onClickDeleteMenuItem,
+    pageId, path, pageInfo, isEnableActions, hideBookmarkMenuItem,
+    onClickBookmarkMenuItem, onClickDuplicateMenuItem, onClickRenameMenuItem, onClickDeleteMenuItem,
     additionalMenuItemRenderer: AdditionalMenuItems,
     additionalMenuItemRenderer: AdditionalMenuItems,
   } = props;
   } = props;
 
 
@@ -52,6 +54,14 @@ const PageItemControlDropdownMenu = React.memo((props: DropdownMenuProps): JSX.E
     await onClickBookmarkMenuItem(pageId, !pageInfo.isBookmarked);
     await onClickBookmarkMenuItem(pageId, !pageInfo.isBookmarked);
   }, [onClickBookmarkMenuItem, pageId, pageInfo]);
   }, [onClickBookmarkMenuItem, pageId, pageInfo]);
 
 
+  // eslint-disable-next-line react-hooks/rules-of-hooks
+  const duplicateItemClickedHandler = useCallback(async() => {
+    if (onClickDuplicateMenuItem == null) {
+      return;
+    }
+    await onClickDuplicateMenuItem(pageId, path);
+  }, [pageId]);
+
   // eslint-disable-next-line react-hooks/rules-of-hooks
   // eslint-disable-next-line react-hooks/rules-of-hooks
   const renameItemClickedHandler = useCallback(async() => {
   const renameItemClickedHandler = useCallback(async() => {
     if (onClickRenameMenuItem == null) {
     if (onClickRenameMenuItem == null) {
@@ -97,7 +107,7 @@ const PageItemControlDropdownMenu = React.memo((props: DropdownMenuProps): JSX.E
 
 
       {/* Duplicate */}
       {/* Duplicate */}
       { isExistPageInfo(pageInfo) && isEnableActions && (
       { isExistPageInfo(pageInfo) && isEnableActions && (
-        <DropdownItem onClick={() => toastr.warning(t('search_result.currently_not_implemented'))}>
+        <DropdownItem onClick={duplicateItemClickedHandler}>
           <i className="icon-fw icon-docs"></i>
           <i className="icon-fw icon-docs"></i>
           {t('Duplicate')}
           {t('Duplicate')}
         </DropdownItem>
         </DropdownItem>
@@ -135,6 +145,7 @@ const PageItemControlDropdownMenu = React.memo((props: DropdownMenuProps): JSX.E
 
 
 type PageItemControlSubstanceProps = CommonProps & {
 type PageItemControlSubstanceProps = CommonProps & {
   pageId: string,
   pageId: string,
+  path: string,
   fetchOnOpen?: boolean,
   fetchOnOpen?: boolean,
 }
 }
 
 
@@ -142,7 +153,7 @@ export const PageItemControlSubstance = (props: PageItemControlSubstanceProps):
 
 
   const {
   const {
     pageId, pageInfo: presetPageInfo, fetchOnOpen,
     pageId, pageInfo: presetPageInfo, fetchOnOpen,
-    onClickBookmarkMenuItem,
+    onClickBookmarkMenuItem, onClickDuplicateMenuItem,
   } = props;
   } = props;
 
 
   const [isOpen, setIsOpen] = useState(false);
   const [isOpen, setIsOpen] = useState(false);
@@ -161,6 +172,16 @@ export const PageItemControlSubstance = (props: PageItemControlSubstanceProps):
     }
     }
   }, [mutatePageInfo, onClickBookmarkMenuItem, shouldFetch]);
   }, [mutatePageInfo, onClickBookmarkMenuItem, shouldFetch]);
 
 
+  const duplicateMenuItemClickHandler = useCallback(async(_pageId: string, _path: string) => {
+    if (onClickDuplicateMenuItem != null) {
+      await onClickDuplicateMenuItem(_pageId, _path);
+    }
+
+    if (shouldFetch) {
+      mutatePageInfo();
+    }
+  }, [mutatePageInfo, onClickDuplicateMenuItem, shouldFetch]);
+
   return (
   return (
     <Dropdown isOpen={isOpen} toggle={() => setIsOpen(!isOpen)}>
     <Dropdown isOpen={isOpen} toggle={() => setIsOpen(!isOpen)}>
       <DropdownToggle color="transparent" className="border-0 rounded grw-btn-page-management p-0">
       <DropdownToggle color="transparent" className="border-0 rounded grw-btn-page-management p-0">
@@ -171,6 +192,7 @@ export const PageItemControlSubstance = (props: PageItemControlSubstanceProps):
         {...props}
         {...props}
         pageInfo={presetPageInfo ?? fetchedPageInfo}
         pageInfo={presetPageInfo ?? fetchedPageInfo}
         onClickBookmarkMenuItem={bookmarkMenuItemClickHandler}
         onClickBookmarkMenuItem={bookmarkMenuItemClickHandler}
+        onClickDuplicateMenuItem={duplicateMenuItemClickHandler}
       />
       />
     </Dropdown>
     </Dropdown>
   );
   );
@@ -180,29 +202,31 @@ export const PageItemControlSubstance = (props: PageItemControlSubstanceProps):
 
 
 type PageItemControlProps = CommonProps & {
 type PageItemControlProps = CommonProps & {
   pageId?: string,
   pageId?: string,
+  path?: string,
 }
 }
 
 
 export const PageItemControl = (props: PageItemControlProps): JSX.Element => {
 export const PageItemControl = (props: PageItemControlProps): JSX.Element => {
-  const { pageId } = props;
+  const { pageId, path } = props;
 
 
-  if (pageId == null) {
+  if (pageId == null || path == null) {
     return <></>;
     return <></>;
   }
   }
 
 
-  return <PageItemControlSubstance pageId={pageId} {...props} />;
+  return <PageItemControlSubstance pageId={pageId} path={path} {...props} />;
 };
 };
 
 
 
 
 type AsyncPageItemControlProps = CommonProps & {
 type AsyncPageItemControlProps = CommonProps & {
   pageId?: string,
   pageId?: string,
+  path?: string,
 }
 }
 
 
 export const AsyncPageItemControl = (props: AsyncPageItemControlProps): JSX.Element => {
 export const AsyncPageItemControl = (props: AsyncPageItemControlProps): JSX.Element => {
-  const { pageId } = props;
+  const { pageId, path } = props;
 
 
-  if (pageId == null) {
+  if (pageId == null || path == null) {
     return <></>;
     return <></>;
   }
   }
 
 
-  return <PageItemControlSubstance pageId={pageId} fetchOnOpen {...props} />;
+  return <PageItemControlSubstance pageId={pageId} path={path} fetchOnOpen {...props} />;
 };
 };

+ 1 - 0
packages/app/src/components/PageList/PageListItemL.tsx

@@ -121,6 +121,7 @@ export const PageListItemL: FC<Props> = memo((props:Props) => {
                 {/* TODO: use PageItemControl with prefetched IPageInfo object */}
                 {/* TODO: use PageItemControl with prefetched IPageInfo object */}
                 <AsyncPageItemControl
                 <AsyncPageItemControl
                   pageId={pageData._id}
                   pageId={pageData._id}
+                  path={pageData.path}
                   onClickDeleteMenuItem={props.onClickDeleteButton}
                   onClickDeleteMenuItem={props.onClickDeleteButton}
                   isEnableActions={isEnableActions}
                   isEnableActions={isEnableActions}
                 />
                 />

+ 12 - 1
packages/app/src/components/Sidebar/PageTree/Item.tsx

@@ -23,6 +23,7 @@ interface ItemProps {
   targetPathOrId?: string
   targetPathOrId?: string
   isOpen?: boolean
   isOpen?: boolean
   onClickDeleteByPage?(page: IPageForPageDeleteModal): void
   onClickDeleteByPage?(page: IPageForPageDeleteModal): void
+  onClickDuplicateMenuItem?(pageId: string, path: string): void
 }
 }
 
 
 // Utility to mark target
 // Utility to mark target
@@ -63,7 +64,7 @@ const ItemCount: FC<ItemCountProps> = (props:ItemCountProps) => {
 const Item: FC<ItemProps> = (props: ItemProps) => {
 const Item: FC<ItemProps> = (props: ItemProps) => {
   const { t } = useTranslation();
   const { t } = useTranslation();
   const {
   const {
-    itemNode, targetPathOrId, isOpen: _isOpen = false, onClickDeleteByPage, isEnableActions,
+    itemNode, targetPathOrId, isOpen: _isOpen = false, onClickDuplicateMenuItem, onClickDeleteByPage, isEnableActions,
   } = props;
   } = props;
 
 
   const { page, children } = itemNode;
   const { page, children } = itemNode;
@@ -122,6 +123,13 @@ const Item: FC<ItemProps> = (props: ItemProps) => {
     setNewPageInputShown(true);
     setNewPageInputShown(true);
   }, []);
   }, []);
 
 
+  const duplicateMenuItemClickHandler = async(_pageId: string, _path: string): Promise<void> => {
+    if (onClickDuplicateMenuItem == null) {
+      return;
+    }
+    await onClickDuplicateMenuItem(_pageId, _path);
+  };
+
   const onClickDeleteButton = useCallback(async(_pageId: string): Promise<void> => {
   const onClickDeleteButton = useCallback(async(_pageId: string): Promise<void> => {
     if (onClickDeleteByPage == null) {
     if (onClickDeleteByPage == null) {
       return;
       return;
@@ -261,8 +269,10 @@ const Item: FC<ItemProps> = (props: ItemProps) => {
         <div className="grw-pagetree-control d-none">
         <div className="grw-pagetree-control d-none">
           <AsyncPageItemControl
           <AsyncPageItemControl
             pageId={page._id}
             pageId={page._id}
+            path={page.path}
             isEnableActions={isEnableActions}
             isEnableActions={isEnableActions}
             onClickBookmarkMenuItem={bookmarkMenuItemClickHandler}
             onClickBookmarkMenuItem={bookmarkMenuItemClickHandler}
+            onClickDuplicateMenuItem={duplicateMenuItemClickHandler}
             onClickDeleteMenuItem={onClickDeleteButton}
             onClickDeleteMenuItem={onClickDeleteButton}
             onClickRenameMenuItem={onClickRenameButton}
             onClickRenameMenuItem={onClickRenameButton}
           />
           />
@@ -293,6 +303,7 @@ const Item: FC<ItemProps> = (props: ItemProps) => {
               itemNode={node}
               itemNode={node}
               isOpen={false}
               isOpen={false}
               targetPathOrId={targetPathOrId}
               targetPathOrId={targetPathOrId}
+              onClickDuplicateMenuItem={onClickDuplicateMenuItem}
               onClickDeleteByPage={onClickDeleteByPage}
               onClickDeleteByPage={onClickDeleteByPage}
             />
             />
           </div>
           </div>

+ 18 - 5
packages/app/src/components/Sidebar/PageTree/ItemsTree.tsx

@@ -7,7 +7,7 @@ import { useSWRxPageAncestorsChildren, useSWRxRootPage } from '../../../stores/p
 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 from '~/components/PageDeleteModal';
 import PageDeleteModal from '~/components/PageDeleteModal';
-import { IPageForPageDeleteModal } from '~/stores/ui';
+import { IPageForPageDeleteModal, usePageDuplicateModalStatus } from '~/stores/ui';
 
 
 /*
 /*
  * Utility to generate initial node
  * Utility to generate initial node
@@ -65,8 +65,14 @@ type ItemsTreeProps = {
 
 
 const renderByInitialNode = (
 const renderByInitialNode = (
     // eslint-disable-next-line max-len
     // eslint-disable-next-line max-len
-    initialNode: ItemNode, DeleteModal: JSX.Element, isEnableActions: boolean, targetPathOrId?: string, onClickDeleteByPage?: (page: IPageForPageDeleteModal) => void,
+    initialNode: ItemNode,
+    DeleteModal: JSX.Element,
+    isEnableActions: boolean,
+    targetPathOrId?: string,
+    onClickDuplicateMenuItem?: (pageId: string, path: string) => void,
+    onClickDeleteByPage?: (page: IPageForPageDeleteModal) => void,
 ): JSX.Element => {
 ): JSX.Element => {
+
   return (
   return (
     <ul className="grw-pagetree list-group p-3">
     <ul className="grw-pagetree list-group p-3">
       <Item
       <Item
@@ -75,6 +81,7 @@ const renderByInitialNode = (
         itemNode={initialNode}
         itemNode={initialNode}
         isOpen
         isOpen
         isEnableActions={isEnableActions}
         isEnableActions={isEnableActions}
+        onClickDuplicateMenuItem={onClickDuplicateMenuItem}
         onClickDeleteByPage={onClickDeleteByPage}
         onClickDeleteByPage={onClickDeleteByPage}
       />
       />
       {DeleteModal}
       {DeleteModal}
@@ -88,12 +95,18 @@ const renderByInitialNode = (
  */
  */
 const ItemsTree: FC<ItemsTreeProps> = (props: ItemsTreeProps) => {
 const ItemsTree: FC<ItemsTreeProps> = (props: ItemsTreeProps) => {
   const {
   const {
-    targetPath, targetPathOrId, targetAndAncestorsData, isDeleteModalOpen, pagesToDelete, isAbleToDeleteCompletely, isDeleteCompletelyModal, onCloseDelete,
+    targetPath, targetPathOrId, targetAndAncestorsData, isDeleteModalOpen, pagesToDelete, isAbleToDeleteCompletely, isDeleteCompletelyModal,
+    onCloseDelete,
     onClickDeleteByPage, isEnableActions,
     onClickDeleteByPage, isEnableActions,
   } = props;
   } = 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 { open: openDuplicateModal } = usePageDuplicateModalStatus();
+
+  const onClickDuplicateMenuItem = (pageId: string, path: string) => {
+    openDuplicateModal(pageId, path);
+  };
 
 
   // TODO: show PageDeleteModal with usePageDeleteModalStatus by 87568
   // TODO: show PageDeleteModal with usePageDeleteModalStatus by 87568
   const DeleteModal = (
   const DeleteModal = (
@@ -117,7 +130,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, DeleteModal, isEnableActions, targetPathOrId, onClickDeleteByPage);
+    return renderByInitialNode(initialNode, DeleteModal, isEnableActions, targetPathOrId, onClickDuplicateMenuItem, onClickDeleteByPage);
   }
   }
 
 
   /*
   /*
@@ -125,7 +138,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, DeleteModal, isEnableActions, targetPathOrId, onClickDeleteByPage);
+    return renderByInitialNode(initialNode, DeleteModal, isEnableActions, targetPathOrId, onClickDuplicateMenuItem, onClickDeleteByPage);
   }
   }
 
 
   return null;
   return null;