UserTable.tsx 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. import React, { useCallback } from 'react';
  2. import type { IUserHasId } from '@growi/core';
  3. import { UserPicture } from '@growi/ui/dist/components';
  4. import { format as dateFnsFormat } from 'date-fns/format';
  5. import { useTranslation } from 'next-i18next';
  6. import AdminUsersContainer from '~/client/services/AdminUsersContainer';
  7. import { withUnstatedContainers } from '../../UnstatedUtils';
  8. import { SortIcons } from './SortIcons';
  9. import UserMenu from './UserMenu';
  10. type UserTableExternalProps = Record<string, never>;
  11. type UserTableProps = UserTableExternalProps & {
  12. adminUsersContainer: AdminUsersContainer;
  13. };
  14. const UserTable = (props: UserTableProps) => {
  15. const { t } = useTranslation('admin');
  16. const { adminUsersContainer } = props;
  17. const getUserStatusLabel = (userStatus: number) => {
  18. let additionalClassName = 'text-bg-info';
  19. let text = 'Approval Pending';
  20. switch (userStatus) {
  21. case 1:
  22. additionalClassName = 'text-bg-info';
  23. text = 'Approval Pending';
  24. break;
  25. case 2:
  26. additionalClassName = 'text-bg-success';
  27. text = 'Active';
  28. break;
  29. case 3:
  30. additionalClassName = 'text-bg-warning';
  31. text = 'Suspended';
  32. break;
  33. case 4:
  34. additionalClassName = 'text-bg-danger';
  35. text = 'Deleted';
  36. break;
  37. case 5:
  38. additionalClassName = 'text-bg-secondary';
  39. text = 'Invited';
  40. break;
  41. }
  42. return <span className={`badge ${additionalClassName}`}>{text}</span>;
  43. };
  44. const sortIconsClickedHandler = useCallback(
  45. async (sort: string, sortOrder: string) => {
  46. const isAsc = sortOrder === 'asc';
  47. await adminUsersContainer.sort(sort, isAsc);
  48. },
  49. [adminUsersContainer],
  50. );
  51. const isCurrentSortOrderAsc = adminUsersContainer.state.sortOrder === 'asc';
  52. return (
  53. <div className="table-responsive text-nowrap h-100">
  54. <table className="table table-default table-bordered table-user-list">
  55. <thead>
  56. <tr>
  57. <th style={{ width: '100px' }}>#</th>
  58. <th>
  59. <div className="d-flex align-items-center">
  60. <div className="me-3">{t('user_management.status')}</div>
  61. <SortIcons
  62. isSelected={adminUsersContainer.state.sort === 'status'}
  63. isAsc={isCurrentSortOrderAsc}
  64. onClick={(sortOrder) =>
  65. sortIconsClickedHandler('status', sortOrder)
  66. }
  67. />
  68. </div>
  69. </th>
  70. <th>
  71. <div className="d-flex align-items-center">
  72. <div className="me-3">
  73. <code>username</code>
  74. </div>
  75. <SortIcons
  76. isSelected={adminUsersContainer.state.sort === 'username'}
  77. isAsc={isCurrentSortOrderAsc}
  78. onClick={(sortOrder) =>
  79. sortIconsClickedHandler('username', sortOrder)
  80. }
  81. />
  82. </div>
  83. </th>
  84. <th>
  85. <div className="d-flex align-items-center">
  86. <div className="me-3">{t('Name')}</div>
  87. <SortIcons
  88. isSelected={adminUsersContainer.state.sort === 'name'}
  89. isAsc={isCurrentSortOrderAsc}
  90. onClick={(sortOrder) =>
  91. sortIconsClickedHandler('name', sortOrder)
  92. }
  93. />
  94. </div>
  95. </th>
  96. <th>
  97. <div className="d-flex align-items-center">
  98. <div className="me-3">{t('Email')}</div>
  99. <SortIcons
  100. isSelected={adminUsersContainer.state.sort === 'email'}
  101. isAsc={isCurrentSortOrderAsc}
  102. onClick={(sortOrder) =>
  103. sortIconsClickedHandler('email', sortOrder)
  104. }
  105. />
  106. </div>
  107. </th>
  108. <th style={{ width: '100px' }}>
  109. <div className="d-flex align-items-center">
  110. <div className="me-3">{t('Created')}</div>
  111. <SortIcons
  112. isSelected={adminUsersContainer.state.sort === 'createdAt'}
  113. isAsc={isCurrentSortOrderAsc}
  114. onClick={(sortOrder) =>
  115. sortIconsClickedHandler('createdAt', sortOrder)
  116. }
  117. />
  118. </div>
  119. </th>
  120. <th style={{ width: '150px' }}>
  121. <div className="d-flex align-items-center">
  122. <div className="me-3">{t('last_login')}</div>
  123. <SortIcons
  124. isSelected={adminUsersContainer.state.sort === 'lastLoginAt'}
  125. isAsc={isCurrentSortOrderAsc}
  126. onClick={(sortOrder) =>
  127. sortIconsClickedHandler('lastLoginAt', sortOrder)
  128. }
  129. />
  130. </div>
  131. </th>
  132. <th style={{ width: '70px' }}></th>
  133. </tr>
  134. </thead>
  135. <tbody>
  136. {adminUsersContainer.state.users.map((user: IUserHasId) => {
  137. return (
  138. <tr data-testid="user-table-tr" key={user._id}>
  139. <td>
  140. <UserPicture user={user} />
  141. </td>
  142. <td>
  143. {getUserStatusLabel(user.status)}
  144. {user.admin && (
  145. <span className="badge text-bg-secondary ms-2">
  146. {t('admin:user_management.user_table.administrator')}
  147. </span>
  148. )}
  149. {user.readOnly && (
  150. <span className="badge text-bg-light ms-2">
  151. {t('admin:user_management.user_table.read_only')}
  152. </span>
  153. )}
  154. </td>
  155. <td>
  156. <strong>{user.username}</strong>
  157. </td>
  158. <td>{user.name}</td>
  159. <td>{user.email}</td>
  160. <td>{dateFnsFormat(user.createdAt, 'yyyy-MM-dd')}</td>
  161. <td>
  162. {user.lastLoginAt && (
  163. <span>
  164. {dateFnsFormat(
  165. new Date(user.lastLoginAt),
  166. 'yyyy-MM-dd HH:mm',
  167. )}
  168. </span>
  169. )}
  170. </td>
  171. <td>
  172. <UserMenu user={user} />
  173. </td>
  174. </tr>
  175. );
  176. })}
  177. </tbody>
  178. </table>
  179. </div>
  180. );
  181. };
  182. const UserTableWrapper = withUnstatedContainers<
  183. UserTableExternalProps,
  184. UserTableProps
  185. >(UserTable, [AdminUsersContainer]);
  186. export default UserTableWrapper;