2
0
Эх сурвалжийг харах

Merge pull request #8519 from weseek/fix/141310-141312-only-ug-or-eug-is-shown-for-group-delete-transfer

fix: Show both UserGroups and ExternalUserGroups for group delete modal transfer select
Yuki Takei 2 жил өмнө
parent
commit
3b6bcce5da

+ 25 - 20
apps/app/src/components/Admin/UserGroup/UserGroupDeleteModal.tsx

@@ -1,8 +1,9 @@
-import React, {
-  FC, useCallback, useState, useMemo,
-} from 'react';
+import type { FC } from 'react';
+import React, { useCallback, useState, useMemo } from 'react';
 
 
-import type { IUserGroupHasId } from '@growi/core';
+import {
+  getIdForRef, isPopulated, type IGrantedGroup, type IUserGroupHasId,
+} from '@growi/core';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import {
 import {
   Modal, ModalHeader, ModalBody, ModalFooter,
   Modal, ModalHeader, ModalBody, ModalFooter,
@@ -17,9 +18,9 @@ import {
  * @extends {React.Component}
  * @extends {React.Component}
  */
  */
 type Props = {
 type Props = {
-  userGroups: IUserGroupHasId[],
+  userGroups: IGrantedGroup[],
   deleteUserGroup?: IUserGroupHasId,
   deleteUserGroup?: IUserGroupHasId,
-  onDelete?: (deleteGroupId: string, actionName: string, transferToUserGroupId: string) => Promise<void> | void,
+  onDelete?: (deleteGroupId: string, actionName: string, transferToUserGroup: IGrantedGroup | null) => Promise<void> | void,
   isShow: boolean,
   isShow: boolean,
   onHide?: () => Promise<void> | void,
   onHide?: () => Promise<void> | void,
 };
 };
@@ -77,14 +78,14 @@ export const UserGroupDeleteModal: FC<Props> = (props: Props) => {
    * State
    * State
    */
    */
   const [actionName, setActionName] = useState<string>('');
   const [actionName, setActionName] = useState<string>('');
-  const [transferToUserGroupId, setTransferToUserGroupId] = useState<string>('');
+  const [transferToUserGroup, setTransferToUserGroup] = useState<IGrantedGroup | null>(null);
 
 
   /*
   /*
    * Function
    * Function
    */
    */
   const resetStates = useCallback(() => {
   const resetStates = useCallback(() => {
     setActionName('');
     setActionName('');
-    setTransferToUserGroupId('');
+    setTransferToUserGroup(null);
   }, []);
   }, []);
 
 
   const toggleHandler = useCallback(() => {
   const toggleHandler = useCallback(() => {
@@ -103,8 +104,9 @@ export const UserGroupDeleteModal: FC<Props> = (props: Props) => {
 
 
   const handleGroupChange = useCallback((e) => {
   const handleGroupChange = useCallback((e) => {
     const transferToUserGroupId = e.target.value;
     const transferToUserGroupId = e.target.value;
-    setTransferToUserGroupId(transferToUserGroupId);
-  }, []);
+    const selectedGroup = userGroups.find(group => getIdForRef(group.item) === transferToUserGroupId) ?? null;
+    setTransferToUserGroup(selectedGroup);
+  }, [userGroups]);
 
 
   const handleSubmit = useCallback((e) => {
   const handleSubmit = useCallback((e) => {
     if (onDelete == null || deleteUserGroup == null) {
     if (onDelete == null || deleteUserGroup == null) {
@@ -116,9 +118,9 @@ export const UserGroupDeleteModal: FC<Props> = (props: Props) => {
     onDelete(
     onDelete(
       deleteUserGroup._id,
       deleteUserGroup._id,
       actionName,
       actionName,
-      transferToUserGroupId,
+      transferToUserGroup,
     );
     );
-  }, [onDelete, deleteUserGroup, actionName, transferToUserGroupId]);
+  }, [onDelete, deleteUserGroup, actionName, transferToUserGroup]);
 
 
   const renderPageActionSelector = useCallback(() => {
   const renderPageActionSelector = useCallback(() => {
     const options = availableOptions.map((opt) => {
     const options = availableOptions.map((opt) => {
@@ -145,28 +147,31 @@ export const UserGroupDeleteModal: FC<Props> = (props: Props) => {
     }
     }
 
 
     const groups = userGroups.filter((group) => {
     const groups = userGroups.filter((group) => {
-      return group._id !== deleteUserGroup._id;
+      return getIdForRef(group.item) !== deleteUserGroup._id;
     });
     });
 
 
     const options = groups.map((group) => {
     const options = groups.map((group) => {
-      return <option key={group._id} value={group._id}>{group.name}</option>;
-    });
+      const groupId = getIdForRef(group.item);
+      const groupName = isPopulated(group.item) ? group.item.name : null;
+      return { id: groupId, name: groupName };
+    }).filter(obj => obj.name != null)
+      .map(obj => <option key={obj.id} value={obj.id}>{obj.name}</option>);
 
 
     const defaultOptionText = groups.length === 0 ? t('admin:user_group_management.delete_modal.no_groups')
     const defaultOptionText = groups.length === 0 ? t('admin:user_group_management.delete_modal.no_groups')
       : t('admin:user_group_management.delete_modal.select_group');
       : t('admin:user_group_management.delete_modal.select_group');
 
 
     return (
     return (
       <select
       <select
-        name="transferToUserGroupId"
+        name="transferToUserGroup"
         className={`form-control ${actionName === actionForPages.transfer ? '' : 'd-none'}`}
         className={`form-control ${actionName === actionForPages.transfer ? '' : 'd-none'}`}
-        value={transferToUserGroupId}
+        value={transferToUserGroup != null ? getIdForRef(transferToUserGroup.item) : ''}
         onChange={handleGroupChange}
         onChange={handleGroupChange}
       >
       >
         <option value="" disabled>{defaultOptionText}</option>
         <option value="" disabled>{defaultOptionText}</option>
         {options}
         {options}
       </select>
       </select>
     );
     );
-  }, [deleteUserGroup, userGroups, t, actionName, transferToUserGroupId, handleGroupChange]);
+  }, [deleteUserGroup, userGroups, t, actionName, transferToUserGroup, handleGroupChange]);
 
 
   const validateForm = useCallback(() => {
   const validateForm = useCallback(() => {
     let isValid = true;
     let isValid = true;
@@ -175,11 +180,11 @@ export const UserGroupDeleteModal: FC<Props> = (props: Props) => {
       isValid = false;
       isValid = false;
     }
     }
     else if (actionName === actionForPages.transfer) {
     else if (actionName === actionForPages.transfer) {
-      isValid = transferToUserGroupId !== '';
+      isValid = transferToUserGroup != null;
     }
     }
 
 
     return isValid;
     return isValid;
-  }, [actionName, transferToUserGroupId]);
+  }, [actionName, transferToUserGroup]);
 
 
   return (
   return (
     <Modal className="modal-md" isOpen={props.isShow} toggle={toggleHandler}>
     <Modal className="modal-md" isOpen={props.isShow} toggle={toggleHandler}>

+ 18 - 4
apps/app/src/components/Admin/UserGroup/UserGroupPage.tsx

@@ -1,12 +1,16 @@
-import React, { FC, useState, useCallback } from 'react';
+import type { FC } from 'react';
+import React, { useState, useCallback } from 'react';
 
 
-import type { IUserGroup, IUserGroupHasId } from '@growi/core';
+import {
+  GroupType, getIdForRef, type IGrantedGroup, type IUserGroup, type IUserGroupHasId,
+} from '@growi/core';
 import dynamic from 'next/dynamic';
 import dynamic from 'next/dynamic';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
 
 
 import { apiv3Delete, apiv3Post, apiv3Put } from '~/client/util/apiv3-client';
 import { apiv3Delete, apiv3Post, apiv3Put } from '~/client/util/apiv3-client';
 import { toastSuccess, toastError } from '~/client/util/toastr';
 import { toastSuccess, toastError } from '~/client/util/toastr';
 import { ExternalGroupManagement } from '~/features/external-user-group/client/components/ExternalUserGroup/ExternalUserGroupManagement';
 import { ExternalGroupManagement } from '~/features/external-user-group/client/components/ExternalUserGroup/ExternalUserGroupManagement';
+import { useSWRxExternalUserGroupList } from '~/features/external-user-group/client/stores/external-user-group';
 import { useIsAclEnabled } from '~/stores/context';
 import { useIsAclEnabled } from '~/stores/context';
 import { useSWRxUserGroupList, useSWRxChildUserGroupList, useSWRxUserGroupRelationList } from '~/stores/user-group';
 import { useSWRxUserGroupList, useSWRxChildUserGroupList, useSWRxUserGroupRelationList } from '~/stores/user-group';
 
 
@@ -24,7 +28,14 @@ export const UserGroupPage: FC = () => {
    * Fetch
    * Fetch
    */
    */
   const { data: userGroupList, mutate: mutateUserGroups } = useSWRxUserGroupList();
   const { data: userGroupList, mutate: mutateUserGroups } = useSWRxUserGroupList();
+  const { data: externalUserGroupList } = useSWRxExternalUserGroupList();
   const userGroups = userGroupList != null ? userGroupList : [];
   const userGroups = userGroupList != null ? userGroupList : [];
+  const userGroupsForDeleteModal: IGrantedGroup[] = userGroups.map((group) => {
+    return { item: group, type: GroupType.userGroup };
+  });
+  const externalUserGroupsForDeleteModal: IGrantedGroup[] = externalUserGroupList != null ? externalUserGroupList.map((group) => {
+    return { item: group, type: GroupType.externalUserGroup };
+  }) : [];
   const userGroupIds = userGroups.map(group => group._id);
   const userGroupIds = userGroups.map(group => group._id);
 
 
   const { data: userGroupRelationList } = useSWRxUserGroupRelationList(userGroupIds);
   const { data: userGroupRelationList } = useSWRxUserGroupRelationList(userGroupIds);
@@ -126,11 +137,14 @@ export const UserGroupPage: FC = () => {
     }
     }
   }, [t, mutateUserGroups, hideUpdateModal]);
   }, [t, mutateUserGroups, hideUpdateModal]);
 
 
-  const deleteUserGroupById = useCallback(async(deleteGroupId: string, actionName: string, transferToUserGroupId: string) => {
+  const deleteUserGroupById = useCallback(async(deleteGroupId: string, actionName: string, transferToUserGroup: IGrantedGroup | null) => {
+    const transferToUserGroupId = transferToUserGroup != null ? getIdForRef(transferToUserGroup.item) : null;
+    const transferToUserGroupType = transferToUserGroup != null ? transferToUserGroup.type : null;
     try {
     try {
       await apiv3Delete(`/user-groups/${deleteGroupId}`, {
       await apiv3Delete(`/user-groups/${deleteGroupId}`, {
         actionName,
         actionName,
         transferToUserGroupId,
         transferToUserGroupId,
+        transferToUserGroupType,
       });
       });
 
 
       // sync
       // sync
@@ -187,7 +201,7 @@ export const UserGroupPage: FC = () => {
       />
       />
 
 
       <UserGroupDeleteModal
       <UserGroupDeleteModal
-        userGroups={userGroups}
+        userGroups={userGroupsForDeleteModal.concat(externalUserGroupsForDeleteModal)}
         deleteUserGroup={selectedUserGroup}
         deleteUserGroup={selectedUserGroup}
         onDelete={deleteUserGroupById}
         onDelete={deleteUserGroupById}
         isShow={isDeleteModalShown}
         isShow={isDeleteModalShown}

+ 15 - 5
apps/app/src/components/Admin/UserGroupDetail/UserGroupDetailPage.tsx

@@ -2,7 +2,9 @@ import React, {
   useState, useCallback, useEffect, useMemo,
   useState, useCallback, useEffect, useMemo,
 } from 'react';
 } from 'react';
 
 
-import type { IUserGroup, IUserGroupHasId } from '@growi/core';
+import {
+  GroupType, getIdForRef, type IGrantedGroup, type IUserGroup, type IUserGroupHasId,
+} from '@growi/core';
 import { objectIdUtils } from '@growi/core/dist/utils';
 import { objectIdUtils } from '@growi/core/dist/utils';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import dynamic from 'next/dynamic';
 import dynamic from 'next/dynamic';
@@ -13,8 +15,9 @@ import {
   apiv3Get, apiv3Put, apiv3Delete, apiv3Post,
   apiv3Get, apiv3Put, apiv3Delete, apiv3Post,
 } from '~/client/util/apiv3-client';
 } from '~/client/util/apiv3-client';
 import { toastSuccess, toastError } from '~/client/util/toastr';
 import { toastSuccess, toastError } from '~/client/util/toastr';
-import { IExternalUserGroupHasId } from '~/features/external-user-group/interfaces/external-user-group';
-import { SearchTypes, SearchType } from '~/interfaces/user-group';
+import type { IExternalUserGroupHasId } from '~/features/external-user-group/interfaces/external-user-group';
+import type { SearchType } from '~/interfaces/user-group';
+import { SearchTypes } from '~/interfaces/user-group';
 import Xss from '~/services/xss';
 import Xss from '~/services/xss';
 import { useIsAclEnabled } from '~/stores/context';
 import { useIsAclEnabled } from '~/stores/context';
 import { useUpdateUserGroupConfirmModal } from '~/stores/modal';
 import { useUpdateUserGroupConfirmModal } from '~/stores/modal';
@@ -84,6 +87,10 @@ const UserGroupDetailPage = (props: Props): JSX.Element => {
 
 
   const { data: childUserGroupsList, mutate: mutateChildUserGroups, updateChild } = useChildUserGroupList(currentUserGroupId, isExternalGroup);
   const { data: childUserGroupsList, mutate: mutateChildUserGroups, updateChild } = useChildUserGroupList(currentUserGroupId, isExternalGroup);
   const childUserGroups = childUserGroupsList != null ? childUserGroupsList.childUserGroups : [];
   const childUserGroups = childUserGroupsList != null ? childUserGroupsList.childUserGroups : [];
+  const childUserGroupsForDeleteModal: IGrantedGroup[] = childUserGroups.map((group) => {
+    const groupType = isExternalGroup ? GroupType.externalUserGroup : GroupType.userGroup;
+    return { item: group, type: groupType };
+  });
   const grandChildUserGroups = childUserGroupsList != null ? childUserGroupsList.grandChildUserGroups : [];
   const grandChildUserGroups = childUserGroupsList != null ? childUserGroupsList.grandChildUserGroups : [];
   const childUserGroupIds = childUserGroups.map(group => group._id);
   const childUserGroupIds = childUserGroups.map(group => group._id);
 
 
@@ -296,12 +303,15 @@ const UserGroupDetailPage = (props: Props): JSX.Element => {
     setDeleteModalShown(false);
     setDeleteModalShown(false);
   }, [setSelectedUserGroup, setDeleteModalShown]);
   }, [setSelectedUserGroup, setDeleteModalShown]);
 
 
-  const deleteChildUserGroupById = useCallback(async(deleteGroupId: string, actionName: string, transferToUserGroupId: string) => {
+  const deleteChildUserGroupById = useCallback(async(deleteGroupId: string, actionName: string, transferToUserGroup: IGrantedGroup | null) => {
     const url = isExternalGroup ? `/external-user-groups/${deleteGroupId}` : `/user-groups/${deleteGroupId}`;
     const url = isExternalGroup ? `/external-user-groups/${deleteGroupId}` : `/user-groups/${deleteGroupId}`;
+    const transferToUserGroupId = transferToUserGroup != null ? getIdForRef(transferToUserGroup.item) : null;
+    const transferToUserGroupType = transferToUserGroup != null ? transferToUserGroup.type : null;
     try {
     try {
       const res = await apiv3Delete(url, {
       const res = await apiv3Delete(url, {
         actionName,
         actionName,
         transferToUserGroupId,
         transferToUserGroupId,
+        transferToUserGroupType,
       });
       });
 
 
       // sync
       // sync
@@ -448,7 +458,7 @@ const UserGroupDetailPage = (props: Props): JSX.Element => {
       />
       />
 
 
       <UserGroupDeleteModal
       <UserGroupDeleteModal
-        userGroups={childUserGroups}
+        userGroups={childUserGroupsForDeleteModal}
         deleteUserGroup={selectedUserGroup}
         deleteUserGroup={selectedUserGroup}
         onDelete={deleteChildUserGroupById}
         onDelete={deleteChildUserGroupById}
         isShow={isDeleteModalShown}
         isShow={isDeleteModalShown}

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

@@ -1,7 +1,8 @@
-import {
-  FC, useCallback, useMemo, useState,
-} from 'react';
+import type { FC } from 'react';
+import { useCallback, useMemo, useState } from 'react';
 
 
+import type { IGrantedGroup } from '@growi/core';
+import { GroupType, getIdForRef } from '@growi/core';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
 import { TabContent, TabPane } from 'reactstrap';
 import { TabContent, TabPane } from 'reactstrap';
 
 
@@ -11,8 +12,9 @@ import { UserGroupDeleteModal } from '~/components/Admin/UserGroup/UserGroupDele
 import { UserGroupModal } from '~/components/Admin/UserGroup/UserGroupModal';
 import { UserGroupModal } from '~/components/Admin/UserGroup/UserGroupModal';
 import { UserGroupTable } from '~/components/Admin/UserGroup/UserGroupTable';
 import { UserGroupTable } from '~/components/Admin/UserGroup/UserGroupTable';
 import CustomNav from '~/components/CustomNavigation/CustomNav';
 import CustomNav from '~/components/CustomNavigation/CustomNav';
-import { IExternalUserGroupHasId } from '~/features/external-user-group/interfaces/external-user-group';
+import type { IExternalUserGroupHasId } from '~/features/external-user-group/interfaces/external-user-group';
 import { useIsAclEnabled } from '~/stores/context';
 import { useIsAclEnabled } from '~/stores/context';
+import { useSWRxUserGroupList } from '~/stores/user-group';
 
 
 import { useSWRxChildExternalUserGroupList, useSWRxExternalUserGroupList, useSWRxExternalUserGroupRelationList } from '../../stores/external-user-group';
 import { useSWRxChildExternalUserGroupList, useSWRxExternalUserGroupList, useSWRxExternalUserGroupRelationList } from '../../stores/external-user-group';
 
 
@@ -21,7 +23,14 @@ import { LdapGroupManagement } from './LdapGroupManagement';
 
 
 export const ExternalGroupManagement: FC = () => {
 export const ExternalGroupManagement: FC = () => {
   const { data: externalUserGroupList, mutate: mutateExternalUserGroups } = useSWRxExternalUserGroupList();
   const { data: externalUserGroupList, mutate: mutateExternalUserGroups } = useSWRxExternalUserGroupList();
+  const { data: userGroupList } = useSWRxUserGroupList();
   const externalUserGroups = externalUserGroupList != null ? externalUserGroupList : [];
   const externalUserGroups = externalUserGroupList != null ? externalUserGroupList : [];
+  const externalUserGroupsForDeleteModal: IGrantedGroup[] = externalUserGroups.map((group) => {
+    return { item: group, type: GroupType.externalUserGroup };
+  });
+  const userGroupsForDeleteModal: IGrantedGroup[] = userGroupList != null ? userGroupList.map((group) => {
+    return { item: group, type: GroupType.userGroup };
+  }) : [];
   const externalUserGroupIds = externalUserGroups.map(group => group._id);
   const externalUserGroupIds = externalUserGroups.map(group => group._id);
 
 
   const { data: externalUserGroupRelationList } = useSWRxExternalUserGroupRelationList(externalUserGroupIds);
   const { data: externalUserGroupRelationList } = useSWRxExternalUserGroupRelationList(externalUserGroupIds);
@@ -93,11 +102,14 @@ export const ExternalGroupManagement: FC = () => {
     }
     }
   }, [t, mutateExternalUserGroups, hideUpdateModal]);
   }, [t, mutateExternalUserGroups, hideUpdateModal]);
 
 
-  const deleteExternalUserGroupById = useCallback(async(deleteGroupId: string, actionName: string, transferToUserGroupId: string) => {
+  const deleteExternalUserGroupById = useCallback(async(deleteGroupId: string, actionName: string, transferToUserGroup: IGrantedGroup | null) => {
+    const transferToUserGroupId = transferToUserGroup != null ? getIdForRef(transferToUserGroup.item) : null;
+    const transferToUserGroupType = transferToUserGroup != null ? transferToUserGroup.type : null;
     try {
     try {
       await apiv3Delete(`/external-user-groups/${deleteGroupId}`, {
       await apiv3Delete(`/external-user-groups/${deleteGroupId}`, {
         actionName,
         actionName,
         transferToUserGroupId,
         transferToUserGroupId,
+        transferToUserGroupType,
       });
       });
 
 
       // sync
       // sync
@@ -154,7 +166,7 @@ export const ExternalGroupManagement: FC = () => {
       />
       />
 
 
       <UserGroupDeleteModal
       <UserGroupDeleteModal
-        userGroups={externalUserGroups}
+        userGroups={userGroupsForDeleteModal.concat(externalUserGroupsForDeleteModal)}
         deleteUserGroup={selectedExternalUserGroup}
         deleteUserGroup={selectedExternalUserGroup}
         onDelete={deleteExternalUserGroupById}
         onDelete={deleteExternalUserGroupById}
         isShow={isDeleteModalShown}
         isShow={isDeleteModalShown}

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

@@ -1,6 +1,7 @@
 import { GroupType } from '@growi/core';
 import { GroupType } from '@growi/core';
 import { ErrorV3 } from '@growi/core/dist/models';
 import { ErrorV3 } from '@growi/core/dist/models';
-import { Router, Request } from 'express';
+import type { Request } from 'express';
+import { Router } from 'express';
 import {
 import {
   body, param, query, validationResult,
   body, param, query, validationResult,
 } from 'express-validator';
 } from 'express-validator';
@@ -8,13 +9,13 @@ import {
 import ExternalUserGroup from '~/features/external-user-group/server/models/external-user-group';
 import ExternalUserGroup from '~/features/external-user-group/server/models/external-user-group';
 import ExternalUserGroupRelation from '~/features/external-user-group/server/models/external-user-group-relation';
 import ExternalUserGroupRelation from '~/features/external-user-group/server/models/external-user-group-relation';
 import { SupportedAction } from '~/interfaces/activity';
 import { SupportedAction } from '~/interfaces/activity';
-import Crowi from '~/server/crowi';
+import type Crowi from '~/server/crowi';
 import { generateAddActivityMiddleware } from '~/server/middlewares/add-activity';
 import { generateAddActivityMiddleware } from '~/server/middlewares/add-activity';
 import { apiV3FormValidator } from '~/server/middlewares/apiv3-form-validator';
 import { apiV3FormValidator } from '~/server/middlewares/apiv3-form-validator';
 import { serializeUserGroupRelationSecurely } from '~/server/models/serializers/user-group-relation-serializer';
 import { serializeUserGroupRelationSecurely } from '~/server/models/serializers/user-group-relation-serializer';
-import { ApiV3Response } from '~/server/routes/apiv3/interfaces/apiv3-response';
+import type { ApiV3Response } from '~/server/routes/apiv3/interfaces/apiv3-response';
 import { configManager } from '~/server/service/config-manager';
 import { configManager } from '~/server/service/config-manager';
-import UserGroupService from '~/server/service/user-group';
+import type UserGroupService from '~/server/service/user-group';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 
 const logger = loggerFactory('growi:routes:apiv3:external-user-group');
 const logger = loggerFactory('growi:routes:apiv3:external-user-group');
@@ -148,16 +149,18 @@ module.exports = (crowi: Crowi): Router => {
   router.delete('/:id', loginRequiredStrictly, adminRequired, validators.delete, apiV3FormValidator, addActivity,
   router.delete('/:id', loginRequiredStrictly, adminRequired, validators.delete, apiV3FormValidator, addActivity,
     async(req: AuthorizedRequest, res: ApiV3Response) => {
     async(req: AuthorizedRequest, res: ApiV3Response) => {
       const { id: deleteGroupId } = req.params;
       const { id: deleteGroupId } = req.params;
-      const { actionName, transferToUserGroupId } = req.query;
+      const { actionName, transferToUserGroupId, transferToUserGroupType } = req.query;
 
 
-      const transferGroupInfo = transferToUserGroupId != null ? {
-        item: transferToUserGroupId as string,
-        type: GroupType.externalUserGroup,
-      } : undefined;
+      const transferToUserGroup = typeof transferToUserGroupId === 'string'
+        && (transferToUserGroupType === GroupType.userGroup || transferToUserGroupType === GroupType.externalUserGroup)
+        ? {
+          item: transferToUserGroupId,
+          type: transferToUserGroupType,
+        } : undefined;
 
 
       try {
       try {
         const userGroups = await (crowi.userGroupService as UserGroupService)
         const userGroups = await (crowi.userGroupService as UserGroupService)
-          .removeCompletelyByRootGroupId(deleteGroupId, actionName, req.user, transferGroupInfo, ExternalUserGroup, ExternalUserGroupRelation);
+          .removeCompletelyByRootGroupId(deleteGroupId, actionName, req.user, transferToUserGroup, ExternalUserGroup, ExternalUserGroupRelation);
 
 
         const parameters = { action: SupportedAction.ACTION_ADMIN_USER_GROUP_DELETE };
         const parameters = { action: SupportedAction.ACTION_ADMIN_USER_GROUP_DELETE };
         activityEvent.emit('update', res.locals.activity._id, parameters);
         activityEvent.emit('update', res.locals.activity._id, parameters);

+ 8 - 6
apps/app/src/server/routes/apiv3/user-group.js

@@ -429,15 +429,17 @@ module.exports = (crowi) => {
    */
    */
   router.delete('/:id', loginRequiredStrictly, adminRequired, validator.delete, apiV3FormValidator, addActivity, async(req, res) => {
   router.delete('/:id', loginRequiredStrictly, adminRequired, validator.delete, apiV3FormValidator, addActivity, async(req, res) => {
     const { id: deleteGroupId } = req.params;
     const { id: deleteGroupId } = req.params;
-    const { actionName, transferToUserGroupId } = req.query;
+    const { actionName, transferToUserGroupId, transferToUserGroupType } = req.query;
 
 
-    const transferGroupInfo = transferToUserGroupId != null ? {
-      item: transferToUserGroupId,
-      type: GroupType.userGroup,
-    } : undefined;
+    const transferToUserGroup = typeof transferToUserGroupId === 'string'
+        && (transferToUserGroupType === GroupType.userGroup || transferToUserGroupType === GroupType.externalUserGroup)
+      ? {
+        item: transferToUserGroupId,
+        type: transferToUserGroupType,
+      } : undefined;
 
 
     try {
     try {
-      const userGroups = await crowi.userGroupService.removeCompletelyByRootGroupId(deleteGroupId, actionName, req.user, transferGroupInfo);
+      const userGroups = await crowi.userGroupService.removeCompletelyByRootGroupId(deleteGroupId, actionName, req.user, transferToUserGroup);
 
 
       const parameters = { action: SupportedAction.ACTION_ADMIN_USER_GROUP_DELETE };
       const parameters = { action: SupportedAction.ACTION_ADMIN_USER_GROUP_DELETE };
       activityEvent.emit('update', res.locals.activity._id, parameters);
       activityEvent.emit('update', res.locals.activity._id, parameters);