Просмотр исходного кода

Merge pull request #6755 from weseek/feat/admin-tszation-Users-01

feat: Admin TSzation Users 01
ryoji-s 3 лет назад
Родитель
Сommit
7289d6da28

+ 2 - 0
packages/app/public/static/locales/en_US/admin.json

@@ -1030,6 +1030,8 @@
     "ADMIN_SEARCH_INDICES_REBUILD": "Rebuild Elasticsearch indexes"
     "ADMIN_SEARCH_INDICES_REBUILD": "Rebuild Elasticsearch indexes"
   },
   },
   "toaster": {
   "toaster": {
+    "give_user_admin": "Succeeded to give {{username}} admin",
+    "remove_user_admin": "Succeeded to remove {{username}} admin",
     "remove_external_user_success": "Succeeded to remove {{accountId}}"
     "remove_external_user_success": "Succeeded to remove {{accountId}}"
   }
   }
 }
 }

+ 0 - 2
packages/app/public/static/locales/en_US/translation.json

@@ -530,8 +530,6 @@
     "file_upload_succeeded": "File upload succeeded.",
     "file_upload_succeeded": "File upload succeeded.",
     "file_upload_failed": "File upload failed.",
     "file_upload_failed": "File upload failed.",
     "initialize_successed": "Succeeded to initialize {{target}}",
     "initialize_successed": "Succeeded to initialize {{target}}",
-    "give_user_admin": "Succeeded to give {{username}} admin",
-    "remove_user_admin": "Succeeded to remove {{username}} admin",
     "activate_user_success": "Succeeded to activating {{username}}",
     "activate_user_success": "Succeeded to activating {{username}}",
     "deactivate_user_success": "Succeeded to deactivate {{username}}",
     "deactivate_user_success": "Succeeded to deactivate {{username}}",
     "remove_user_success": "Succeeded to removing {{username}}",
     "remove_user_success": "Succeeded to removing {{username}}",

+ 2 - 0
packages/app/public/static/locales/ja_JP/admin.json

@@ -1042,6 +1042,8 @@
     "ADMIN_SEARCH_INDICES_REBUILD": "Elasticsearch のインデックスのリビルド"
     "ADMIN_SEARCH_INDICES_REBUILD": "Elasticsearch のインデックスのリビルド"
   },
   },
   "toaster": {
   "toaster": {
+    "give_user_admin": "{{username}}を管理者に設定しました",
+    "remove_user_admin": "{{username}}を管理者から外しました",
     "remove_external_user_success": "{{accountId}}を削除しました"
     "remove_external_user_success": "{{accountId}}を削除しました"
   }
   }
 }
 }

+ 0 - 2
packages/app/public/static/locales/ja_JP/translation.json

@@ -521,8 +521,6 @@
     "file_upload_succeeded": "ファイルをアップロードしました",
     "file_upload_succeeded": "ファイルをアップロードしました",
     "file_upload_failed": "ファイルのアップロードに失敗しました",
     "file_upload_failed": "ファイルのアップロードに失敗しました",
     "initialize_successed": "{{target}}を初期化しました",
     "initialize_successed": "{{target}}を初期化しました",
-    "give_user_admin": "{{username}}を管理者に設定しました",
-    "remove_user_admin": "{{username}}を管理者から外しました",
     "activate_user_success": "{{username}}を有効化しました",
     "activate_user_success": "{{username}}を有効化しました",
     "deactivate_user_success": "{{username}}を無効化しました",
     "deactivate_user_success": "{{username}}を無効化しました",
     "remove_user_success": "{{username}}を削除しました",
     "remove_user_success": "{{username}}を削除しました",

+ 2 - 0
packages/app/public/static/locales/zh_CN/admin.json

@@ -1008,6 +1008,8 @@
     "ADMIN_SEARCH_INDICES_REBUILD": "重建 Elasticsearch 索引"
     "ADMIN_SEARCH_INDICES_REBUILD": "重建 Elasticsearch 索引"
   },
   },
   "toaster": {
   "toaster": {
+		"give_user_admin": "Succeeded to give {{username}} admin",
+    "remove_user_admin": "Succeeded to remove {{username}} admin",
     "remove_external_user_success": "Succeeded to remove {{accountId}}"
     "remove_external_user_success": "Succeeded to remove {{accountId}}"
   }
   }
 }
 }

