UserGroupForm.tsx 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. import type { FC } from 'react';
  2. import React, { useCallback, useEffect, useState } from 'react';
  3. import type { IUserGroupHasId } from '@growi/core';
  4. import { format as dateFnsFormat } from 'date-fns/format';
  5. import { useTranslation } from 'next-i18next';
  6. type Props = {
  7. userGroup: IUserGroupHasId;
  8. parentUserGroup?: IUserGroupHasId;
  9. selectableParentUserGroups?: IUserGroupHasId[];
  10. submitButtonLabel: string;
  11. onSubmit: (
  12. targetGroup: IUserGroupHasId,
  13. userGroupData: Partial<IUserGroupHasId>,
  14. ) => Promise<void>;
  15. isExternalGroup?: boolean;
  16. };
  17. export const UserGroupForm: FC<Props> = (props: Props) => {
  18. const { t } = useTranslation('admin');
  19. const {
  20. userGroup,
  21. parentUserGroup,
  22. selectableParentUserGroups,
  23. submitButtonLabel,
  24. onSubmit,
  25. isExternalGroup = false,
  26. } = props;
  27. /*
  28. * State
  29. */
  30. const [currentName, setName] = useState<string>(userGroup.name);
  31. const [currentDescription, setDescription] = useState<string>(
  32. userGroup.description,
  33. );
  34. const [selectedParent, setSelectedParent] = useState<
  35. IUserGroupHasId | undefined
  36. >();
  37. /*
  38. * Function
  39. */
  40. const onChangeNameHandler = useCallback((e) => {
  41. setName(e.target.value);
  42. }, []);
  43. const onChangeDescriptionHandler = useCallback((e) => {
  44. setDescription(e.target.value);
  45. }, []);
  46. const onChangeParentButtonHandler = useCallback(
  47. (userGroup: IUserGroupHasId) => {
  48. if (userGroup._id !== selectedParent?._id) {
  49. setSelectedParent(userGroup);
  50. }
  51. },
  52. [selectedParent],
  53. );
  54. useEffect(() => {
  55. setSelectedParent(parentUserGroup);
  56. }, [parentUserGroup]);
  57. const isSelectableParentUserGroups =
  58. selectableParentUserGroups != null && selectableParentUserGroups.length > 0;
  59. const isChildUserGroup = parentUserGroup !== undefined;
  60. const messageAtReleaseParentGroup = isChildUserGroup
  61. ? t('user_group_management.release_parent_group')
  62. : t('user_group_management.select_parent_group');
  63. return (
  64. <form
  65. onSubmit={(e) => {
  66. e.preventDefault();
  67. onSubmit(props.userGroup, {
  68. name: currentName,
  69. description: currentDescription,
  70. parent: selectedParent,
  71. });
  72. }}
  73. >
  74. <fieldset>
  75. <h2 className="admin-setting-header">
  76. {t('user_group_management.basic_info')}
  77. </h2>
  78. {isExternalGroup && (
  79. <div className="mb-3">
  80. <small className="text-muted">
  81. {t('external_user_group.only_description_edit_allowed')}
  82. </small>
  83. </div>
  84. )}
  85. {userGroup?.createdAt != null && (
  86. <div className="row mb-3">
  87. <p className="col-md-2 col-form-label">{t('Created')}</p>
  88. <p className="col-md-6 my-auto">
  89. {dateFnsFormat(userGroup.createdAt, 'yyyy-MM-dd')}
  90. </p>
  91. </div>
  92. )}
  93. <div className="row mb-3">
  94. <label htmlFor="name" className="col-md-2 col-form-label">
  95. {t('user_group_management.group_name')}
  96. </label>
  97. <div className="col-md-6 my-auto">
  98. <input
  99. className="form-control"
  100. type="text"
  101. name="name"
  102. placeholder={t('user_group_management.group_example')}
  103. value={currentName}
  104. onChange={onChangeNameHandler}
  105. required
  106. disabled={isExternalGroup}
  107. />
  108. </div>
  109. </div>
  110. <div className="row mb-3">
  111. <label htmlFor="description" className="col-md-2 col-form-label">
  112. {t('Description')}
  113. </label>
  114. <div className="col-md-6">
  115. <textarea
  116. className="form-control"
  117. name="description"
  118. value={currentDescription}
  119. onChange={onChangeDescriptionHandler}
  120. />
  121. </div>
  122. </div>
  123. <div className="row mb-3">
  124. <label htmlFor="parent" className="col-md-2 col-form-label">
  125. {t('user_group_management.parent_group')}
  126. </label>
  127. <div className="dropdown col-md-6">
  128. <button
  129. type="button"
  130. id="dropdownMenuButton"
  131. data-bs-toggle="dropdown"
  132. className="btn btn-outline-secondary dropdown-toggle mb-3"
  133. disabled={isExternalGroup || !isSelectableParentUserGroups}
  134. >
  135. {selectedParent?.name ?? messageAtReleaseParentGroup}
  136. </button>
  137. <div className="dropdown-menu">
  138. {isSelectableParentUserGroups && (
  139. <>
  140. {selectableParentUserGroups.map((userGroup) => (
  141. <button
  142. key={userGroup._id}
  143. type="button"
  144. className={`dropdown-item ${selectedParent?._id === userGroup._id ? 'active' : ''}`}
  145. onClick={() => onChangeParentButtonHandler(userGroup)}
  146. >
  147. {userGroup.name}
  148. </button>
  149. ))}
  150. </>
  151. )}
  152. <div className="dropdown-divider" />
  153. <button
  154. className="dropdown-item"
  155. type="button"
  156. onClick={() => {
  157. setSelectedParent(undefined);
  158. }}
  159. >
  160. {t('user_group_management.release_parent_group')}
  161. </button>
  162. </div>
  163. </div>
  164. </div>
  165. <div className="row mb-5">
  166. <div className="offset-md-2 col-md-10">
  167. <button type="submit" className="btn btn-primary">
  168. {submitButtonLabel}
  169. </button>
  170. </div>
  171. </div>
  172. </fieldset>
  173. </form>
  174. );
  175. };