AdminUsers.vue 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. <template>
  2. <div class='admin_users' ref='scrollElement'>
  3. <h1 class='admin_users__header'>Users</h1>
  4. <div class='category_widget__box'>
  5. <div class='category_widget__text__title'>Filter users</div>
  6. <div class='admin_users__filters'>
  7. <fancy-input
  8. placeholder='Filter users'
  9. :large='true'
  10. v-model='search'
  11. ></fancy-input>
  12. <select-filter
  13. name='Role'
  14. :options='roleOptions'
  15. v-model='roleSelected'
  16. >
  17. </select-filter>
  18. </div>
  19. </div>
  20. <scroll-load
  21. class='category_widget__box'
  22. @loadNext='fetchData'
  23. :loading='loading'
  24. query-selector='.admin_users'
  25. :padding-bottom='100'
  26. >
  27. <div class='category_widget__text__title'>Showing 18 / 18 users</div>
  28. <table>
  29. <tr>
  30. <th>
  31. <sort-menu v-model='tableSort' column='username' display='Username'></sort-menu>
  32. </th>
  33. <th>
  34. Role
  35. </th>
  36. <th>
  37. <sort-menu v-model='tableSort' column='createdAt' display='Account created at'></sort-menu>
  38. </th>
  39. <th>
  40. <sort-menu v-model='tableSort' column='postCount' display='Posts count'></sort-menu>
  41. </th>
  42. <th>
  43. <sort-menu v-model='tableSort' column='threadCount' display='Threads count'></sort-menu>
  44. </th>
  45. </tr>
  46. <tr v-for='user in users'>
  47. <td class='admin_users__user_column'>
  48. <avatar-icon :user='user' size='small'></avatar-icon>
  49. <router-link :to='"/user/" + user.username'>{{user.username}}</router-link>
  50. </td>
  51. <td>{{user.admin ? "Admin" : "User"}}</td>
  52. <td>{{user.createdAt | formatDate}}</td>
  53. <td>{{user.postCount}}</td>
  54. <td>{{user.threadCount}}</td>
  55. </tr>
  56. </table>
  57. <transition name='fade' mode='out-in'>
  58. <loading-message key='loading' v-if='loading'></loading-message>
  59. <div class='overlay_message' v-if='!loading && !users.length'>
  60. No users found
  61. </div>
  62. </transition>
  63. </scroll-load>
  64. </div>
  65. </template>
  66. <script>
  67. import SelectFilter from '../SelectFilter.vue';
  68. import SortMenu from '../SortMenu.vue';
  69. import FancyInput from '../FancyInput.vue';
  70. import LoadingMessage from '../LoadingMessage';
  71. import ScrollLoad from '../ScrollLoad';
  72. import AvatarIcon from '../AvatarIcon';
  73. import throttle from 'lodash.throttle';
  74. import AjaxErrorHandler from '../../assets/js/errorHandler';
  75. export default {
  76. name: 'AdminUsers',
  77. components: {
  78. FancyInput,
  79. SelectFilter,
  80. SortMenu,
  81. LoadingMessage,
  82. ScrollLoad,
  83. AvatarIcon
  84. },
  85. data () {
  86. return {
  87. search: '',
  88. users: [],
  89. loading: true,
  90. offset: 0,
  91. limit: 15,
  92. roleOptions: [
  93. { name: 'Admins', value: 'admin' },
  94. { name: 'Users', value: 'user' }
  95. ],
  96. roleSelected: ['admin', 'user'],
  97. tableSort: {
  98. column: 'username',
  99. sort: 'desc'
  100. }
  101. }
  102. },
  103. methods: {
  104. fetchData () {
  105. if(this.offset === null) return;
  106. let url = `/api/v1/user?
  107. sort=${this.tableSort.column}
  108. &order=${this.tableSort.sort}
  109. &offset=${this.offset}
  110. `;
  111. if(this.roleSelected.length === 1) {
  112. url += '&role=' + this.roleSelected[0];
  113. }
  114. if(this.search.length) {
  115. url += '&search=' + encodeURIComponent(this.search.trim());
  116. }
  117. /* let loading = true;
  118. setTimeout(() => {
  119. if(loading) {
  120. this.loading = true;
  121. this.users = [];
  122. }
  123. }, 200);*/
  124. this.loading = true;
  125. this.axios
  126. .get(url)
  127. .then(res => {
  128. this.users.push(...res.data);
  129. this.loading = /*loading =*/ false;
  130. //If returned data is less than the limit
  131. //then there must be no more pages to paginate
  132. if(res.data.length < this.limit) {
  133. this.offset = null;
  134. } else {
  135. this.offset+= this.limit;
  136. }
  137. })
  138. .catch(e => {
  139. AjaxErrorHandler(this.$store)(e);
  140. this.loading = /*loading =*/ false;
  141. });
  142. },
  143. resetFetchData () {
  144. this.offset = 0;
  145. this.users = [];
  146. this.fetchData();
  147. }
  148. },
  149. mounted () {
  150. this.fetchData();
  151. },
  152. watch: {
  153. tableSort: 'resetFetchData',
  154. roleSelected: 'resetFetchData',
  155. search: throttle(function (input) {
  156. this.resetFetchData();
  157. }, 200)
  158. }
  159. }
  160. </script>
  161. <style lang='scss' scoped>
  162. @import '../../assets/scss/variables.scss';
  163. .admin_users {
  164. padding: 1rem 2rem;
  165. @at-root #{&}__header {
  166. margin: 0.5rem 0 1rem 0;
  167. }
  168. @at-root #{&}__filters {
  169. margin-bottom: 0.5rem;
  170. .select_filter {
  171. margin-right: 0.5rem;
  172. }
  173. }
  174. table {
  175. border-collapse: collapse;
  176. width: 100%;
  177. th {
  178. border-bottom: 0.125rem solid $color__gray--darker;
  179. padding: 0.5rem 0.75rem;
  180. text-align: left;
  181. }
  182. tr {
  183. cursor: default;
  184. &:first-child {
  185. background-color: #fff;
  186. }
  187. &:nth-child(odd) {
  188. background-color: lighten($color__gray--primary, 20%);
  189. }
  190. &:nth-child(even) {
  191. background-color: $color__gray--primary;
  192. }
  193. }
  194. td {
  195. padding: 0.75rem;
  196. }
  197. }
  198. @at-root #{&}__user_column {
  199. display: flex;
  200. align-items: center;
  201. a {
  202. margin: 0 0.25rem;
  203. }
  204. }
  205. .overlay_message {
  206. padding-top: 2rem;
  207. padding-bottom: 1rem;
  208. }
  209. }
  210. </style>