Преглед изворни кода

refs 125405: merge ExternalUserGroup UserGroup detail page

Futa Arai пре 2 година
родитељ
комит
272cdb1ff2

+ 44 - 18
apps/app/src/components/Admin/UserGroupDetail/UserGroupDetailPage.tsx

@@ -42,15 +42,16 @@ const UpdateParentConfirmModal = dynamic(() => import('./UpdateParentConfirmModa
 
 type Props = {
   userGroupId: string,
+  isExternalGroup: boolean,
 }
 
 const UserGroupDetailPage = (props: Props): JSX.Element => {
   const { t } = useTranslation('admin');
   const router = useRouter();
   const xss = useMemo(() => new Xss(), []);
-  const { userGroupId: currentUserGroupId } = props;
+  const { userGroupId: currentUserGroupId, isExternalGroup } = props;
 
-  const { data: currentUserGroup } = useSWRxUserGroup(currentUserGroupId);
+  const { data: currentUserGroup } = useSWRxUserGroup(currentUserGroupId, isExternalGroup);
 
   const [searchType, setSearchType] = useState<SearchType>(SearchTypes.PARTIAL);
   const [isAlsoMailSearched, setAlsoMailSearched] = useState<boolean>(false);
@@ -76,26 +77,36 @@ const UserGroupDetailPage = (props: Props): JSX.Element => {
    */
   const { data: userGroupPages } = useSWRxUserGroupPages(currentUserGroupId, 10, 0);
 
-  const { data: userGroupRelations, mutate: mutateUserGroupRelations } = useSWRxUserGroupRelations(currentUserGroupId);
+  const { data: userGroupRelations, mutate: mutateUserGroupRelations } = useSWRxUserGroupRelations(currentUserGroupId, isExternalGroup);
 
-  const { data: childUserGroupsList, mutate: mutateChildUserGroups } = useSWRxChildUserGroupList(currentUserGroupId ? [currentUserGroupId] : [], true);
+  const { data: childUserGroupsList, mutate: mutateChildUserGroups } = useSWRxChildUserGroupList(
+    currentUserGroupId ? [currentUserGroupId] : [], true, isExternalGroup,
+  );
   const childUserGroups = childUserGroupsList != null ? childUserGroupsList.childUserGroups : [];
   const grandChildUserGroups = childUserGroupsList != null ? childUserGroupsList.grandChildUserGroups : [];
   const childUserGroupIds = childUserGroups.map(group => group._id);
 
-  const { data: userGroupRelationList, mutate: mutateUserGroupRelationList } = useSWRxUserGroupRelationList(childUserGroupIds);
+  const { data: userGroupRelationList, mutate: mutateUserGroupRelationList } = useSWRxUserGroupRelationList(
+    childUserGroupIds, undefined, undefined, isExternalGroup,
+  );
   const childUserGroupRelations = userGroupRelationList != null ? userGroupRelationList : [];
 
   const { data: selectableParentUserGroups, mutate: mutateSelectableParentUserGroups } = useSWRxSelectableParentUserGroups(currentUserGroupId);
   const { data: selectableChildUserGroups, mutate: mutateSelectableChildUserGroups } = useSWRxSelectableChildUserGroups(currentUserGroupId);
 
-  const { data: ancestorUserGroups, mutate: mutateAncestorUserGroups } = useSWRxAncestorUserGroups(currentUserGroupId);
+  const { data: ancestorUserGroups, mutate: mutateAncestorUserGroups } = useSWRxAncestorUserGroups(currentUserGroupId, isExternalGroup);
 
   const { data: isAclEnabled } = useIsAclEnabled();
 
   const { open: openUpdateParentConfirmModal } = useUpdateUserGroupConfirmModal();
 
-  const parentUserGroup = selectableParentUserGroups?.find(selectableParentUserGroup => selectableParentUserGroup._id === currentUserGroup?.parent);
+  const parentUserGroup = (() => {
+    if (isExternalGroup) {
+      return ancestorUserGroups != null && ancestorUserGroups.length > 1
+        ? ancestorUserGroups[ancestorUserGroups.length - 2] : undefined;
+    }
+    return selectableParentUserGroups?.find(selectableParentUserGroup => selectableParentUserGroup._id === currentUserGroup?.parent);
+  })();
   /*
    * Function
    */
@@ -213,11 +224,18 @@ const UserGroupDetailPage = (props: Props): JSX.Element => {
 
   const updateChildUserGroup = useCallback(async(userGroupData: IUserGroupHasId) => {
     try {
-      await apiv3Put(`/user-groups/${userGroupData._id}`, {
-        name: userGroupData.name,
-        description: userGroupData.description,
-        parentId: userGroupData.parent,
-      });
+      if (isExternalGroup) {
+        await apiv3Put(`/external-user-groups/${userGroupData._id}`, {
+          description: userGroupData.description,
+        });
+      }
+      else {
+        await apiv3Put(`/user-groups/${userGroupData._id}`, {
+          name: userGroupData.name,
+          description: userGroupData.description,
+          parentId: userGroupData.parent,
+        });
+      }
 
       toastSuccess(t('toaster.update_successed', { target: t('UserGroup'), ns: 'commons' }));
 
@@ -229,7 +247,7 @@ const UserGroupDetailPage = (props: Props): JSX.Element => {
     catch (err) {
       toastError(err);
     }
-  }, [t, mutateChildUserGroups, hideUpdateModal]);
+  }, [t, mutateChildUserGroups, hideUpdateModal, isExternalGroup]);
 
   const onClickAddExistingUserGroupButtonHandler = useCallback(async(selectedChild: IUserGroupHasId): Promise<void> => {
     // show confirm modal before submiting
@@ -283,8 +301,9 @@ const UserGroupDetailPage = (props: Props): JSX.Element => {
   }, [setSelectedUserGroup, setDeleteModalShown]);
 
   const deleteChildUserGroupById = useCallback(async(deleteGroupId: string, actionName: string, transferToUserGroupId: string) => {
+    const url = isExternalGroup ? `/external-user-groups/${deleteGroupId}` : `/user-groups/${deleteGroupId}`;
     try {
-      const res = await apiv3Delete(`/user-groups/${deleteGroupId}`, {
+      const res = await apiv3Delete(url, {
         actionName,
         transferToUserGroupId,
       });
@@ -300,7 +319,7 @@ const UserGroupDetailPage = (props: Props): JSX.Element => {
     catch (err) {
       toastError(new Error('Unable to delete the groups'));
     }
-  }, [mutateChildUserGroups, setSelectedUserGroup, setDeleteModalShown]);
+  }, [mutateChildUserGroups, setSelectedUserGroup, setDeleteModalShown, isExternalGroup]);
 
   const removeChildUserGroup = useCallback(async(userGroupData: IUserGroupHasId) => {
     try {
@@ -348,7 +367,10 @@ const UserGroupDetailPage = (props: Props): JSX.Element => {
                 { ancestorUserGroup._id === currentUserGroupId ? (
                   <span>{ancestorUserGroup.name}</span>
                 ) : (
-                  <Link href={`/admin/user-group-detail/${ancestorUserGroup._id}`} prefetch={false}>
+                  <Link href={{
+                    pathname: `/admin/user-group-detail/${ancestorUserGroup._id}`,
+                    query: { isExternalGroup: 'true' },
+                  }} prefetch={false}>
                     {ancestorUserGroup.name}
                   </Link>
                 ) }
@@ -366,6 +388,7 @@ const UserGroupDetailPage = (props: Props): JSX.Element => {
           selectableParentUserGroups={selectableParentUserGroups}
           submitButtonLabel={t('Update')}
           onSubmit={onClickSubmitForm}
+          isExternalGroup
         />
       </div>
       <h2 className="admin-setting-header mt-4">{t('user_group_management.user_list')}</h2>
@@ -373,6 +396,7 @@ const UserGroupDetailPage = (props: Props): JSX.Element => {
         userGroupRelations={userGroupRelations}
         onClickPlusBtn={() => setIsUserGroupUserModalShown(true)}
         onClickRemoveUserBtn={removeUserByUsername}
+        isExternalGroup
       />
       <UserGroupUserModal
         isOpen={isUserGroupUserModalShown}
@@ -389,11 +413,11 @@ const UserGroupDetailPage = (props: Props): JSX.Element => {
       />
 
       <h2 className="admin-setting-header mt-4">{t('user_group_management.child_group_list')}</h2>
-      <UserGroupDropdown
+      {!isExternalGroup && <UserGroupDropdown
         selectableUserGroups={selectableChildUserGroups}
         onClickAddExistingUserGroupButton={onClickAddExistingUserGroupButtonHandler}
         onClickCreateUserGroupButton={showCreateModal}
-      />
+      />}
 
       <UserGroupModal
         userGroup={selectedUserGroup}
@@ -401,6 +425,7 @@ const UserGroupDetailPage = (props: Props): JSX.Element => {
         onClickSubmit={updateChildUserGroup}
         isShow={isUpdateModalShown}
         onHide={hideUpdateModal}
+        isExternalGroup
       />
 
       <UserGroupModal
@@ -420,6 +445,7 @@ const UserGroupDetailPage = (props: Props): JSX.Element => {
         onRemove={removeChildUserGroup}
         onDelete={showDeleteModal}
         userGroupRelations={childUserGroupRelations}
+        isExternalGroup
       />
 
       <UserGroupDeleteModal

+ 1 - 4
apps/app/src/components/Admin/UserGroupDetail/UserGroupUserTable.tsx

@@ -10,10 +10,7 @@ type Props = {
   userGroupRelations: IUserGroupRelationHasIdPopulatedUser[] | undefined,
   onClickRemoveUserBtn: (username: string) => Promise<void>,
   onClickPlusBtn: () => void,
-  isExternalGroup?: false
-} | {
-  userGroupRelations: IUserGroupRelationHasIdPopulatedUser[] | undefined,
-  isExternalGroup: true
+  isExternalGroup?: boolean
 }
 
 export const UserGroupUserTable = (props: Props): JSX.Element => {

+ 4 - 8
apps/app/src/features/external-user-group/client/components/ExternalUserGroup/ExternalUserGroupManagement.tsx

@@ -11,25 +11,21 @@ import { UserGroupDeleteModal } from '~/components/Admin/UserGroup/UserGroupDele
 import { UserGroupModal } from '~/components/Admin/UserGroup/UserGroupModal';
 import { UserGroupTable } from '~/components/Admin/UserGroup/UserGroupTable';
 import CustomNav from '~/components/CustomNavigation/CustomNav';
-import {
-  useSWRxChildExternalUserGroupList,
-  useSWRxExternalUserGroupList,
-  useSWRxExternalUserGroupRelationList,
-} from '~/features/external-user-group/client/stores/external-user-group';
 import { IExternalUserGroupHasId } from '~/features/external-user-group/interfaces/external-user-group';
 import { useIsAclEnabled } from '~/stores/context';
+import { useSWRxChildUserGroupList, useSWRxUserGroupList, useSWRxUserGroupRelationList } from '~/stores/user-group';
 
 import { LdapGroupManagement } from './LdapGroupManagement';
 
 export const ExternalGroupManagement: FC = () => {
-  const { data: externalUserGroupList, mutate: mutateExternalUserGroups } = useSWRxExternalUserGroupList();
+  const { data: externalUserGroupList, mutate: mutateExternalUserGroups } = useSWRxUserGroupList(undefined, true);
   const externalUserGroups = externalUserGroupList != null ? externalUserGroupList : [];
   const externalUserGroupIds = externalUserGroups.map(group => group._id);
 
-  const { data: externalUserGroupRelationList } = useSWRxExternalUserGroupRelationList(externalUserGroupIds);
+  const { data: externalUserGroupRelationList } = useSWRxUserGroupRelationList(externalUserGroupIds, undefined, undefined, true);
   const externalUserGroupRelations = externalUserGroupRelationList != null ? externalUserGroupRelationList : [];
 
-  const { data: childExternalUserGroupsList } = useSWRxChildExternalUserGroupList(externalUserGroupIds);
+  const { data: childExternalUserGroupsList } = useSWRxChildUserGroupList(externalUserGroupIds, false, true);
   const childExternalUserGroups = childExternalUserGroupsList?.childUserGroups != null ? childExternalUserGroupsList.childUserGroups : [];
 
   const { data: isAclEnabled } = useIsAclEnabled();

+ 0 - 242
apps/app/src/features/external-user-group/client/components/ExternalUserGroupDetail.tsx

@@ -1,242 +0,0 @@
-import React, {
-  useState, useCallback, useEffect,
-} from 'react';
-
-import { objectIdUtils } from '@growi/core';
-import { useTranslation } from 'next-i18next';
-import Link from 'next/link';
-import { useRouter } from 'next/router';
-
-import {
-  apiv3Put, apiv3Delete,
-} from '~/client/util/apiv3-client';
-import { toastSuccess, toastError } from '~/client/util/toastr';
-import { UserGroupDeleteModal } from '~/components/Admin/UserGroup/UserGroupDeleteModal';
-import { UserGroupForm } from '~/components/Admin/UserGroup/UserGroupForm';
-import { UserGroupModal } from '~/components/Admin/UserGroup/UserGroupModal';
-import { UserGroupTable } from '~/components/Admin/UserGroup/UserGroupTable';
-import styles from '~/components/Admin/UserGroupDetail/UserGroupDetailPage.module.scss';
-import { UserGroupUserTable } from '~/components/Admin/UserGroupDetail/UserGroupUserTable';
-import {
-  useSWRxAncestorExternalUserGroups,
-  useSWRxChildExternalUserGroupList, useSWRxExternalUserGroup, useSWRxExternalUserGroupRelationList, useSWRxExternalUserGroupRelations,
-} from '~/features/external-user-group/client/stores/external-user-group';
-import { IExternalUserGroupHasId } from '~/features/external-user-group/interfaces/external-user-group';
-import { useIsAclEnabled } from '~/stores/context';
-
-type Props = {
-  externalUserGroupId: string,
-}
-
-const ExternalUserGroupDetailPage = (props: Props): JSX.Element => {
-  const { t } = useTranslation('admin');
-  const router = useRouter();
-  const { externalUserGroupId: currentExternalUserGroupId } = props;
-
-  const { data: currentExternalUserGroup } = useSWRxExternalUserGroup(currentExternalUserGroupId);
-  const [selectedExternalUserGroup, setSelectedExternalUserGroup] = useState<IExternalUserGroupHasId | undefined>(undefined); // not null but undefined (to use defaultProps in UserGroupDeleteModal)
-  const [isUpdateModalShown, setUpdateModalShown] = useState<boolean>(false);
-  const [isDeleteModalShown, setDeleteModalShown] = useState<boolean>(false);
-
-  const isLoading = currentExternalUserGroup === undefined;
-  const notExistsUerGroup = !isLoading && currentExternalUserGroup == null;
-
-  useEffect(() => {
-    if (!objectIdUtils.isValidObjectId(currentExternalUserGroupId) || notExistsUerGroup) {
-      router.push('/admin/user-groups');
-    }
-  }, [currentExternalUserGroup, currentExternalUserGroupId, notExistsUerGroup, router]);
-
-
-  // TODO: fetch pages (https://redmine.weseek.co.jp/issues/124385)
-  // const { data: userGroupPages } = useSWRxUserGroupPages(currentExternalUserGroupId, 10, 0);
-
-  const { data: externalUserGroupRelations } = useSWRxExternalUserGroupRelations(currentExternalUserGroupId);
-
-  const { data: childExternalUserGroupsList, mutate: mutateExternalChildUserGroups } = useSWRxChildExternalUserGroupList(
-    currentExternalUserGroupId ? [currentExternalUserGroupId] : [], true,
-  );
-  const childUserGroups = childExternalUserGroupsList != null ? childExternalUserGroupsList.childUserGroups : [];
-  const grandChildUserGroups = childExternalUserGroupsList != null ? childExternalUserGroupsList.grandChildUserGroups : [];
-  const childUserGroupIds = childUserGroups.map(group => group._id);
-
-  const { data: externalUserGroupRelationList } = useSWRxExternalUserGroupRelationList(childUserGroupIds);
-  const childUserGroupRelations = externalUserGroupRelationList != null ? externalUserGroupRelationList : [];
-
-  const { data: ancestorExternalUserGroups } = useSWRxAncestorExternalUserGroups(currentExternalUserGroupId);
-
-  const { data: isAclEnabled } = useIsAclEnabled();
-
-  const parentUserGroup = ancestorExternalUserGroups != null && ancestorExternalUserGroups.length > 1
-    ? ancestorExternalUserGroups[ancestorExternalUserGroups.length - 2] : undefined;
-
-  const selectableParentUserGroups = parentUserGroup != null ? [parentUserGroup] : [];
-
-  const showUpdateModal = useCallback((group: IExternalUserGroupHasId) => {
-    setUpdateModalShown(true);
-    setSelectedExternalUserGroup(group);
-  }, [setUpdateModalShown]);
-
-  const hideUpdateModal = useCallback(() => {
-    setUpdateModalShown(false);
-    setSelectedExternalUserGroup(undefined);
-  }, [setUpdateModalShown]);
-
-  const updateChildExternalUserGroup = useCallback(async(userGroupData: IExternalUserGroupHasId) => {
-    try {
-      await apiv3Put(`/external-user-groups/${userGroupData._id}`, {
-        description: userGroupData.description,
-      });
-
-      toastSuccess(t('toaster.update_successed', { target: t('UserGroup'), ns: 'commons' }));
-
-      // mutate
-      mutateExternalChildUserGroups();
-
-      hideUpdateModal();
-    }
-    catch (err) {
-      toastError(err);
-    }
-  }, [t, mutateExternalChildUserGroups, hideUpdateModal]);
-
-  const showDeleteModal = useCallback(async(group: IExternalUserGroupHasId) => {
-    setSelectedExternalUserGroup(group);
-    setDeleteModalShown(true);
-  }, [setSelectedExternalUserGroup, setDeleteModalShown]);
-
-  const hideDeleteModal = useCallback(() => {
-    setSelectedExternalUserGroup(undefined);
-    setDeleteModalShown(false);
-  }, [setSelectedExternalUserGroup, setDeleteModalShown]);
-
-  const deleteChildUserGroupById = useCallback(async(deleteGroupId: string, actionName: string, transferToUserGroupId: string) => {
-    try {
-      const res = await apiv3Delete(`/user-groups/${deleteGroupId}`, {
-        actionName,
-        transferToUserGroupId,
-      });
-
-      // sync
-      await mutateExternalChildUserGroups();
-
-      setSelectedExternalUserGroup(undefined);
-      setDeleteModalShown(false);
-
-      toastSuccess(`Deleted ${res.data.userGroups.length} groups.`);
-    }
-    catch (err) {
-      toastError(new Error('Unable to delete the groups'));
-    }
-  }, [mutateExternalChildUserGroups, setSelectedExternalUserGroup, setDeleteModalShown]);
-
-  const onClickSubmitForm = useCallback(async(targetGroup: IExternalUserGroupHasId, userGroupData: IExternalUserGroupHasId) => {
-    try {
-      await apiv3Put(`/external-user-groups/${targetGroup._id}`, {
-        description: userGroupData.description,
-      });
-      toastSuccess(t('toaster.update_successed', { target: t('ExternalUserGroup'), ns: 'commons' }));
-    }
-    catch {
-      toastError(t('toaster.update_failed', { target: t('ExternalUserGroup'), ns: 'commons' }));
-    }
-  }, [t]);
-
-  /*
-   * Dependencies
-   */
-  if (currentExternalUserGroup == null || currentExternalUserGroupId == null) {
-    return <></>;
-  }
-
-  return (
-    <div>
-      <nav aria-label="breadcrumb">
-        <ol className="breadcrumb">
-          <li className="breadcrumb-item">
-            <Link href="/admin/user-groups" prefetch={false}>
-              {t('user_group_management.group_list')}
-            </Link>
-          </li>
-          {
-            ancestorExternalUserGroups != null && ancestorExternalUserGroups.length > 0
-            && (ancestorExternalUserGroups.map((ancestorExternalUserGroup: IExternalUserGroupHasId) => (
-              <li
-                key={ancestorExternalUserGroup._id}
-                className={`breadcrumb-item ${ancestorExternalUserGroup._id === currentExternalUserGroupId ? 'active' : ''}`}
-                aria-current="page"
-              >
-                { ancestorExternalUserGroup._id === currentExternalUserGroupId ? (
-                  <span>{ancestorExternalUserGroup.name}</span>
-                ) : (
-                  <Link
-                    href={{
-                      pathname: `/admin/user-group-detail/${ancestorExternalUserGroup._id}`,
-                      query: { isExternalGroup: 'true' },
-                    }}
-                    prefetch={false}>
-                    {ancestorExternalUserGroup.name}
-                  </Link>
-                ) }
-              </li>
-            ))
-            )
-          }
-        </ol>
-      </nav>
-
-      <div className="mt-4 form-box">
-        <UserGroupForm
-          userGroup={currentExternalUserGroup}
-          parentUserGroup={parentUserGroup}
-          selectableParentUserGroups={selectableParentUserGroups}
-          submitButtonLabel={t('Update')}
-          onSubmit={onClickSubmitForm}
-          isExternalGroup
-        />
-      </div>
-      <h2 className="admin-setting-header mt-4">{t('user_group_management.user_list')}</h2>
-      <UserGroupUserTable
-        userGroupRelations={externalUserGroupRelations}
-        isExternalGroup
-      />
-
-      <h2 className="admin-setting-header mt-4">{t('user_group_management.child_group_list')}</h2>
-
-      <UserGroupModal
-        userGroup={selectedExternalUserGroup}
-        buttonLabel={t('Update')}
-        onClickSubmit={updateChildExternalUserGroup}
-        isShow={isUpdateModalShown}
-        onHide={hideUpdateModal}
-        isExternalGroup
-      />
-
-      <UserGroupTable
-        userGroups={childUserGroups}
-        childUserGroups={grandChildUserGroups}
-        isAclEnabled={isAclEnabled ?? false}
-        onEdit={showUpdateModal}
-        onDelete={showDeleteModal}
-        userGroupRelations={childUserGroupRelations}
-        isExternalGroup
-      />
-
-      <UserGroupDeleteModal
-        userGroups={childUserGroups}
-        deleteUserGroup={selectedExternalUserGroup}
-        onDelete={deleteChildUserGroupById}
-        isShow={isDeleteModalShown}
-        onHide={hideDeleteModal}
-      />
-
-      {/* show user group pages (https://redmine.weseek.co.jp/issues/124385) */}
-      {/* <h2 className="admin-setting-header mt-4">{t('Page')}</h2>
-      <div className={`page-list ${styles['page-list']}`}>
-        <UserGroupPageList userGroupId={currentExternalUserGroupId} relatedPages={userGroupPages} />
-      </div> */}
-    </div>
-  );
-};
-
-export default ExternalUserGroupDetailPage;

+ 1 - 60
apps/app/src/features/external-user-group/client/stores/external-user-group.ts

@@ -1,9 +1,7 @@
 import useSWR, { SWRResponse } from 'swr';
-import useSWRImmutable from 'swr/immutable';
 
 import { apiv3Get } from '~/client/util/apiv3-client';
-import { IExternalUserGroupHasId, IExternalUserGroupRelationHasId, LdapGroupSyncSettings } from '~/features/external-user-group/interfaces/external-user-group';
-import { ChildUserGroupListResult, IUserGroupRelationHasIdPopulatedUser, UserGroupRelationListResult } from '~/interfaces/user-group-response';
+import { LdapGroupSyncSettings } from '~/features/external-user-group/interfaces/external-user-group';
 
 export const useSWRxLdapGroupSyncSettings = (): SWRResponse<LdapGroupSyncSettings, Error> => {
   return useSWR(
@@ -13,60 +11,3 @@ export const useSWRxLdapGroupSyncSettings = (): SWRResponse<LdapGroupSyncSetting
     }),
   );
 };
-
-export const useSWRxExternalUserGroup = (groupId: string): SWRResponse<IExternalUserGroupHasId, Error> => {
-  return useSWRImmutable(
-    `/external-user-groups/${groupId}`,
-    endpoint => apiv3Get(endpoint).then(result => result.data.externalUserGroup),
-  );
-};
-
-export const useSWRxExternalUserGroupList = (initialData?: IExternalUserGroupHasId[]): SWRResponse<IExternalUserGroupHasId[], Error> => {
-  return useSWRImmutable(
-    '/external-user-groups',
-    endpoint => apiv3Get(endpoint, { pagination: false }).then(result => result.data.externalUserGroups),
-    {
-      fallbackData: initialData,
-    },
-  );
-};
-
-export const useSWRxChildExternalUserGroupList = (
-    parentIds?: string[], includeGrandChildren?: boolean,
-): SWRResponse<ChildUserGroupListResult<IExternalUserGroupHasId>, Error> => {
-  const shouldFetch = parentIds != null && parentIds.length > 0;
-  return useSWRImmutable(
-    shouldFetch ? ['/external-user-groups/children', parentIds, includeGrandChildren] : null,
-    ([endpoint, parentIds, includeGrandChildren]) => apiv3Get<ChildUserGroupListResult<IExternalUserGroupHasId>>(
-      endpoint, { parentIds, includeGrandChildren },
-    ).then((result => result.data)),
-  );
-};
-
-export const useSWRxExternalUserGroupRelations = (groupId: string): SWRResponse<IUserGroupRelationHasIdPopulatedUser[], Error> => {
-  return useSWRImmutable(
-    groupId != null ? `/external-user-groups/${groupId}/external-user-group-relations` : null,
-    endpoint => apiv3Get(endpoint).then(result => result.data.userGroupRelations),
-  );
-};
-
-export const useSWRxExternalUserGroupRelationList = (
-    groupIds: string[] | undefined, childGroupIds?: string[], initialData?: IExternalUserGroupRelationHasId[],
-): SWRResponse<IExternalUserGroupRelationHasId[], Error> => {
-  return useSWRImmutable(
-    groupIds != null ? ['/external-user-group-relations', groupIds, childGroupIds] : null,
-    ([endpoint, groupIds, childGroupIds]) => apiv3Get<UserGroupRelationListResult<IExternalUserGroupRelationHasId>>(
-      endpoint, { groupIds, childGroupIds },
-    ).then(result => result.data.userGroupRelations),
-    {
-      fallbackData: initialData,
-    },
-  );
-};
-
-export const useSWRxAncestorExternalUserGroups = (groupId?: string): SWRResponse<IExternalUserGroupHasId[], Error> => {
-  return useSWRImmutable(
-    groupId != null ? ['/external-user-groups/ancestors', groupId] : null,
-    ([endpoint, groupId]) => apiv3Get(endpoint, { groupId }).then(result => result.data.ancestorExternalUserGroups),
-  );
-};

+ 8 - 8
apps/app/src/features/external-user-group/server/routes/apiv3/external-user-group.ts

@@ -78,8 +78,8 @@ module.exports = (crowi: Crowi): Router => {
       const result = await ExternalUserGroup.findWithPagination({
         page, limit, offset, pagination,
       });
-      const { docs: externalUserGroups, totalDocs: totalExternalUserGroups, limit: pagingLimit } = result;
-      return res.apiv3({ externalUserGroups, totalExternalUserGroups, pagingLimit });
+      const { docs: userGroups, totalDocs: totalUserGroups, limit: pagingLimit } = result;
+      return res.apiv3({ userGroups, totalUserGroups, pagingLimit });
     }
     catch (err) {
       const msg = 'Error occurred in fetching external user group list';
@@ -93,8 +93,8 @@ module.exports = (crowi: Crowi): Router => {
 
     try {
       const userGroup = await ExternalUserGroup.findById(groupId);
-      const ancestorExternalUserGroups = await ExternalUserGroup.findGroupsWithAncestorsRecursively(userGroup);
-      return res.apiv3({ ancestorExternalUserGroups });
+      const ancestorUserGroups = await ExternalUserGroup.findGroupsWithAncestorsRecursively(userGroup);
+      return res.apiv3({ ancestorUserGroups });
     }
     catch (err) {
       const msg = 'Error occurred while searching user groups';
@@ -124,8 +124,8 @@ module.exports = (crowi: Crowi): Router => {
     const { id } = req.params;
 
     try {
-      const externalUserGroup = await ExternalUserGroup.findById(id);
-      return res.apiv3({ externalUserGroup });
+      const userGroup = await ExternalUserGroup.findById(id);
+      return res.apiv3({ userGroup });
     }
     catch (err) {
       const msg = 'Error occurred while getting external user group';
@@ -165,12 +165,12 @@ module.exports = (crowi: Crowi): Router => {
     } = req.body;
 
     try {
-      const externalUserGroup = await ExternalUserGroup.findOneAndUpdate({ _id: id }, { description });
+      const userGroup = await ExternalUserGroup.findOneAndUpdate({ _id: id }, { description });
 
       const parameters = { action: SupportedAction.ACTION_ADMIN_USER_GROUP_UPDATE };
       activityEvent.emit('update', res.locals.activity._id, parameters);
 
-      return res.apiv3({ externalUserGroup });
+      return res.apiv3({ userGroup });
     }
     catch (err) {
       const msg = 'Error occurred in updating an external user group';

+ 7 - 7
apps/app/src/interfaces/user-group-response.ts

@@ -13,17 +13,17 @@ export type UserGroupListResult = {
   userGroups: IUserGroupHasId[],
 };
 
-export type ChildUserGroupListResult<TUSERGROUP extends IUserGroupHasId = IUserGroupHasId> = {
-  childUserGroups: TUSERGROUP[],
-  grandChildUserGroups: TUSERGROUP[],
+export type ChildUserGroupListResult = {
+  childUserGroups: IUserGroupHasId[],
+  grandChildUserGroups: IUserGroupHasId[],
 };
 
-export type UserGroupRelationListResult<TUSERGROUPRELATION extends IUserGroupRelationHasId = IUserGroupRelationHasId> = {
-  userGroupRelations: TUSERGROUPRELATION[],
+export type UserGroupRelationListResult = {
+  userGroupRelations: IUserGroupRelationHasId[],
 };
 
-export type IUserGroupRelationHasIdPopulatedUser<TUSERGROUP extends IUserGroup = IUserGroup> = {
-  relatedGroup: Ref<TUSERGROUP>,
+export type IUserGroupRelationHasIdPopulatedUser = {
+  relatedGroup: Ref<IUserGroup>,
   relatedUser: IUserHasId,
   createdAt: Date,
 } & HasObjectId;

+ 1 - 4
apps/app/src/pages/admin/user-group-detail/[userGroupId].page.tsx

@@ -6,7 +6,6 @@ import dynamic from 'next/dynamic';
 import Head from 'next/head';
 import { useRouter } from 'next/router';
 
-import ExternalUserGroupDetailPage from '~/features/external-user-group/client/components/ExternalUserGroupDetail';
 import { CrowiRequest } from '~/interfaces/crowi-request';
 import { CommonProps, generateCustomTitle } from '~/pages/utils/commons';
 import { useIsAclEnabled, useCurrentUser } from '~/stores/context';
@@ -44,9 +43,7 @@ const AdminUserGroupDetailPage: NextPage<Props> = (props: Props) => {
       </Head>
       {
         currentUserGroupId != null && router.isReady
-      && (isExternalGroupBool
-        ? <ExternalUserGroupDetailPage externalUserGroupId={currentUserGroupId}/>
-        : <UserGroupDetailPage userGroupId={currentUserGroupId} />)
+      && <UserGroupDetailPage userGroupId={currentUserGroupId} isExternalGroup={isExternalGroupBool} />
       }
     </AdminLayout>
   );

+ 18 - 12
apps/app/src/stores/user-group.tsx

@@ -23,16 +23,18 @@ export const useSWRxMyUserGroupRelations = (shouldFetch: boolean): SWRResponse<I
   );
 };
 
-export const useSWRxUserGroup = (groupId: string | undefined): SWRResponse<IUserGroupHasId, Error> => {
+export const useSWRxUserGroup = (groupId: string | undefined, isExternalGroup = false): SWRResponse<IUserGroupHasId, Error> => {
+  const url = isExternalGroup ? `/external-user-groups/${groupId}` : `/user-groups/${groupId}`;
   return useSWRImmutable(
-    groupId != null ? `/user-groups/${groupId}` : null,
+    groupId != null ? url : null,
     endpoint => apiv3Get<UserGroupResult>(endpoint).then(result => result.data.userGroup),
   );
 };
 
-export const useSWRxUserGroupList = (initialData?: IUserGroupHasId[]): SWRResponse<IUserGroupHasId[], Error> => {
+export const useSWRxUserGroupList = (initialData?: IUserGroupHasId[], isExternalGroup = false): SWRResponse<IUserGroupHasId[], Error> => {
+  const url = isExternalGroup ? '/external-user-groups' : '/user-groups';
   return useSWRImmutable(
-    '/user-groups',
+    url,
     endpoint => apiv3Get<UserGroupListResult>(endpoint, { pagination: false }).then(result => result.data.userGroups),
     {
       fallbackData: initialData,
@@ -41,29 +43,32 @@ export const useSWRxUserGroupList = (initialData?: IUserGroupHasId[]): SWRRespon
 };
 
 export const useSWRxChildUserGroupList = (
-    parentIds?: string[], includeGrandChildren?: boolean,
+    parentIds?: string[], includeGrandChildren?: boolean, isExternalGroup = false,
 ): SWRResponse<ChildUserGroupListResult, Error> => {
   const shouldFetch = parentIds != null && parentIds.length > 0;
+  const url = isExternalGroup ? '/external-user-groups/children' : '/user-groups/children';
   return useSWRImmutable(
-    shouldFetch ? ['/user-groups/children', parentIds, includeGrandChildren] : null,
+    shouldFetch ? [url, parentIds, includeGrandChildren] : null,
     ([endpoint, parentIds, includeGrandChildren]) => apiv3Get<ChildUserGroupListResult>(
       endpoint, { parentIds, includeGrandChildren },
     ).then((result => result.data)),
   );
 };
 
-export const useSWRxUserGroupRelations = (groupId: string): SWRResponse<IUserGroupRelationHasIdPopulatedUser[], Error> => {
+export const useSWRxUserGroupRelations = (groupId: string, isExternalGroup = false): SWRResponse<IUserGroupRelationHasIdPopulatedUser[], Error> => {
+  const url = isExternalGroup ? `/external-user-groups/${groupId}/external-user-group-relations` : `/user-groups/${groupId}/user-group-relations`;
   return useSWRImmutable(
-    groupId != null ? `/user-groups/${groupId}/user-group-relations` : null,
+    groupId != null ? url : null,
     endpoint => apiv3Get<UserGroupRelationsResult>(endpoint).then(result => result.data.userGroupRelations),
   );
 };
 
 export const useSWRxUserGroupRelationList = (
-    groupIds: string[] | undefined, childGroupIds?: string[], initialData?: IUserGroupRelationHasId[],
+    groupIds: string[] | undefined, childGroupIds?: string[], initialData?: IUserGroupRelationHasId[], isExternalGroup = false,
 ): SWRResponse<IUserGroupRelationHasId[], Error> => {
+  const url = isExternalGroup ? '/external-user-group-relations' : '/user-group-relations';
   return useSWRImmutable(
-    groupIds != null ? ['/user-group-relations', groupIds, childGroupIds] : null,
+    groupIds != null ? [url, groupIds, childGroupIds] : null,
     ([endpoint, groupIds, childGroupIds]) => apiv3Get<UserGroupRelationListResult>(
       endpoint, { groupIds, childGroupIds },
     ).then(result => result.data.userGroupRelations),
@@ -94,9 +99,10 @@ export const useSWRxSelectableChildUserGroups = (groupId: string | undefined): S
   );
 };
 
-export const useSWRxAncestorUserGroups = (groupId: string | undefined): SWRResponse<IUserGroupHasId[], Error> => {
+export const useSWRxAncestorUserGroups = (groupId: string | undefined, isExternalGroup = false): SWRResponse<IUserGroupHasId[], Error> => {
+  const url = isExternalGroup ? '/external-user-groups/ancestors' : '/user-groups/ancestors';
   return useSWRImmutable(
-    groupId != null ? ['/user-groups/ancestors', groupId] : null,
+    groupId != null ? [url, groupId] : null,
     ([endpoint, groupId]) => apiv3Get<AncestorUserGroupsResult>(endpoint, { groupId }).then(result => result.data.ancestorUserGroups),
   );
 };