Users.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. <template>
  2. <div>
  3. <page-metadata title="Admin | Users" />
  4. <div class="container">
  5. <h2>Data Requests</h2>
  6. <advanced-table
  7. :column-default="dataRequests.columnDefault"
  8. :columns="dataRequests.columns"
  9. :filters="dataRequests.filters"
  10. data-action="dataRequests.getData"
  11. name="admin-data-requests"
  12. max-width="1200"
  13. :query="false"
  14. :events="dataRequests.events"
  15. >
  16. <template #column-options="slotProps">
  17. <div class="row-options">
  18. <quick-confirm
  19. placement="right"
  20. @confirm="resolveDataRequest(slotProps.item._id)"
  21. :disabled="slotProps.item.removed"
  22. >
  23. <button
  24. class="
  25. button
  26. is-success
  27. icon-with-button
  28. material-icons
  29. "
  30. content="Resolve Data Request"
  31. v-tippy
  32. >
  33. done_all
  34. </button>
  35. </quick-confirm>
  36. </div>
  37. </template>
  38. <template #column-type="slotProps">
  39. <span
  40. :title="
  41. slotProps.item.type
  42. ? 'Remove all associated data'
  43. : slotProps.item.type
  44. "
  45. >{{
  46. slotProps.item.type
  47. ? "Remove all associated data"
  48. : slotProps.item.type
  49. }}</span
  50. >
  51. </template>
  52. <template #column-userId="slotProps">
  53. <span :title="slotProps.item.userId">{{
  54. slotProps.item.userId
  55. }}</span>
  56. </template>
  57. <template #column-_id="slotProps">
  58. <span :title="slotProps.item._id">{{
  59. slotProps.item._id
  60. }}</span>
  61. </template>
  62. </advanced-table>
  63. <h1 id="page-title">Users</h1>
  64. <advanced-table
  65. :column-default="users.columnDefault"
  66. :columns="users.columns"
  67. :filters="users.filters"
  68. data-action="users.getData"
  69. name="admin-users"
  70. max-width="1200"
  71. :events="users.events"
  72. >
  73. <template #column-options="slotProps">
  74. <div class="row-options">
  75. <button
  76. class="
  77. button
  78. is-primary
  79. icon-with-button
  80. material-icons
  81. "
  82. @click="edit(slotProps.item._id)"
  83. :disabled="slotProps.item.removed"
  84. content="Edit User"
  85. v-tippy
  86. >
  87. edit
  88. </button>
  89. </div>
  90. </template>
  91. <template #column-profilePicture="slotProps">
  92. <profile-picture
  93. :avatar="slotProps.item.avatar"
  94. :name="
  95. slotProps.item.name
  96. ? slotProps.item.name
  97. : slotProps.item.username
  98. "
  99. />
  100. </template>
  101. <template #column-name="slotProps">
  102. <span :title="slotProps.item.name">{{
  103. slotProps.item.name
  104. }}</span>
  105. </template>
  106. <template #column-username="slotProps">
  107. <span :title="slotProps.item.username">{{
  108. slotProps.item.username
  109. }}</span>
  110. </template>
  111. <template #column-_id="slotProps">
  112. <span :title="slotProps.item._id">{{
  113. slotProps.item._id
  114. }}</span>
  115. </template>
  116. <template #column-githubId="slotProps">
  117. <span
  118. v-if="slotProps.item.services.github"
  119. :title="slotProps.item.services.github.id"
  120. >{{ slotProps.item.services.github.id }}</span
  121. >
  122. </template>
  123. <template #column-hasPassword="slotProps">
  124. <span :title="slotProps.item.hasPassword">{{
  125. slotProps.item.hasPassword
  126. }}</span>
  127. </template>
  128. <template #column-role="slotProps">
  129. <span :title="slotProps.item.role">{{
  130. slotProps.item.role
  131. }}</span>
  132. </template>
  133. <template #column-emailAddress="slotProps">
  134. <span :title="slotProps.item.email.address">{{
  135. slotProps.item.email.address
  136. }}</span>
  137. </template>
  138. <template #column-emailVerified="slotProps">
  139. <span :title="slotProps.item.email.verified">{{
  140. slotProps.item.email.verified
  141. }}</span>
  142. </template>
  143. <template #column-songsRequested="slotProps">
  144. <span :title="slotProps.item.statistics.songsRequested">{{
  145. slotProps.item.statistics.songsRequested
  146. }}</span>
  147. </template>
  148. </advanced-table>
  149. </div>
  150. <edit-user
  151. v-if="modals.editUser"
  152. :user-id="editingUserId"
  153. sector="admin"
  154. />
  155. </div>
  156. </template>
  157. <script>
  158. import { mapState, mapActions, mapGetters } from "vuex";
  159. import { defineAsyncComponent } from "vue";
  160. import Toast from "toasters";
  161. import AdvancedTable from "@/components/AdvancedTable.vue";
  162. import ProfilePicture from "@/components/ProfilePicture.vue";
  163. import QuickConfirm from "@/components/QuickConfirm.vue";
  164. export default {
  165. components: {
  166. EditUser: defineAsyncComponent(() =>
  167. import("@/components/modals/EditUser.vue")
  168. ),
  169. AdvancedTable,
  170. ProfilePicture,
  171. QuickConfirm
  172. },
  173. data() {
  174. return {
  175. editingUserId: "",
  176. dataRequests: {
  177. columnDefault: {
  178. sortable: true,
  179. hidable: true,
  180. defaultVisibility: "shown",
  181. draggable: true,
  182. resizable: true,
  183. minWidth: 150,
  184. maxWidth: 600
  185. },
  186. columns: [
  187. {
  188. name: "options",
  189. displayName: "Options",
  190. properties: ["_id"],
  191. sortable: false,
  192. hidable: false,
  193. resizable: false,
  194. minWidth: 76,
  195. defaultWidth: 76
  196. },
  197. {
  198. name: "type",
  199. displayName: "Type",
  200. properties: ["type"],
  201. sortable: false
  202. },
  203. {
  204. name: "userId",
  205. displayName: "User ID",
  206. properties: ["userId"],
  207. sortProperty: "userId"
  208. },
  209. {
  210. name: "_id",
  211. displayName: "Request ID",
  212. properties: ["_id"],
  213. sortProperty: "_id"
  214. }
  215. ],
  216. filters: [
  217. {
  218. name: "_id",
  219. displayName: "Request ID",
  220. property: "_id",
  221. filterTypes: ["exact"],
  222. defaultFilterType: "exact"
  223. },
  224. {
  225. name: "userId",
  226. displayName: "User ID",
  227. property: "userId",
  228. filterTypes: ["contains", "exact", "regex"],
  229. defaultFilterType: "contains"
  230. }
  231. ],
  232. events: {
  233. adminRoom: "users",
  234. removed: {
  235. event: "admin.dataRequests.resolved",
  236. id: "dataRequestId"
  237. }
  238. }
  239. },
  240. users: {
  241. columnDefault: {
  242. sortable: true,
  243. hidable: true,
  244. defaultVisibility: "shown",
  245. draggable: true,
  246. resizable: true,
  247. minWidth: 150,
  248. maxWidth: 600
  249. },
  250. columns: [
  251. {
  252. name: "options",
  253. displayName: "Options",
  254. properties: ["_id"],
  255. sortable: false,
  256. hidable: false,
  257. resizable: false,
  258. minWidth: 76,
  259. defaultWidth: 76
  260. },
  261. {
  262. name: "profilePicture",
  263. displayName: "Image",
  264. properties: ["avatar", "name", "username"],
  265. sortable: false,
  266. resizable: false,
  267. minWidth: 71,
  268. defaultWidth: 71
  269. },
  270. {
  271. name: "name",
  272. displayName: "Display Name",
  273. properties: ["name"],
  274. sortProperty: "name"
  275. },
  276. {
  277. name: "username",
  278. displayName: "Username",
  279. properties: ["username"],
  280. sortProperty: "username"
  281. },
  282. {
  283. name: "_id",
  284. displayName: "User ID",
  285. properties: ["_id"],
  286. sortProperty: "_id",
  287. minWidth: 230,
  288. defaultWidth: 230
  289. },
  290. {
  291. name: "githubId",
  292. displayName: "GitHub ID",
  293. properties: ["services.github.id"],
  294. sortProperty: "services.github.id",
  295. minWidth: 115,
  296. defaultWidth: 115
  297. },
  298. {
  299. name: "hasPassword",
  300. displayName: "Has Password",
  301. properties: ["hasPassword"],
  302. sortProperty: "hasPassword"
  303. },
  304. {
  305. name: "role",
  306. displayName: "Role",
  307. properties: ["role"],
  308. sortProperty: "role",
  309. minWidth: 90,
  310. defaultWidth: 90
  311. },
  312. {
  313. name: "emailAddress",
  314. displayName: "Email Address",
  315. properties: ["email.address"],
  316. sortProperty: "email.address",
  317. defaultVisibility: "hidden"
  318. },
  319. {
  320. name: "emailVerified",
  321. displayName: "Email Verified",
  322. properties: ["email.verified"],
  323. sortProperty: "email.verified",
  324. defaultVisibility: "hidden",
  325. minWidth: 140,
  326. defaultWidth: 140
  327. },
  328. {
  329. name: "songsRequested",
  330. displayName: "Songs Requested",
  331. properties: ["statistics.songsRequested"],
  332. sortProperty: "statistics.songsRequested",
  333. minWidth: 170,
  334. defaultWidth: 170
  335. }
  336. ],
  337. filters: [
  338. {
  339. name: "_id",
  340. displayName: "User ID",
  341. property: "_id",
  342. filterTypes: ["exact"],
  343. defaultFilterType: "exact"
  344. },
  345. {
  346. name: "name",
  347. displayName: "Display Name",
  348. property: "name",
  349. filterTypes: ["contains", "exact", "regex"],
  350. defaultFilterType: "contains"
  351. },
  352. {
  353. name: "username",
  354. displayName: "Username",
  355. property: "username",
  356. filterTypes: ["contains", "exact", "regex"],
  357. defaultFilterType: "contains"
  358. },
  359. {
  360. name: "githubId",
  361. displayName: "GitHub ID",
  362. property: "services.github.id",
  363. filterTypes: ["contains", "exact", "regex"],
  364. defaultFilterType: "contains"
  365. },
  366. {
  367. name: "hasPassword",
  368. displayName: "Has Password",
  369. property: "hasPassword",
  370. filterTypes: ["boolean"],
  371. defaultFilterType: "boolean"
  372. },
  373. {
  374. name: "role",
  375. displayName: "Role",
  376. property: "role",
  377. filterTypes: ["exact"],
  378. defaultFilterType: "exact",
  379. dropdown: [
  380. ["admin", "Admin"],
  381. ["default", "Default"]
  382. ]
  383. },
  384. {
  385. name: "emailAddress",
  386. displayName: "Email Address",
  387. property: "email.address",
  388. filterTypes: ["contains", "exact", "regex"],
  389. defaultFilterType: "contains"
  390. },
  391. {
  392. name: "emailVerified",
  393. displayName: "Email Verified",
  394. property: "email.verified",
  395. filterTypes: ["boolean"],
  396. defaultFilterType: "boolean"
  397. },
  398. {
  399. name: "songsRequested",
  400. displayName: "Songs Requested",
  401. property: "statistics.songsRequested",
  402. filterTypes: [
  403. "numberLesserEqual",
  404. "numberLesser",
  405. "numberGreater",
  406. "numberGreaterEqual",
  407. "numberEquals"
  408. ],
  409. defaultFilterType: "numberLesser"
  410. }
  411. ],
  412. events: {
  413. adminRoom: "users",
  414. updated: {
  415. event: "admin.user.updated",
  416. id: "user._id",
  417. item: "user"
  418. },
  419. removed: {
  420. event: "user.removed",
  421. id: "userId"
  422. }
  423. }
  424. }
  425. };
  426. },
  427. computed: {
  428. ...mapState("modalVisibility", {
  429. modals: state => state.modals
  430. }),
  431. ...mapGetters({
  432. socket: "websockets/getSocket"
  433. })
  434. },
  435. mounted() {
  436. if (this.$route.query.userId) this.edit(this.$route.query.userId);
  437. },
  438. methods: {
  439. edit(userId) {
  440. this.editingUserId = userId;
  441. this.openModal("editUser");
  442. },
  443. resolveDataRequest(id) {
  444. this.socket.dispatch("dataRequests.resolve", id, res => {
  445. if (res.status === "success") new Toast(res.message);
  446. });
  447. },
  448. ...mapActions("modalVisibility", ["openModal"])
  449. }
  450. };
  451. </script>
  452. <style lang="scss" scoped>
  453. #page-title {
  454. margin: 30px 0;
  455. }
  456. h2 {
  457. font-size: 30px;
  458. text-align: center;
  459. @media only screen and (min-width: 700px) {
  460. font-size: 35px;
  461. }
  462. }
  463. .profile-picture {
  464. max-width: 50px !important;
  465. max-height: 50px !important;
  466. }
  467. /deep/ .profile-picture.using-initials span {
  468. font-size: 20px; // 2/5th of .profile-picture height/width
  469. }
  470. </style>