فهرست منبع

create UserTable

jam411 3 سال پیش
والد
کامیت
6881283419

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

@@ -4,6 +4,7 @@
   },
   "wiki_management_home_page": "Wiki Management Home Page",
   "app_settings": "App Settings",
+  "last_login": "Last login",
   "security_settings": {
     "security_settings": "Security Settings",
     "Guest Users Access": "Guest users access",

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

@@ -78,7 +78,6 @@
   "username": "Username",
   "Created": "Created",
   "Last updated": "Updated",
-  "last_login": "Last login",
   "Share": "Share",
   "Markdown Link": "Markdown Link",
   "Create/Edit Template": "Create/Edit template page",

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

@@ -9,6 +9,7 @@
   "Created": "创建",
   "Edit": "编辑",
   "Description": "描述",
+  "last_login": "上次登录",
   "wiki_management_home_page": "Wiki管理首页",
   "app_settings": "系统设置",
   "public": "公共",

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

@@ -73,7 +73,6 @@
   "username": "用户名",
 	"Created": "创建",
 	"Last updated": "上次更新",
-  "last_login": "上次登录",
 	"Share": "分享",
   "Share Link": "分享链接",
 	"Markdown Link": "Markdown链接",

+ 26 - 22
packages/app/src/components/Admin/UserManagement.tsx

@@ -32,7 +32,7 @@ const UserManagement = (props: UserManagementProps) => {
     catch (err) {
       toastError(err);
     }
-  })
+  });
 
   // componentDidMount
   useEffect(() => {
@@ -43,10 +43,10 @@ const UserManagement = (props: UserManagementProps) => {
     return (adminUsersContainer.isSelected(statusType)) ? (
       adminUsersContainer.state.selectedStatusList.size > 1
     )
-    : (
-      true
-    )
-  }
+      : (
+        true
+      );
+  };
 
   const clickHandler = (statusType: string) => {
     if (!validateToggleStatus(statusType)) {
@@ -57,22 +57,24 @@ const UserManagement = (props: UserManagementProps) => {
       setIsNotifyCommentShow(false);
     }
     adminUsersContainer.handleClick(statusType);
-  }
+  };
 
   const resetButtonClickHandler = (async() => {
     try {
       await adminUsersContainer.resetAllChanges();
       setIsNotifyCommentShow(false);
-      (inputRef.current != null) && (inputRef.current.value = '');
+      if (inputRef.current != null) {
+        inputRef.current.value = '';
+      }
     }
     catch (err) {
       toastError(err);
     }
-  })
+  });
 
   const changeSearchTextHandler = (async(e: React.FormEvent<HTMLInputElement>) => {
     await adminUsersContainer.handleChangeSearchText(e?.currentTarget.value);
-  })
+  });
 
   const renderCheckbox = (status: string, statusLabel: string, statusColor: string) => {
     return (
@@ -90,8 +92,8 @@ const UserManagement = (props: UserManagementProps) => {
           </span>
         </label>
       </div>
-    )
-  }
+    );
+  };
 
   const pager = (
     <div className="my-3">
@@ -119,7 +121,7 @@ const UserManagement = (props: UserManagementProps) => {
       <p>
         <InviteUserControl />
         <a className="btn btn-outline-secondary ml-2" href="/admin/users/external-accounts" role="button">
-          <i className="icon-user-follow" aria-hidden="true"></i>
+          <i className="icon-user-follow mr-1" aria-hidden="true"></i>
           {t('admin:user_management.external_account')}
         </a>
       </p>
@@ -140,15 +142,17 @@ const UserManagement = (props: UserManagementProps) => {
               {/* TODO: Fix position */}
               {
                 adminUsersContainer.state.searchText.length > 0
-                ? ( <i
-                     className={`icon-close ${styles['search-clear']}`}
-                      onClick={async() => {
-                        await adminUsersContainer.clearSearchText();
-                        (inputRef.current != null) && (inputRef.current.value = '');
-                      }}
-                    />
+                  ? (<i
+                    className={`icon-close ${styles['search-clear']}`}
+                    onClick={async() => {
+                      await adminUsersContainer.clearSearchText();
+                      if (inputRef.current != null) {
+                        inputRef.current.value = '';
+                      }
+                    }}
+                  />
                   )
-                : ''
+                  : ''
               }
             </span>
           </div>
@@ -184,9 +188,9 @@ const UserManagement = (props: UserManagementProps) => {
       {pager}
 
     </div>
-  )
+  );
 
-}
+};
 
 const UserManagementWrapper = withUnstatedContainers(UserManagement, [AdminUsersContainer]);
 

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

@@ -1,231 +0,0 @@
-import React, { Fragment } from 'react';
-
-import { UserPicture } from '@growi/ui';
-import dateFnsFormat from 'date-fns/format';
-import { useTranslation } from 'next-i18next';
-import PropTypes from 'prop-types';
-
-import AdminUsersContainer from '~/client/services/AdminUsersContainer';
-
-import { withUnstatedContainers } from '../../UnstatedUtils';
-
-import { SortIcons } from './SortIcons';
-import UserMenu from './UserMenu';
-
-
-class UserTable extends React.Component {
-
-  constructor(props) {
-    super(props);
-
-    this.state = {
-
-    };
-
-    this.getUserStatusLabel = this.getUserStatusLabel.bind(this);
-  }
-
-  /**
-   * return status label element by `userStatus`
-   * @param {string} userStatus
-   * @return status label element
-   */
-  getUserStatusLabel(userStatus) {
-    let additionalClassName;
-    let text;
-
-    switch (userStatus) {
-      case 1:
-        additionalClassName = 'badge-info';
-        text = 'Approval Pending';
-        break;
-      case 2:
-        additionalClassName = 'badge-success';
-        text = 'Active';
-        break;
-      case 3:
-        additionalClassName = 'badge-warning';
-        text = 'Suspended';
-        break;
-      case 4:
-        additionalClassName = 'badge-danger';
-        text = 'Deleted';
-        break;
-      case 5:
-        additionalClassName = 'badge-pink';
-        text = 'Invited';
-        break;
-    }
-
-    return (
-      <span className={`badge badge-pill ${additionalClassName}`}>
-        {text}
-      </span>
-    );
-  }
-
-  /**
-   * return admin label element by `isAdmin`
-   * @param {string} isAdmin
-   * @return admin label element
-   */
-  getUserAdminLabel(isAdmin) {
-    const { t } = this.props;
-
-    if (isAdmin) {
-      return <span className="badge badge-indigo badge-pill ml-2">{t('admin:user_management.user_table.administrator')}</span>;
-    }
-  }
-
-  sortIconsClickedHandler(sort, sortOrder) {
-    const isAsc = sortOrder === 'asc';
-
-    const { adminUsersContainer } = this.props;
-    adminUsersContainer.sort(sort, isAsc);
-  }
-
-  render() {
-    const { t, adminUsersContainer } = this.props;
-
-    const isCurrentSortOrderAsc = adminUsersContainer.state.sortOrder === 'asc';
-
-    return (
-      <Fragment>
-        <div className="table-responsive text-nowrap h-100">
-          <table className="table table-default table-bordered table-user-list">
-            <thead>
-              <tr>
-                <th width="100px">#</th>
-                <th>
-                  <div className="d-flex align-items-center">
-                    <div className="mr-3">
-                      {t('user_management.status')}
-                    </div>
-                    <SortIcons
-                      isSelected={adminUsersContainer.state.sort === 'status'}
-                      isAsc={isCurrentSortOrderAsc}
-                      onClick={(sortOrder) => {
-                        this.sortIconsClickedHandler('status', sortOrder);
-                      }}
-                    />
-                  </div>
-                </th>
-                <th>
-                  <div className="d-flex align-items-center">
-                    <div className="mr-3">
-                      <code>username</code>
-                    </div>
-                    <SortIcons
-                      isSelected={adminUsersContainer.state.sort === 'username'}
-                      isAsc={isCurrentSortOrderAsc}
-                      onClick={(sortOrder) => {
-                        this.sortIconsClickedHandler('username', sortOrder);
-                      }}
-                    />
-                  </div>
-                </th>
-                <th>
-                  <div className="d-flex align-items-center">
-                    <div className="mr-3">
-                      {t('Name')}
-                    </div>
-                    <SortIcons
-                      isSelected={adminUsersContainer.state.sort === 'name'}
-                      isAsc={isCurrentSortOrderAsc}
-                      onClick={(sortOrder) => {
-                        this.sortIconsClickedHandler('name', sortOrder);
-                      }}
-                    />
-                  </div>
-                </th>
-                <th>
-                  <div className="d-flex align-items-center">
-                    <div className="mr-3">
-                      {t('Email')}
-                    </div>
-                    <SortIcons
-                      isSelected={adminUsersContainer.state.sort === 'email'}
-                      isAsc={isCurrentSortOrderAsc}
-                      onClick={(sortOrder) => {
-                        this.sortIconsClickedHandler('email', sortOrder);
-                      }}
-                    />
-                  </div>
-                </th>
-                <th width="100px">
-                  <div className="d-flex align-items-center">
-                    <div className="mr-3">
-                      {t('Created')}
-                    </div>
-                    <SortIcons
-                      isSelected={adminUsersContainer.state.sort === 'createdAt'}
-                      isAsc={isCurrentSortOrderAsc}
-                      onClick={(sortOrder) => {
-                        this.sortIconsClickedHandler('createdAt', sortOrder);
-                      }}
-                    />
-                  </div>
-                </th>
-                <th width="150px">
-                  <div className="d-flex align-items-center">
-                    <div className="mr-3">
-                      {t('last_login')}
-                    </div>
-                    <SortIcons
-                      isSelected={adminUsersContainer.state.sort === 'lastLoginAt'}
-                      isAsc={isCurrentSortOrderAsc}
-                      onClick={(sortOrder) => {
-                        this.sortIconsClickedHandler('lastLoginAt', sortOrder);
-                      }}
-                    />
-                  </div>
-                </th>
-                <th width="70px"></th>
-              </tr>
-            </thead>
-            <tbody>
-              {adminUsersContainer.state.users.map((user) => {
-                return (
-                  <tr data-testid="user-table-tr" key={user._id}>
-                    <td>
-                      <UserPicture user={user} className="picture rounded-circle" />
-                    </td>
-                    <td>{this.getUserStatusLabel(user.status)} {this.getUserAdminLabel(user.admin)}</td>
-                    <td>
-                      <strong>{user.username}</strong>
-                    </td>
-                    <td>{user.name}</td>
-                    <td>{user.email}</td>
-                    <td>{dateFnsFormat(new Date(user.createdAt), 'yyyy-MM-dd')}</td>
-                    <td>
-                      {user.lastLoginAt && <span>{dateFnsFormat(new Date(user.lastLoginAt), 'yyyy-MM-dd HH:mm')}</span>}
-                    </td>
-                    <td>
-                      <UserMenu user={user} />
-                    </td>
-                  </tr>
-                );
-              })}
-            </tbody>
-          </table>
-        </div>
-      </Fragment>
-    );
-  }
-
-}
-
-
-UserTable.propTypes = {
-  t: PropTypes.func.isRequired, // i18next
-  adminUsersContainer: PropTypes.instanceOf(AdminUsersContainer).isRequired,
-};
-
-const UserTableWrapperFC = (props) => {
-  const { t } = useTranslation('admin');
-  return <UserTable t={t} {...props} />;
-};
-
-const UserTableWrapper = withUnstatedContainers(UserTableWrapperFC, [AdminUsersContainer]);
-
-export default UserTableWrapper;

+ 190 - 0
packages/app/src/components/Admin/Users/UserTable.tsx

@@ -0,0 +1,190 @@
+import React, { useEffect } from 'react';
+
+import { UserPicture } from '@growi/ui';
+import dateFnsFormat from 'date-fns/format';
+import { useTranslation } from 'next-i18next';
+
+import type { IUserHasId } from '@growi/core';
+import AdminUsersContainer from '~/client/services/AdminUsersContainer';
+
+import { withUnstatedContainers } from '../../UnstatedUtils';
+
+import { SortIcons } from './SortIcons';
+import UserMenu from './UserMenu';
+
+type UserTableProps = {
+  adminUsersContainer: AdminUsersContainer,
+}
+
+const UserTable = (props: UserTableProps) => {
+
+  const { t } = useTranslation('admin');
+  const { adminUsersContainer } = props;
+
+
+  useEffect(()=>{
+    adminUsersContainer.state.users
+  }, [])
+
+  const getUserStatusLabel = (userStatus: number) => {
+    let additionalClassName = 'badge-info';
+    let text = 'Approval Pending';
+
+    switch (userStatus) {
+      case 1:
+        additionalClassName = 'badge-info';
+        text = 'Approval Pending';
+        break;
+      case 2:
+        additionalClassName = 'badge-success';
+        text = 'Active';
+        break;
+      case 3:
+        additionalClassName = 'badge-warning';
+        text = 'Suspended';
+        break;
+      case 4:
+        additionalClassName = 'badge-danger';
+        text = 'Deleted';
+        break;
+      case 5:
+        additionalClassName = 'badge-pink';
+        text = 'Invited';
+        break;
+    }
+
+    return (
+      <span className={`badge badge-pill ${additionalClassName}`}>
+        {text}
+      </span>
+    )
+  }
+
+  const sortIconsClickedHandler = (async(sort: string, sortOrder: string) => {
+    const isAsc = sortOrder === 'asc';
+    await adminUsersContainer.sort(sort, isAsc);
+  })
+
+  const isCurrentSortOrderAsc = adminUsersContainer.state.sortOrder === 'asc';
+
+  return (
+    <div className="table-responsive text-nowrap h-100">
+      <table className="table table-default table-bordered table-user-list">
+        <thead>
+          <tr>
+            <th style={{ width: "100px" }}>#</th>
+            <th>
+              <div className="d-flex align-items-center">
+                <div className="mr-3">
+                  {t('user_management.status')}
+                </div>
+                <SortIcons
+                  isSelected={adminUsersContainer.state.sort === 'status'}
+                  isAsc={isCurrentSortOrderAsc}
+                  onClick={(sortOrder) => sortIconsClickedHandler('status', sortOrder)}
+                />
+              </div>
+            </th>
+            <th>
+              <div className="d-flex align-items-center">
+                <div className="mr-3">
+                  <code>username</code>
+                </div>
+                <SortIcons
+                  isSelected={adminUsersContainer.state.sort === 'username'}
+                  isAsc={isCurrentSortOrderAsc}
+                  onClick={(sortOrder) => sortIconsClickedHandler('username', sortOrder)}
+                />
+              </div>
+            </th>
+            <th>
+              <div className="d-flex align-items-center">
+                <div className="mr-3">
+                  {t('Name')}
+                </div>
+                <SortIcons
+                  isSelected={adminUsersContainer.state.sort === 'name'}
+                  isAsc={isCurrentSortOrderAsc}
+                  onClick={(sortOrder) => sortIconsClickedHandler('name', sortOrder)}
+                />
+              </div>
+            </th>
+            <th>
+              <div className="d-flex align-items-center">
+                <div className="mr-3">
+                  {t('Email')}
+                </div>
+                <SortIcons
+                  isSelected={adminUsersContainer.state.sort === 'email'}
+                  isAsc={isCurrentSortOrderAsc}
+                  onClick={(sortOrder) => sortIconsClickedHandler('email', sortOrder)}
+                />
+              </div>
+            </th>
+            <th style={{ width: "100px" }}>
+              <div className="d-flex align-items-center">
+                <div className="mr-3">
+                  {t('Created')}
+                </div>
+                <SortIcons
+                  isSelected={adminUsersContainer.state.sort === 'createdAt'}
+                  isAsc={isCurrentSortOrderAsc}
+                  onClick={(sortOrder) => sortIconsClickedHandler('createdAt', sortOrder)}
+                />
+              </div>
+            </th>
+            <th style={{ width: "150px" }}>
+              <div className="d-flex align-items-center">
+                <div className="mr-3">
+                  {t('last_login')}
+                </div>
+                <SortIcons
+                  isSelected={adminUsersContainer.state.sort === 'lastLoginAt'}
+                  isAsc={isCurrentSortOrderAsc}
+                  onClick={(sortOrder) => sortIconsClickedHandler('lastLoginAt', sortOrder)}
+                />
+              </div>
+            </th>
+            <th style={{ width: "70px" }}></th>
+          </tr>
+        </thead>
+        <tbody>
+          { adminUsersContainer.state.users.map((user: IUserHasId) => {
+            return (
+              <tr data-testid="user-table-tr" key={user._id}>
+                <td>
+                  <UserPicture user={user} className="picture rounded-circle" />
+                </td>
+                <td>
+                  {getUserStatusLabel(user.status)}
+                  {(user.admin) && (
+                    <span className="badge badge-indigo badge-pill ml-2">
+                      {t('admin:user_management.user_table.administrator')}
+                    </span>
+                  )}
+                </td>
+                <td>
+                  <strong>{user.username}</strong>
+                </td>
+                <td>{user.name}</td>
+                <td>{user.email}</td>
+                <td>{dateFnsFormat(new Date(user.createdAt), 'yyyy-MM-dd')}</td>
+                <td>
+                  {user.lastLoginAt && <span>{dateFnsFormat(new Date(user.lastLoginAt), 'yyyy-MM-dd HH:mm')}</span>}
+                </td>
+                <td>
+                  <UserMenu user={user} />
+                </td>
+              </tr>
+            );
+          }) }
+        </tbody>
+      </table>
+    </div>
+  )
+
+}
+
+const UserTableWrapper = withUnstatedContainers(UserTable, [AdminUsersContainer]);
+
+export default UserTableWrapper;