+ 0 - 2
packages/app/public/static/locales/zh_CN/translation.json

@@ -503,8 +503,6 @@
     "file_upload_succeeded": "文件上传成功",
     "file_upload_succeeded": "文件上传成功",
     "file_upload_failed": "文件上传失败",
     "file_upload_failed": "文件上传失败",
     "initialize_successed": "Succeeded to initialize {{target}}",
     "initialize_successed": "Succeeded to initialize {{target}}",
-		"give_user_admin": "Succeeded to give {{username}} admin",
-    "remove_user_admin": "Succeeded to remove {{username}} admin ",
 		"activate_user_success": "Succeeded to activating {{username}}",
 		"activate_user_success": "Succeeded to activating {{username}}",
 		"deactivate_user_success": "Succeeded to deactivate {{username}}",
 		"deactivate_user_success": "Succeeded to deactivate {{username}}",
 		"remove_user_success": "Succeeded to removing {{username}} ",
 		"remove_user_success": "Succeeded to removing {{username}} ",

+ 1 - 1
packages/app/src/components/Admin/UserGroup/UserGroupTable.tsx

@@ -2,11 +2,11 @@ import React, {
   FC, useState, useEffect,
   FC, useState, useEffect,
 } from 'react';
 } from 'react';
 
 
+import type { IUserGroupHasId, IUserGroupRelation, IUserHasId } from '@growi/core';
 import dateFnsFormat from 'date-fns/format';
 import dateFnsFormat from 'date-fns/format';
 import { TFunctionResult } from 'i18next';
 import { TFunctionResult } from 'i18next';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 
 
