soumaeda 2 лет назад
Родитель
Сommit
850d457665

+ 6 - 0
apps/app/src/components/Layout/AdminLayout.tsx

@@ -13,6 +13,11 @@ import styles from './Admin.module.scss';
 const PageCreateModal = dynamic(() => import('../PageCreateModal'), { ssr: false });
 const SystemVersion = dynamic(() => import('../SystemVersion'), { ssr: false });
 const HotkeysManager = dynamic(() => import('../Hotkeys/HotkeysManager'), { ssr: false });
+const PluginDeleteModal = dynamic(() => import(
+  '../../features/growi-plugin/client/components/Admin/PluginsExtensionPageContents/PluginDeleteModal'
+).then(
+  mod => mod.PluginDeleteModal,
+), { ssr: false });
 
 
 type Props = {
@@ -47,6 +52,7 @@ const AdminLayout = ({
         </div>
 
         <PageCreateModal />
+        <PluginDeleteModal />
         <SystemVersion />
       </div>
 

+ 7 - 41
apps/app/src/features/growi-plugin/client/components/Admin/PluginsExtensionPageContents/PluginCard.tsx

@@ -3,10 +3,9 @@ import React, { useState } from 'react';
 import { useTranslation } from 'next-i18next';
 import Link from 'next/link';
 
-import { apiv3Delete, apiv3Put } from '~/client/util/apiv3-client';
+import { apiv3Put } from '~/client/util/apiv3-client';
 import { toastSuccess, toastError } from '~/client/util/toastr';
-
-import { PluginDeleteModal } from './PluginDeleteModal';
+import { usePluginDeleteModal } from '~/stores/modal';
 
 import styles from './PluginCard.module.scss';
 
@@ -15,17 +14,19 @@ type Props = {
   name: string,
   url: string,
   isEnalbed: boolean,
-  mutate: () => void,
   desc?: string,
+  onDelete: () => void,
+  mutate: () => void,
 }
 
 export const PluginCard = (props: Props): JSX.Element => {
 
   const {
-    id, name, url, isEnalbed, desc, mutate,
+    id, name, url, isEnalbed, desc,
   } = props;
 
   const { t } = useTranslation('admin');
+  const { open: openPluginDeleteModal } = usePluginDeleteModal();
 
   const PluginCardButton = (): JSX.Element => {
     const [isEnabled, setState] = useState<boolean>(isEnalbed);
@@ -71,51 +72,16 @@ export const PluginCard = (props: Props): JSX.Element => {
   };
 
   const PluginDeleteButton = (): JSX.Element => {
-    const [isDeleteConfirmModalShown, setIsDeleteConfirmModalShown] = useState<boolean>(false);
-
-    const onClickPluginDeleteButton = () => {
-      setIsDeleteConfirmModalShown(true);
-    };
-
-    const onCancelDeletePlugin = () => {
-      setIsDeleteConfirmModalShown(false);
-    };
-
-    const onClickPluginDeleteBtnHandler = async() => {
-      const reqUrl = `/plugins/${id}/remove`;
-
-      try {
-        const res = await apiv3Delete(reqUrl);
-        const pluginName = res.data.pluginName;
-        toastSuccess(t('toaster.remove_plugin_success', { pluginName }));
-      }
-      catch (err) {
-        toastError(err);
-      }
-      finally {
-        mutate();
-        setIsDeleteConfirmModalShown(false);
-      }
-    };
 
     return (
       <div className="">
         <button
           type="submit"
           className="btn btn-primary"
-          onClick={() => onClickPluginDeleteButton()}
+          onClick={props.onDelete}
         >
           {t('plugins.delete')}
         </button>
-        {isDeleteConfirmModalShown && (
-          <PluginDeleteModal
-            isShown={isDeleteConfirmModalShown}
-            name={name}
-            url={url}
-            cancelToDelete={onCancelDeletePlugin}
-            confirmToDelete={onClickPluginDeleteBtnHandler}
-          />
-        )}
       </div>
     );
   };

+ 32 - 16
apps/app/src/features/growi-plugin/client/components/Admin/PluginsExtensionPageContents/PluginDeleteModal.tsx

@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useCallback } from 'react';
 
 import { useTranslation } from 'next-i18next';
 import Link from 'next/link';
@@ -6,21 +6,37 @@ import {
   Button, Modal, ModalHeader, ModalBody, ModalFooter,
 } from 'reactstrap';
 
+import { apiv3Delete } from '~/client/util/apiv3-client';
+import { toastSuccess, toastError } from '~/client/util/toastr';
+import { usePluginDeleteModal } from '~/stores/modal';
 
-export type PluginDeleteModalProps = {
-  isShown: boolean,
-  name: string,
-  url: string,
-  cancelToDelete: () => void,
-  confirmToDelete: () => void,
-}
 
-export const PluginDeleteModal = (props: PluginDeleteModalProps): JSX.Element => {
-  const {
-    isShown, name, url, cancelToDelete, confirmToDelete,
-  } = props;
+export const PluginDeleteModal: React.FC = () => {
 
   const { t } = useTranslation('admin');
+  const { data: pluginDeleteModal, close: closePluginDeleteModal } = usePluginDeleteModal();
+  const isShown = pluginDeleteModal?.isShown;
+  const name = pluginDeleteModal?.name;
+  const url = pluginDeleteModal?.url;
+  const id = pluginDeleteModal?.id;
+
+  const toggleHandler = useCallback(() => {
+    closePluginDeleteModal();
+  }, [closePluginDeleteModal]);
+
+  const onClickDeleteButtonHandler = useCallback(async() => {
+    const reqUrl = `/plugins/${id}/remove`;
+
+    try {
+      const res = await apiv3Delete(reqUrl);
+      const pluginName = res.data.pluginName;
+      closePluginDeleteModal();
+      toastSuccess(t('toaster.remove_plugin_success', { pluginName }));
+    }
+    catch (err) {
+      toastError(err);
+    }
+  }, [id, closePluginDeleteModal, t]);
 
   const headerContent = () => {
     return (
@@ -33,7 +49,7 @@ export const PluginDeleteModal = (props: PluginDeleteModalProps): JSX.Element =>
   const bodyContent = () => {
 
     return (
-      <div className="card well mt-2 p-2">
+      <div className="card well mt-2 p-2" key={id}>
         <Link href={`${url}`} legacyBehavior>{name}</Link>
       </div>
     );
@@ -42,7 +58,7 @@ export const PluginDeleteModal = (props: PluginDeleteModalProps): JSX.Element =>
   const footerContent = () => {
     return (
       <>
-        <Button color="danger" onClick={confirmToDelete}>
+        <Button color="danger" onClick={onClickDeleteButtonHandler}>
           {t('plugins.delete')}
         </Button>
       </>
@@ -50,8 +66,8 @@ export const PluginDeleteModal = (props: PluginDeleteModalProps): JSX.Element =>
   };
 
   return (
-    <Modal isOpen={isShown} toggle={cancelToDelete}>
-      <ModalHeader tag="h4" toggle={cancelToDelete} className="bg-danger text-light">
+    <Modal isOpen={isShown} toggle={toggleHandler}>
+      <ModalHeader tag="h4" toggle={toggleHandler} className="bg-danger text-light" name={name}>
         {headerContent()}
       </ModalHeader>
       <ModalBody>

+ 16 - 1
apps/app/src/features/growi-plugin/client/components/Admin/PluginsExtensionPageContents/PluginsExtensionPageContents.tsx

@@ -3,6 +3,8 @@ import React from 'react';
 import { useTranslation } from 'next-i18next';
 import { Spinner } from 'reactstrap';
 
+import { usePluginDeleteModal } from '~/stores/modal';
+
 import { useSWRxAdminPlugins } from '../../../stores/admin-plugins';
 
 import { PluginCard } from './PluginCard';
@@ -21,6 +23,8 @@ export const PluginsExtensionPageContents = (): JSX.Element => {
 
   const { data, mutate } = useSWRxAdminPlugins();
 
+  const { open: openPluginDeleteModal } = usePluginDeleteModal();
+
   return (
     <div>
       <div className="row mb-5">
@@ -45,12 +49,22 @@ export const PluginsExtensionPageContents = (): JSX.Element => {
                 { data.plugins.length === 0 && (
                   <div>{t('plugins.plugin_is_not_installed')}</div>
                 )}
-                { data.plugins.map((plugin) => {
+                {data.plugins.map((plugin) => {
                   const pluginId = plugin._id;
                   const pluginName = plugin.meta.name;
                   const pluginUrl = plugin.origin.url;
                   const pluginIsEnabled = plugin.isEnabled;
                   const pluginDiscription = plugin.meta.desc;
+                  const onDeleteClicked = () => {
+                    openPluginDeleteModal({
+                      _id: pluginId,
+                      meta: { name: pluginName, types: [], desc: pluginDiscription },
+                      origin: { url: pluginUrl },
+                      isEnabled: pluginIsEnabled,
+                      installedPath: '',
+                      organizationName: '',
+                    });
+                  };
                   return (
                     <PluginCard
                       key={pluginId}
@@ -59,6 +73,7 @@ export const PluginsExtensionPageContents = (): JSX.Element => {
                       url={pluginUrl}
                       isEnalbed={pluginIsEnabled}
                       desc={pluginDiscription}
+                      onDelete={onDeleteClicked}
                       mutate={mutate}
                     />
                   );

+ 68 - 0
apps/app/src/stores/modal.tsx

@@ -13,8 +13,12 @@ import {
 import { IUserGroupHasId } from '~/interfaces/user';
 import loggerFactory from '~/utils/logger';
 
+import { useSWRxAdminPlugins } from '../features/growi-plugin/client/stores/admin-plugins';
+import type { IGrowiPluginHasId } from '../features/growi-plugin/interfaces';
+
 import { useStaticSWR } from './use-static-swr';
 
+
 const logger = loggerFactory('growi:stores:modal');
 
 /*
@@ -730,3 +734,67 @@ export const useLinkEditModal = (): SWRResponse<LinkEditModalStatus, Error> & Li
     },
   });
 };
+
+/*
+ * PluginDeleteModal
+ */
+type PluginDeleteModalStatus = {
+  isShown: boolean,
+  plugins?: IGrowiPluginHasId[]
+  name: string,
+  url: string,
+  id: string,
+}
+
+type PluginDeleteModalUtils = {
+  open(plugin: IGrowiPluginHasId): void,
+  close(): void,
+}
+
+export interface PluginData {
+  id: string;
+  name: string;
+  url: string;
+}
+
+export const usePluginDeleteModal = (): SWRResponse<PluginDeleteModalStatus, Error> & PluginDeleteModalUtils => {
+  const initialStatus: PluginDeleteModalStatus = {
+    isShown: false,
+    plugins: [],
+    name: '',
+    url: '',
+    id: '',
+  };
+
+  const swrResponse = useStaticSWR<PluginDeleteModalStatus, Error>('pluginDeleteModal', undefined, { fallbackData: initialStatus });
+  const { mutate } = swrResponse;
+
+  const open = useCallback((plugin: IGrowiPluginHasId) => {
+
+    // Update the plugins field in the current state with the extractedPlugins
+    mutate({
+      ...swrResponse.data,
+      isShown: true,
+      name: plugin.meta.name,
+      url: plugin.origin.url,
+      id: plugin._id,
+    });
+  }, [mutate, swrResponse.data]);
+
+  const close = useCallback((): void => {
+    mutate({
+      ...swrResponse.data,
+      isShown: false,
+      plugins: [],
+      name: '',
+      url: '',
+      id: '',
+    });
+  }, [mutate, swrResponse.data]);
+
+  return {
+    ...swrResponse,
+    open,
+    close,
+  };
+};