Playlists.vue 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. <template>
  2. <div>
  3. <metadata title="Admin | Playlists" />
  4. <div class="container">
  5. <button
  6. class="button is-primary"
  7. @click="deleteOrphanedStationPlaylists()"
  8. >
  9. Delete orphaned station playlists
  10. </button>
  11. <button
  12. class="button is-primary"
  13. @click="deleteOrphanedGenrePlaylists()"
  14. >
  15. Delete orphaned genre playlists
  16. </button>
  17. <button
  18. class="button is-primary"
  19. @click="requestOrphanedPlaylistSongs()"
  20. >
  21. Request orphaned playlist songs
  22. </button>
  23. <button
  24. class="button is-primary"
  25. @click="clearAndRefillAllStationPlaylists()"
  26. >
  27. Clear and refill all station playlists
  28. </button>
  29. <button
  30. class="button is-primary"
  31. @click="clearAndRefillAllGenrePlaylists()"
  32. >
  33. Clear and refill all genre playlists
  34. </button>
  35. <br />
  36. <br />
  37. <table class="table is-striped">
  38. <thead>
  39. <tr>
  40. <td>Display name</td>
  41. <td>Type</td>
  42. <td>Is user modifiable</td>
  43. <td>Privacy</td>
  44. <td>Songs #</td>
  45. <td>Playlist length</td>
  46. <td>Created by</td>
  47. <td>Created at</td>
  48. <td>Created for</td>
  49. <td>Playlist id</td>
  50. <td>Options</td>
  51. </tr>
  52. </thead>
  53. <tbody>
  54. <tr v-for="playlist in playlists" :key="playlist._id">
  55. <td>{{ playlist.displayName }}</td>
  56. <td>{{ playlist.type }}</td>
  57. <td>{{ playlist.isUserModifiable }}</td>
  58. <td>{{ playlist.privacy }}</td>
  59. <td>{{ playlist.songs.length }}</td>
  60. <td>{{ totalLengthForPlaylist(playlist.songs) }}</td>
  61. <td v-if="playlist.createdBy === 'Musare'">Musare</td>
  62. <td v-else>
  63. <user-id-to-username
  64. :user-id="playlist.createdBy"
  65. :link="true"
  66. />
  67. </td>
  68. <td :title="new Date(playlist.createdAt)">
  69. {{ getDateFormatted(playlist.createdAt) }}
  70. </td>
  71. <td>{{ playlist.createdFor }}</td>
  72. <td>{{ playlist._id }}</td>
  73. <td>
  74. <button
  75. class="button is-primary"
  76. @click="edit(playlist._id)"
  77. >
  78. View
  79. </button>
  80. </td>
  81. </tr>
  82. </tbody>
  83. </table>
  84. </div>
  85. <edit-playlist v-if="modals.editPlaylist" sector="admin" />
  86. <report v-if="modals.report" />
  87. <edit-song v-if="modals.editSong" song-type="songs" />
  88. </div>
  89. </template>
  90. <script>
  91. import { mapState, mapActions, mapGetters } from "vuex";
  92. import Toast from "toasters";
  93. import UserIdToUsername from "@/components/UserIdToUsername.vue";
  94. import ws from "@/ws";
  95. import utils from "../../../../js/utils";
  96. export default {
  97. components: {
  98. EditPlaylist: () => import("@/components/modals/EditPlaylist.vue"),
  99. UserIdToUsername,
  100. Report: () => import("@/components/modals/Report.vue"),
  101. EditSong: () => import("@/components/modals/EditSong.vue")
  102. },
  103. data() {
  104. return {
  105. utils,
  106. playlists: []
  107. };
  108. },
  109. computed: {
  110. ...mapState("modalVisibility", {
  111. modals: state => state.modals
  112. }),
  113. ...mapGetters({
  114. socket: "websockets/getSocket"
  115. })
  116. },
  117. mounted() {
  118. if (this.socket.readyState === 1) this.init();
  119. ws.onConnect(() => this.init());
  120. },
  121. methods: {
  122. edit(playlistId) {
  123. this.editPlaylist(playlistId);
  124. this.openModal("editPlaylist");
  125. },
  126. init() {
  127. this.socket.dispatch("playlists.index", res => {
  128. if (res.status === "success") {
  129. this.playlists = res.data.playlists;
  130. // if (this.$route.query.userId) {
  131. // const user = this.users.find(
  132. // user => user._id === this.$route.query.userId
  133. // );
  134. // if (user) this.edit(user);
  135. // }
  136. }
  137. });
  138. this.socket.dispatch("apis.joinAdminRoom", "playlists", () => {});
  139. },
  140. getDateFormatted(createdAt) {
  141. const date = new Date(createdAt);
  142. const year = date.getFullYear();
  143. const month = `${date.getMonth() + 1}`.padStart(2, 0);
  144. const day = `${date.getDate()}`.padStart(2, 0);
  145. const hour = `${date.getHours()}`.padStart(2, 0);
  146. const minute = `${date.getMinutes()}`.padStart(2, 0);
  147. return `${year}-${month}-${day} ${hour}:${minute}`;
  148. },
  149. totalLengthForPlaylist(songs) {
  150. let length = 0;
  151. songs.forEach(song => {
  152. length += song.duration;
  153. });
  154. return this.utils.formatTimeLong(length);
  155. },
  156. deleteOrphanedStationPlaylists() {
  157. this.socket.dispatch(
  158. "playlists.deleteOrphanedStationPlaylists",
  159. res => {
  160. if (res.status === "success") new Toast(res.message);
  161. else new Toast(`Error: ${res.message}`);
  162. }
  163. );
  164. },
  165. deleteOrphanedGenrePlaylists() {
  166. this.socket.dispatch(
  167. "playlists.deleteOrphanedGenrePlaylists",
  168. res => {
  169. if (res.status === "success") new Toast(res.message);
  170. else new Toast(`Error: ${res.message}`);
  171. }
  172. );
  173. },
  174. requestOrphanedPlaylistSongs() {
  175. this.socket.dispatch(
  176. "playlists.requestOrphanedPlaylistSongs",
  177. res => {
  178. if (res.status === "success") new Toast(res.message);
  179. else new Toast(`Error: ${res.message}`);
  180. }
  181. );
  182. },
  183. clearAndRefillAllStationPlaylists() {
  184. this.socket.dispatch(
  185. "playlists.clearAndRefillAllStationPlaylists",
  186. res => {
  187. if (res.status === "success")
  188. new Toast({ content: res.message, timeout: 4000 });
  189. else
  190. new Toast({
  191. content: `Error: ${res.message}`,
  192. timeout: 4000
  193. });
  194. }
  195. );
  196. },
  197. clearAndRefillAllGenrePlaylists() {
  198. this.socket.dispatch(
  199. "playlists.clearAndRefillAllGenrePlaylists",
  200. res => {
  201. if (res.status === "success")
  202. new Toast({ content: res.message, timeout: 4000 });
  203. else
  204. new Toast({
  205. content: `Error: ${res.message}`,
  206. timeout: 4000
  207. });
  208. }
  209. );
  210. },
  211. ...mapActions("modalVisibility", ["openModal"]),
  212. ...mapActions("user/playlists", ["editPlaylist"])
  213. }
  214. };
  215. </script>
  216. <style lang="scss" scoped>
  217. .night-mode {
  218. .table {
  219. color: var(--light-grey-2);
  220. background-color: var(--dark-grey-3);
  221. thead tr {
  222. background: var(--dark-grey-3);
  223. td {
  224. color: var(--white);
  225. }
  226. }
  227. tbody tr:hover {
  228. background-color: var(--dark-grey-4) !important;
  229. }
  230. tbody tr:nth-child(even) {
  231. background-color: var(--dark-grey-2);
  232. }
  233. strong {
  234. color: var(--light-grey-2);
  235. }
  236. }
  237. }
  238. body {
  239. font-family: "Hind", sans-serif;
  240. }
  241. td {
  242. vertical-align: middle;
  243. }
  244. .is-primary:focus {
  245. background-color: var(--primary-color) !important;
  246. }
  247. </style>