-import { IUserGroupHasId, IUserGroupRelation, IUserHasId } from '~/interfaces/user';
 
 
 type Props = {
 type Props = {
   headerLabel?: TFunctionResult,
   headerLabel?: TFunctionResult,

+ 0 - 60
packages/app/src/components/Admin/Users/GiveAdminButton.jsx

@@ -1,60 +0,0 @@
-import React from 'react';
-
-import { useTranslation } from 'next-i18next';
-import PropTypes from 'prop-types';
-
-import AdminUsersContainer from '~/client/services/AdminUsersContainer';
-import { toastSuccess, toastError } from '~/client/util/apiNotification';
-
-import { withUnstatedContainers } from '../../UnstatedUtils';
-
-class GiveAdminButton extends React.Component {
-
-  constructor(props) {
-    super(props);
-
-    this.onClickGiveAdminBtn = this.onClickGiveAdminBtn.bind(this);
-  }
-
-  async onClickGiveAdminBtn() {
-    const { t } = this.props;
-
-    try {
-      const username = await this.props.adminUsersContainer.giveUserAdmin(this.props.user._id);
-      toastSuccess(t('toaster.give_user_admin', { username }));
-    }
-    catch (err) {
-      toastError(err);
-    }
-  }
-
-  render() {
-    const { t } = this.props;
-
-    return (
-      <button className="dropdown-item" type="button" onClick={() => { this.onClickGiveAdminBtn() }}>
-        <i className="icon-fw icon-user-following"></i> {t('admin:user_management.user_table.give_admin_access')}
-      </button>
-    );
-  }
-
-}
-
-const GiveAdminButtonWrapperFC = (props) => {
-  const { t } = useTranslation();
-  return <GiveAdminButton t={t} {...props} />;
-};
-
-/**
- * Wrapper component for using unstated
- */
-const GiveAdminButtonWrapper = withUnstatedContainers(GiveAdminButtonWrapperFC, [AdminUsersContainer]);
-
-GiveAdminButton.propTypes = {
-  t: PropTypes.func.isRequired, // i18next
-  adminUsersContainer: PropTypes.instanceOf(AdminUsersContainer).isRequired,
-
-  user: PropTypes.object.isRequired,
-};
-
-export default GiveAdminButtonWrapper;

+ 44 - 0
packages/app/src/components/Admin/Users/GiveAdminButton.tsx

@@ -0,0 +1,44 @@
+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/apiNotification';
+
+import { withUnstatedContainers } from '../../UnstatedUtils';
+
+type GiveAdminButtonProps = {
+  adminUsersContainer: AdminUsersContainer,
+  user: IUserHasId,
+}
+
+const GiveAdminButton = (props: GiveAdminButtonProps): JSX.Element => {
+
+  const { t } = useTranslation();
+  const { adminUsersContainer, user } = props;
+
+  const onClickGiveAdminBtnHandler = useCallback(async() => {
+    try {
+      const username = await adminUsersContainer.giveUserAdmin(user._id);
+      toastSuccess(t('admin:toaster.give_user_admin', { username }));
+    }
+    catch (err) {
+      toastError(err);
+    }
+  }, [adminUsersContainer, t, user._id]);
+
+  return (
+    <button className="dropdown-item" type="button" onClick={() => onClickGiveAdminBtnHandler()}>
+      <i className="icon-fw icon-user-following"></i> {t('admin:user_management.user_table.give_admin_access')}
+    </button>
+  );
+
+};
+
+/**
+ * Wrapper component for using unstated
+ */
+const GiveAdminButtonWrapper = withUnstatedContainers(GiveAdminButton, [AdminUsersContainer]);
+
+export default GiveAdminButtonWrapper;

+ 0 - 86
packages/app/src/components/Admin/Users/RemoveAdminButton.jsx

@@ -1,86 +0,0 @@
-import React, { Fragment } from 'react';
-
-import PropTypes from 'prop-types';
-import { useTranslation } from 'next-i18next';
-
-import AdminUsersContainer from '~/client/services/AdminUsersContainer';
-import AppContainer from '~/client/services/AppContainer';
-import { toastSuccess, toastError } from '~/client/util/apiNotification';
-
-import { withUnstatedContainers } from '../../UnstatedUtils';
-
-class RemoveAdminButton extends React.Component {
-
-  constructor(props) {
-    super(props);
-
-    this.onClickRemoveAdminBtn = this.onClickRemoveAdminBtn.bind(this);
-  }
-
-  async onClickRemoveAdminBtn() {
-    const { t } = this.props;
-
-    try {
-      const username = await this.props.adminUsersContainer.removeUserAdmin(this.props.user._id);
-      toastSuccess(t('toaster.remove_user_admin', { username }));
-    }
-    catch (err) {
-      toastError(err);
-    }
-  }
-
-
-  renderRemoveAdminBtn() {
-    const { t } = this.props;
-
-    return (
-      <button className="dropdown-item" type="button" onClick={() => { this.onClickRemoveAdminBtn() }}>
-        <i className="icon-fw icon-user-unfollow"></i> {t('admin:user_management.user_table.remove_admin_access')}
-      </button>
-    );
-  }
-
-  renderRemoveAdminAlert() {
-    const { t } = this.props;
-
-    return (
-      <div className="px-4">
-        <i className="icon-fw icon-user-unfollow mb-2"></i>{t('admin:user_management.user_table.remove_admin_access')}
-        <p className="alert alert-danger">{t('admin:user_management.user_table.cannot_remove')}</p>
-      </div>
-    );
-  }
-
-  render() {
-    const { user } = this.props;
-    const { currentUsername } = this.props.appContainer;
-
-    return (
-      <Fragment>
-        {user.username !== currentUsername ? this.renderRemoveAdminBtn()
-          : this.renderRemoveAdminAlert()}
-      </Fragment>
-    );
-  }
-
-}
-
-const RemoveAdminButtonWrapperFC = (props) => {
-  const { t } = useTranslation();
-  return <RemoveAdminButton t={t} {...props} />;
-};
-
-/**
-* Wrapper component for using unstated
-*/
-const RemoveAdminButtonWrapper = withUnstatedContainers(RemoveAdminButtonWrapperFC, [AppContainer, AdminUsersContainer]);
-
-RemoveAdminButton.propTypes = {
-  t: PropTypes.func.isRequired, // i18next
-  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
-  adminUsersContainer: PropTypes.instanceOf(AdminUsersContainer).isRequired,
-
-  user: PropTypes.object.isRequired,
-};
-
-export default RemoveAdminButtonWrapper;

+ 67 - 0
packages/app/src/components/Admin/Users/RemoveAdminButton.tsx

@@ -0,0 +1,67 @@
+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/apiNotification';
+import { useCurrentUser } from '~/stores/context';
+
+import { withUnstatedContainers } from '../../UnstatedUtils';
+
+type RemoveAdminButtonProps = {
+  adminUsersContainer: AdminUsersContainer,
+  user: IUserHasId,
+}
+
+const RemoveAdminButton = (props: RemoveAdminButtonProps): JSX.Element => {
+
+  const { t } = useTranslation();
+  const { data: currentUser } = useCurrentUser();
+  const { adminUsersContainer, user } = props;
+
+  const onClickRemoveAdminBtnHandler = useCallback(async() => {
+    try {
+      const username = await adminUsersContainer.removeUserAdmin(user._id);
+      toastSuccess(t('admin:toaster.remove_user_admin', { username }));
+    }
+    catch (err) {
+      toastError(err);
+    }
+  }, [adminUsersContainer, t, user._id]);
+
+  const renderRemoveAdminBtn = () => {
+    return (
+      <button className="dropdown-item" type="button" onClick={() => onClickRemoveAdminBtnHandler()}>
+        <i className="icon-fw icon-user-unfollow"></i>{t('admin:user_management.user_table.remove_admin_access')}
+      </button>
+    );
+  };
+
+  const renderRemoveAdminAlert = () => {
+    return (
+      <div className="px-4">
+        <i className="icon-fw icon-user-unfollow mb-2"></i>{t('admin:user_management.user_table.remove_admin_access')}
+        <p className="alert alert-danger">{t('admin:user_management.user_table.cannot_remove')}</p>
+      </div>
+    );
+  };
+
+  if (currentUser == null) {
+    return <></>;
+  }
+
+  return (
+    <>
+      {user.username !== currentUser.username ? renderRemoveAdminBtn()
+        : renderRemoveAdminAlert()}
+    </>
+  );
+};
+
+/**
+* Wrapper component for using unstated
+*/
+const RemoveAdminButtonWrapper = withUnstatedContainers(RemoveAdminButton, [AdminUsersContainer]);
+
+export default RemoveAdminButtonWrapper;

+ 2 - 2
packages/app/src/components/Admin/Users/RemoveAdminMenuItem.tsx

@@ -1,10 +1,10 @@
 import React, { useCallback } from 'react';
 import React, { useCallback } from 'react';
 
 
+import type { IUserHasId } from '@growi/core';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 
 
 import AdminUsersContainer from '~/client/services/AdminUsersContainer';
 import AdminUsersContainer from '~/client/services/AdminUsersContainer';
 import { toastSuccess, toastError } from '~/client/util/apiNotification';
 import { toastSuccess, toastError } from '~/client/util/apiNotification';
-import { IUserHasId } from '~/interfaces/user';
 import { useCurrentUser } from '~/stores/context';
 import { useCurrentUser } from '~/stores/context';
 
 
 import { withUnstatedContainers } from '../../UnstatedUtils';
 import { withUnstatedContainers } from '../../UnstatedUtils';
@@ -38,7 +38,7 @@ const RemoveAdminMenuItem = (props: Props): JSX.Element => {
   const clickRemoveAdminBtnHandler = useCallback(async() => {
   const clickRemoveAdminBtnHandler = useCallback(async() => {
     try {
     try {
       const username = await adminUsersContainer.removeUserAdmin(user._id);
       const username = await adminUsersContainer.removeUserAdmin(user._id);
-      toastSuccess(t('toaster.remove_user_admin', { username }));
+      toastSuccess(t('admin:toaster.remove_user_admin', { username }));
     }
     }
     catch (err) {
     catch (err) {
       toastError(err);
       toastError(err);

+ 9 - 13
packages/app/src/components/Admin/Users/SortIcons.jsx → packages/app/src/components/Admin/Users/SortIcons.tsx

@@ -1,31 +1,27 @@
 import React from 'react';
 import React from 'react';
 
 
-import PropTypes from 'prop-types';
+type SortIconsProps = {
+  onClick: (sortOrder: string) => void,
+  isSelected: boolean,
+  isAsc: boolean,
+}
 
 
-const SortIcons = (props) => {
+export const SortIcons = (props: SortIconsProps): JSX.Element => {
 
 
-  const { isSelected, isAsc } = props;
+  const { onClick, isSelected, isAsc } = props;
 
 
   return (
   return (
     <div className="d-flex flex-column text-center">
     <div className="d-flex flex-column text-center">
       <a
       <a
         className={`fa ${isSelected && isAsc ? 'fa-chevron-up' : 'fa-angle-up'}`}
         className={`fa ${isSelected && isAsc ? 'fa-chevron-up' : 'fa-angle-up'}`}
         aria-hidden="true"
         aria-hidden="true"
-        onClick={() => props.onClick('asc')}
+        onClick={() => onClick('asc')}
       />
       />
       <a
       <a
         className={`fa ${isSelected && !isAsc ? 'fa-chevron-down' : 'fa-angle-down'}`}
         className={`fa ${isSelected && !isAsc ? 'fa-chevron-down' : 'fa-angle-down'}`}
         aria-hidden="true"
         aria-hidden="true"
-        onClick={() => props.onClick('desc')}
+        onClick={() => onClick('desc')}
       />
       />
     </div>
     </div>
   );
   );
 };
 };
-
-SortIcons.propTypes = {
-  onClick: PropTypes.func.isRequired,
-  isSelected: PropTypes.bool.isRequired,
-  isAsc: PropTypes.bool.isRequired,
-};
-
-export default SortIcons;

+ 1 - 1
packages/app/src/components/Admin/Users/StatusSuspendMenuItem.tsx

@@ -1,11 +1,11 @@
 import React, { useCallback } from 'react';
 import React, { useCallback } from 'react';
 
 
+import type { IUserHasId } from '@growi/core';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 
 
 import AdminUsersContainer from '~/client/services/AdminUsersContainer';
 import AdminUsersContainer from '~/client/services/AdminUsersContainer';
 import { toastSuccess, toastError } from '~/client/util/apiNotification';
 import { toastSuccess, toastError } from '~/client/util/apiNotification';
 import { withUnstatedContainers } from '~/components/UnstatedUtils';
 import { withUnstatedContainers } from '~/components/UnstatedUtils';
-import { IUserHasId } from '~/interfaces/user';
 import { useCurrentUser } from '~/stores/context';
 import { useCurrentUser } from '~/stores/context';
 
 
 
 

+ 1 - 1
packages/app/src/components/Admin/Users/UserTable.jsx

@@ -9,7 +9,7 @@ import AdminUsersContainer from '~/client/services/AdminUsersContainer';
 
 
 import { withUnstatedContainers } from '../../UnstatedUtils';
 import { withUnstatedContainers } from '../../UnstatedUtils';
 
 
-import SortIcons from './SortIcons';
+import { SortIcons } from './SortIcons';
 import UserMenu from './UserMenu';
 import UserMenu from './UserMenu';
 
 
 
 

+ 1 - 1
packages/app/src/components/ContentLinkButtons.tsx

@@ -1,6 +1,6 @@
 import React, { useCallback } from 'react';
 import React, { useCallback } from 'react';
 
 
-import { IUserHasId } from '@growi/core';
+import type { IUserHasId } from '@growi/core';
 
 
 import { smoothScrollIntoView } from '~/client/util/smooth-scroll';
 import { smoothScrollIntoView } from '~/client/util/smooth-scroll';
 import { RecentlyCreatedIcon } from '~/components/Icons/RecentlyCreatedIcon';
 import { RecentlyCreatedIcon } from '~/components/Icons/RecentlyCreatedIcon';

+ 1 - 1
packages/app/src/components/User/UserInfo.tsx

@@ -1,6 +1,6 @@
 import React from 'react';
 import React from 'react';
 
 
-import { IUserHasId } from '@growi/core';
+import type { IUserHasId } from '@growi/core';
 import { UserPicture } from '@growi/ui';
 import { UserPicture } from '@growi/ui';
 
 
 import styles from './UserInfo.module.scss';
 import styles from './UserInfo.module.scss';