Playlists.vue 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. <template>
  2. <div>
  3. <page-metadata title="Admin | Playlists" />
  4. <div class="admin-tab">
  5. <div class="button-row">
  6. <run-job-dropdown :jobs="jobs" />
  7. </div>
  8. <advanced-table
  9. :column-default="columnDefault"
  10. :columns="columns"
  11. :filters="filters"
  12. data-action="playlists.getData"
  13. name="admin-playlists"
  14. :events="events"
  15. >
  16. <template #column-options="slotProps">
  17. <div class="row-options">
  18. <button
  19. class="
  20. button
  21. is-primary
  22. icon-with-button
  23. material-icons
  24. "
  25. @click="edit(slotProps.item._id)"
  26. :disabled="slotProps.item.removed"
  27. content="Edit Playlist"
  28. v-tippy
  29. >
  30. edit
  31. </button>
  32. </div>
  33. </template>
  34. <template #column-displayName="slotProps">
  35. <span :title="slotProps.item.displayName">{{
  36. slotProps.item.displayName
  37. }}</span>
  38. </template>
  39. <template #column-type="slotProps">
  40. <span :title="slotProps.item.type">{{
  41. slotProps.item.type
  42. }}</span>
  43. </template>
  44. <template #column-privacy="slotProps">
  45. <span :title="slotProps.item.privacy">{{
  46. slotProps.item.privacy
  47. }}</span>
  48. </template>
  49. <template #column-songsCount="slotProps">
  50. <span :title="slotProps.item.songsCount">{{
  51. slotProps.item.songsCount
  52. }}</span>
  53. </template>
  54. <template #column-totalLength="slotProps">
  55. <span :title="formatTimeLong(slotProps.item.totalLength)">{{
  56. formatTimeLong(slotProps.item.totalLength)
  57. }}</span>
  58. </template>
  59. <template #column-createdBy="slotProps">
  60. <span v-if="slotProps.item.createdBy === 'Musare'"
  61. >Musare</span
  62. >
  63. <user-id-to-username
  64. v-else
  65. :user-id="slotProps.item.createdBy"
  66. :link="true"
  67. />
  68. </template>
  69. <template #column-createdAt="slotProps">
  70. <span :title="new Date(slotProps.item.createdAt)">{{
  71. getDateFormatted(slotProps.item.createdAt)
  72. }}</span>
  73. </template>
  74. <template #column-createdFor="slotProps">
  75. <span :title="slotProps.item.createdFor">{{
  76. slotProps.item.createdFor
  77. }}</span>
  78. </template>
  79. <template #column-_id="slotProps">
  80. <span :title="slotProps.item._id">{{
  81. slotProps.item._id
  82. }}</span>
  83. </template>
  84. </advanced-table>
  85. </div>
  86. <edit-playlist v-if="modals.editPlaylist" sector="admin" />
  87. <edit-song v-if="modals.editSong" song-type="songs" />
  88. <report v-if="modals.report" />
  89. </div>
  90. </template>
  91. <script>
  92. import { mapState, mapActions } from "vuex";
  93. import { defineAsyncComponent } from "vue";
  94. import AdvancedTable from "@/components/AdvancedTable.vue";
  95. import RunJobDropdown from "@/components/RunJobDropdown.vue";
  96. import UserIdToUsername from "@/components/UserIdToUsername.vue";
  97. import utils from "../../../../js/utils";
  98. export default {
  99. components: {
  100. EditPlaylist: defineAsyncComponent(() =>
  101. import("@/components/modals/EditPlaylist")
  102. ),
  103. Report: defineAsyncComponent(() =>
  104. import("@/components/modals/Report.vue")
  105. ),
  106. EditSong: defineAsyncComponent(() =>
  107. import("@/components/modals/EditSong")
  108. ),
  109. AdvancedTable,
  110. RunJobDropdown,
  111. UserIdToUsername
  112. },
  113. data() {
  114. return {
  115. utils,
  116. columnDefault: {
  117. sortable: true,
  118. hidable: true,
  119. defaultVisibility: "shown",
  120. draggable: true,
  121. resizable: true,
  122. minWidth: 150,
  123. maxWidth: 600
  124. },
  125. columns: [
  126. {
  127. name: "options",
  128. displayName: "Options",
  129. properties: ["_id"],
  130. sortable: false,
  131. hidable: false,
  132. resizable: false,
  133. minWidth: 76,
  134. defaultWidth: 76
  135. },
  136. {
  137. name: "displayName",
  138. displayName: "Display Name",
  139. properties: ["displayName"],
  140. sortProperty: "displayName"
  141. },
  142. {
  143. name: "type",
  144. displayName: "Type",
  145. properties: ["type"],
  146. sortProperty: "type"
  147. },
  148. {
  149. name: "privacy",
  150. displayName: "Privacy",
  151. properties: ["privacy"],
  152. sortProperty: "privacy"
  153. },
  154. {
  155. name: "songsCount",
  156. displayName: "Songs #",
  157. properties: ["songsCount"],
  158. sortProperty: "songsCount",
  159. minWidth: 100,
  160. defaultWidth: 100
  161. },
  162. {
  163. name: "totalLength",
  164. displayName: "Total Length",
  165. properties: ["totalLength"],
  166. sortProperty: "totalLength",
  167. minWidth: 250,
  168. defaultWidth: 250
  169. },
  170. {
  171. name: "createdBy",
  172. displayName: "Created By",
  173. properties: ["createdBy"],
  174. sortProperty: "createdBy",
  175. defaultWidth: 150
  176. },
  177. {
  178. name: "createdAt",
  179. displayName: "Created At",
  180. properties: ["createdAt"],
  181. sortProperty: "createdAt",
  182. defaultWidth: 150
  183. },
  184. {
  185. name: "createdFor",
  186. displayName: "Created For",
  187. properties: ["createdFor"],
  188. sortProperty: "createdFor",
  189. minWidth: 230,
  190. defaultWidth: 230
  191. },
  192. {
  193. name: "_id",
  194. displayName: "Playlist ID",
  195. properties: ["_id"],
  196. sortProperty: "_id",
  197. minWidth: 230,
  198. defaultWidth: 230
  199. }
  200. ],
  201. filters: [
  202. {
  203. name: "_id",
  204. displayName: "Playlist ID",
  205. property: "_id",
  206. filterTypes: ["exact"],
  207. defaultFilterType: "exact"
  208. },
  209. {
  210. name: "displayName",
  211. displayName: "Display Name",
  212. property: "displayName",
  213. filterTypes: ["contains", "exact", "regex"],
  214. defaultFilterType: "contains"
  215. },
  216. {
  217. name: "type",
  218. displayName: "Type",
  219. property: "type",
  220. filterTypes: ["exact"],
  221. defaultFilterType: "exact",
  222. dropdown: [
  223. ["genre", "Genre"],
  224. ["station", "Station"],
  225. ["user", "User"],
  226. ["user-disliked", "User Disliked"],
  227. ["user-liked", "User Liked"]
  228. ]
  229. },
  230. {
  231. name: "privacy",
  232. displayName: "Privacy",
  233. property: "privacy",
  234. filterTypes: ["exact"],
  235. defaultFilterType: "exact",
  236. dropdown: [
  237. ["public", "Public"],
  238. ["private", "Private"]
  239. ]
  240. },
  241. {
  242. name: "songsCount",
  243. displayName: "Songs Count",
  244. property: "songsCount",
  245. filterTypes: [
  246. "numberLesserEqual",
  247. "numberLesser",
  248. "numberGreater",
  249. "numberGreaterEqual",
  250. "numberEquals"
  251. ],
  252. defaultFilterType: "numberLesser"
  253. },
  254. {
  255. name: "totalLength",
  256. displayName: "Total Length",
  257. property: "totalLength",
  258. filterTypes: [
  259. "numberLesserEqual",
  260. "numberLesser",
  261. "numberGreater",
  262. "numberGreaterEqual",
  263. "numberEquals"
  264. ],
  265. defaultFilterType: "numberLesser"
  266. },
  267. {
  268. name: "createdBy",
  269. displayName: "Created By",
  270. property: "createdBy",
  271. filterTypes: ["contains", "exact", "regex"],
  272. defaultFilterType: "contains"
  273. },
  274. {
  275. name: "createdAt",
  276. displayName: "Created At",
  277. property: "createdAt",
  278. filterTypes: ["datetimeBefore", "datetimeAfter"],
  279. defaultFilterType: "datetimeBefore"
  280. },
  281. {
  282. name: "createdFor",
  283. displayName: "Created For",
  284. property: "createdFor",
  285. filterTypes: ["contains", "exact", "regex"],
  286. defaultFilterType: "contains"
  287. }
  288. ],
  289. events: {
  290. adminRoom: "playlists",
  291. updated: {
  292. event: "admin.playlist.updated",
  293. id: "playlist._id",
  294. item: "playlist"
  295. },
  296. removed: {
  297. event: "admin.playlist.deleted",
  298. id: "playlistId"
  299. }
  300. },
  301. jobs: [
  302. {
  303. name: "Delete orphaned station playlists",
  304. socket: "playlists.deleteOrphanedStationPlaylists"
  305. },
  306. {
  307. name: "Delete orphaned genre playlists",
  308. socket: "playlists.deleteOrphanedGenrePlaylists"
  309. },
  310. {
  311. name: "Request orphaned playlist songs",
  312. socket: "playlists.requestOrphanedPlaylistSongs"
  313. },
  314. {
  315. name: "Clear and refill all station playlists",
  316. socket: "playlists.clearAndRefillAllStationPlaylists"
  317. },
  318. {
  319. name: "Clear and refill all genre playlists",
  320. socket: "playlists.clearAndRefillAllGenrePlaylists"
  321. },
  322. {
  323. name: "Create missing genre playlists",
  324. socket: "playlists.createMissingGenrePlaylists"
  325. }
  326. ]
  327. };
  328. },
  329. computed: {
  330. ...mapState("modalVisibility", {
  331. modals: state => state.modals
  332. })
  333. },
  334. methods: {
  335. edit(playlistId) {
  336. this.editPlaylist(playlistId);
  337. this.openModal("editPlaylist");
  338. },
  339. getDateFormatted(createdAt) {
  340. const date = new Date(createdAt);
  341. const year = date.getFullYear();
  342. const month = `${date.getMonth() + 1}`.padStart(2, 0);
  343. const day = `${date.getDate()}`.padStart(2, 0);
  344. const hour = `${date.getHours()}`.padStart(2, 0);
  345. const minute = `${date.getMinutes()}`.padStart(2, 0);
  346. return `${year}-${month}-${day} ${hour}:${minute}`;
  347. },
  348. formatTimeLong(length) {
  349. return this.utils.formatTimeLong(length);
  350. },
  351. ...mapActions("modalVisibility", ["openModal"]),
  352. ...mapActions("user/playlists", ["editPlaylist"])
  353. }
  354. };
  355. </script>