ryoji-s 2 лет назад
Родитель
Сommit
cc08ae9704

+ 54 - 0
apps/app/src/components/Admin/Users/GiveReadOnlyButton.tsx

@@ -0,0 +1,54 @@
+import React, { useCallback } from 'react';
+
+import type { IUserHasId } from '@growi/core';
+import { useTranslation } from 'next-i18next';
+
+import AdminUsersContainer from '~/client/services/AdminUsersContainer';
+import { toastSuccess, toastError } from '~/client/util/toastr';
+
+import { withUnstatedContainers } from '../../UnstatedUtils';
+
+const GiveReadOnlyAlert = React.memo((): JSX.Element => {
+  const { t } = useTranslation();
+
+  return (
+    <div className="px-4">
+      <i className="icon-fw icon-user-unfollow mb-2"></i>{t('admin:user_management.user_table.remove_read_only_access')}
+      <p className="alert alert-danger">{t('admin:user_management.user_table.cannot_give_read_only')}</p>
+    </div>
+  );
+});
+GiveReadOnlyAlert.displayName = 'GiveReadOnlyAlert';
+
+const GiveReadOnlyButton: React.FC<{
+  adminUsersContainer: AdminUsersContainer,
+  user: IUserHasId,
+}> = ({ adminUsersContainer, user }): JSX.Element => {
+  const { t } = useTranslation('admin');
+
+  const onClickGiveReadOnlyBtnHandler = useCallback(async() => {
+    try {
+      const username = await adminUsersContainer.giveUserReadOnly(user._id);
+      toastSuccess(t('toaster.give_user_read_only', { username }));
+    }
+    catch (err) {
+      toastError(err);
+    }
+  }, [adminUsersContainer, t, user._id]);
+
+  return user.admin
+    ? <GiveReadOnlyAlert />
+    : (
+      <button className="dropdown-item" type="button" onClick={onClickGiveReadOnlyBtnHandler}>
+        <i className="icon-fw icon-user-following"></i> {t('user_management.user_table.give_read_only_access')}
+      </button>
+    );
+};
+
+/**
+ * Wrapper component for using unstated
+ */
+// eslint-disable-next-line max-len
+const GiveReadOnlyButtonWrapper: React.ForwardRefExoticComponent<Pick<any, string | number | symbol> & React.RefAttributes<any>> = withUnstatedContainers(GiveReadOnlyButton, [AdminUsersContainer]);
+
+export default GiveReadOnlyButtonWrapper;

+ 40 - 0
apps/app/src/components/Admin/Users/RemoveReadOnlyMenuItem.tsx

@@ -0,0 +1,40 @@
+import React, { useCallback } from 'react';
+
+import type { IUserHasId } from '@growi/core';
+import { useTranslation } from 'next-i18next';
+
+import AdminUsersContainer from '~/client/services/AdminUsersContainer';
+import { toastSuccess, toastError } from '~/client/util/toastr';
+
+import { withUnstatedContainers } from '../../UnstatedUtils';
+
+const RemoveReadOnlyMenuItem: React.FC<{
+  adminUsersContainer: AdminUsersContainer,
+  user: IUserHasId,
+}> = ({ adminUsersContainer, user }): JSX.Element => {
+  const { t } = useTranslation('admin');
+
+  const clickRemoveReadOnlyBtnHandler = useCallback(async() => {
+    try {
+      const username = await adminUsersContainer.removeUserReadOnly(user._id);
+      toastSuccess(t('toaster.remove_user_read_only', { username }));
+    }
+    catch (err) {
+      toastError(err);
+    }
+  }, [adminUsersContainer, t, user._id]);
+
+  return (
+    <button className="dropdown-item" type="button" onClick={clickRemoveReadOnlyBtnHandler}>
+      <i className="icon-fw icon-user-unfollow"></i> {t('user_management.user_table.remove_read_only_access')}
+    </button>
+  );
+};
+
+/**
+* Wrapper component for using unstated
+*/
+// eslint-disable-next-line max-len
+const RemoveReadOnlyMenuItemWrapper: React.ForwardRefExoticComponent<Pick<any, string | number | symbol> & React.RefAttributes<any>> = withUnstatedContainers(RemoveReadOnlyMenuItem, [AdminUsersContainer]);
+
+export default RemoveReadOnlyMenuItemWrapper;

+ 6 - 0
apps/app/src/components/Admin/Users/UserMenu.tsx

@@ -11,7 +11,9 @@ import AdminUsersContainer from '~/client/services/AdminUsersContainer';
 import { withUnstatedContainers } from '../../UnstatedUtils';
 
 import GiveAdminButton from './GiveAdminButton';
+import GiveReadOnlyButton from './GiveReadOnlyButton';
 import RemoveAdminMenuItem from './RemoveAdminMenuItem';
+import RemoveReadOnlyMenuItem from './RemoveReadOnlyMenuItem';
 import SendInvitationEmailButton from './SendInvitationEmailButton';
 import StatusActivateButton from './StatusActivateButton';
 import StatusSuspendedMenuItem from './StatusSuspendMenuItem';
@@ -84,6 +86,10 @@ const UserMenu = (props: UserMenuProps) => {
           {user.admin === true && <RemoveAdminMenuItem user={user} />}
           {user.admin === false && <GiveAdminButton user={user} />}
         </li>
+        <li>
+          {user.readOnly === true && <RemoveReadOnlyMenuItem user={user} />}
+          {user.readOnly === false && <GiveReadOnlyButton user={user} />}
+        </li>
       </>
     );
   }, [t, user]);

+ 1 - 0
packages/core/src/interfaces/user.ts

@@ -13,6 +13,7 @@ export type IUser = {
   imageUrlCached: string,
   isGravatarEnabled: boolean,
   admin: boolean,
+  readOnly: boolean,
   apiToken?: string,
   isEmailPublished: boolean,
   isInvitationEmailSended: boolean,