UserGroupDeleteModal.tsx 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. import type { FC } from 'react';
  2. import React, { useCallback, useState, useMemo } from 'react';
  3. import {
  4. getIdForRef, isPopulated, type IGrantedGroup, type IUserGroupHasId,
  5. } from '@growi/core';
  6. import { useTranslation } from 'next-i18next';
  7. import {
  8. Modal, ModalHeader, ModalBody, ModalFooter,
  9. } from 'reactstrap';
  10. import { PageActionOnGroupDelete } from '~/interfaces/user-group';
  11. /**
  12. * Delete User Group Select component
  13. *
  14. * @export
  15. * @class GrantSelector
  16. * @extends {React.Component}
  17. */
  18. type Props = {
  19. userGroups: IGrantedGroup[],
  20. deleteUserGroup?: IUserGroupHasId,
  21. onDelete?: (deleteGroupId: string, actionName: PageActionOnGroupDelete, transferToUserGroup: IGrantedGroup | null) => Promise<void> | void,
  22. isShow: boolean,
  23. onHide?: () => Promise<void> | void,
  24. };
  25. type AvailableOption = {
  26. id: number,
  27. actionForPages: PageActionOnGroupDelete,
  28. iconClass: string,
  29. styleClass: string,
  30. label: string,
  31. };
  32. export const UserGroupDeleteModal: FC<Props> = (props: Props) => {
  33. const { t } = useTranslation();
  34. const {
  35. onHide, onDelete, userGroups, deleteUserGroup,
  36. } = props;
  37. const availableOptions = useMemo<AvailableOption[]>(() => {
  38. return [
  39. {
  40. id: 1,
  41. actionForPages: PageActionOnGroupDelete.publicize,
  42. iconClass: 'icon-people',
  43. styleClass: '',
  44. label: t('admin:user_group_management.delete_modal.publish_pages'),
  45. },
  46. {
  47. id: 2,
  48. actionForPages: PageActionOnGroupDelete.delete,
  49. iconClass: 'icon-trash',
  50. styleClass: 'text-danger',
  51. label: t('admin:user_group_management.delete_modal.delete_pages'),
  52. },
  53. {
  54. id: 3,
  55. actionForPages: PageActionOnGroupDelete.transfer,
  56. iconClass: 'icon-options',
  57. styleClass: '',
  58. label: t('admin:user_group_management.delete_modal.transfer_pages'),
  59. },
  60. ];
  61. }, [t]);
  62. /*
  63. * State
  64. */
  65. const [actionName, setActionName] = useState<PageActionOnGroupDelete | null>(null);
  66. const [transferToUserGroup, setTransferToUserGroup] = useState<IGrantedGroup | null>(null);
  67. /*
  68. * Function
  69. */
  70. const resetStates = useCallback(() => {
  71. setActionName(null);
  72. setTransferToUserGroup(null);
  73. }, []);
  74. const toggleHandler = useCallback(() => {
  75. if (onHide == null) {
  76. return;
  77. }
  78. resetStates();
  79. onHide();
  80. }, [onHide, resetStates]);
  81. const handleActionChange = useCallback((e) => {
  82. const actionName = e.target.value;
  83. setActionName(actionName);
  84. }, [setActionName]);
  85. const handleGroupChange = useCallback((e) => {
  86. const transferToUserGroupId = e.target.value;
  87. const selectedGroup = userGroups.find(group => getIdForRef(group.item) === transferToUserGroupId) ?? null;
  88. setTransferToUserGroup(selectedGroup);
  89. }, [userGroups]);
  90. const handleSubmit = useCallback((e) => {
  91. if (onDelete == null || deleteUserGroup == null || actionName == null) {
  92. return;
  93. }
  94. e.preventDefault();
  95. onDelete(
  96. deleteUserGroup._id,
  97. actionName,
  98. transferToUserGroup,
  99. );
  100. }, [onDelete, deleteUserGroup, actionName, transferToUserGroup]);
  101. const renderPageActionSelector = useCallback(() => {
  102. const options = availableOptions.map((opt) => {
  103. return <option key={opt.id} value={opt.actionForPages}>{opt.label}</option>;
  104. });
  105. return (
  106. <select
  107. name="actionName"
  108. className="form-control"
  109. placeholder="select"
  110. value={actionName ?? ''}
  111. onChange={handleActionChange}
  112. >
  113. <option value="" disabled>{t('admin:user_group_management.delete_modal.dropdown_desc')}</option>
  114. {options}
  115. </select>
  116. );
  117. }, [availableOptions, actionName, handleActionChange, t]);
  118. const renderGroupSelector = useCallback(() => {
  119. if (deleteUserGroup == null) {
  120. return;
  121. }
  122. const groups = userGroups.filter((group) => {
  123. return getIdForRef(group.item) !== deleteUserGroup._id;
  124. });
  125. const options = groups.map((group) => {
  126. const groupId = getIdForRef(group.item);
  127. const groupName = isPopulated(group.item) ? group.item.name : null;
  128. return { id: groupId, name: groupName };
  129. }).filter(obj => obj.name != null)
  130. .map(obj => <option key={obj.id} value={obj.id}>{obj.name}</option>);
  131. const defaultOptionText = groups.length === 0 ? t('admin:user_group_management.delete_modal.no_groups')
  132. : t('admin:user_group_management.delete_modal.select_group');
  133. return (
  134. <select
  135. name="transferToUserGroup"
  136. className={`form-control ${actionName === PageActionOnGroupDelete.transfer ? '' : 'd-none'}`}
  137. value={transferToUserGroup != null ? getIdForRef(transferToUserGroup.item) : ''}
  138. onChange={handleGroupChange}
  139. >
  140. <option value="" disabled>{defaultOptionText}</option>
  141. {options}
  142. </select>
  143. );
  144. }, [deleteUserGroup, userGroups, t, actionName, transferToUserGroup, handleGroupChange]);
  145. const validateForm = useCallback(() => {
  146. let isValid = true;
  147. if (actionName === null) {
  148. isValid = false;
  149. }
  150. else if (actionName === PageActionOnGroupDelete.transfer) {
  151. isValid = transferToUserGroup != null;
  152. }
  153. return isValid;
  154. }, [actionName, transferToUserGroup]);
  155. return (
  156. <Modal className="modal-md" isOpen={props.isShow} toggle={toggleHandler}>
  157. <ModalHeader tag="h4" toggle={toggleHandler} className="bg-danger text-light">
  158. <span className="material-symbols-outlined">delete_forever</span> {t('admin:user_group_management.delete_modal.header')}
  159. </ModalHeader>
  160. <ModalBody>
  161. <div>
  162. <span className="fw-bold">{t('admin:user_group_management.group_name')}</span> : &quot;{props?.deleteUserGroup?.name || ''}&quot;
  163. </div>
  164. <div className="text-danger mt-3">
  165. {t('admin:user_group_management.delete_modal.desc')}
  166. </div>
  167. </ModalBody>
  168. <ModalFooter>
  169. <form className="d-flex justify-content-between w-100" onSubmit={handleSubmit}>
  170. <div className="d-flex mb-0 me-3">
  171. {renderPageActionSelector()}
  172. {renderGroupSelector()}
  173. </div>
  174. <button type="submit" value="" className="btn btn-sm btn-danger text-nowrap" disabled={!validateForm()}>
  175. <span className="material-symbols-outlined">delete_forever</span> {t('Delete')}
  176. </button>
  177. </form>
  178. {actionName === PageActionOnGroupDelete.publicize && (
  179. <div className="form-text text-muted">
  180. <small>{t('admin:user_group_management.delete_modal.option_explanation')}</small>
  181. </div>
  182. )}
  183. </ModalFooter>
  184. </Modal>
  185. );
  186. };