Test.vue 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. <template>
  2. <div>
  3. <page-metadata title="Admin | Test" />
  4. <div class="admin-tab">
  5. <advanced-table
  6. :column-default="columnDefault"
  7. :columns="columns"
  8. :filters="filters"
  9. data-action="songs.getData"
  10. name="admin-test"
  11. >
  12. <template #column-thumbnailImage="slotProps">
  13. <img
  14. class="song-thumbnail"
  15. :src="slotProps.item.thumbnail"
  16. onerror="this.src='/assets/notes-transparent.png'"
  17. loading="lazy"
  18. />
  19. </template>
  20. <template #column-thumbnailUrl="slotProps">
  21. <a :href="slotProps.item.thumbnail" target="_blank">
  22. {{ slotProps.item.thumbnail }}
  23. </a>
  24. </template>
  25. <template #column-_id="slotProps">
  26. <span :title="slotProps.item._id">{{
  27. slotProps.item._id
  28. }}</span>
  29. </template>
  30. <template #column-youtubeId="slotProps">
  31. <a
  32. :href="
  33. 'https://www.youtube.com/watch?v=' +
  34. `${slotProps.item.youtubeId}`
  35. "
  36. target="_blank"
  37. >
  38. {{ slotProps.item.youtubeId }}
  39. </a>
  40. </template>
  41. <template #column-title="slotProps">
  42. <span :title="slotProps.item.title">{{
  43. slotProps.item.title
  44. }}</span>
  45. </template>
  46. <template #column-artists="slotProps">
  47. <span :title="slotProps.item.artists.join(', ')">{{
  48. slotProps.item.artists.join(", ")
  49. }}</span>
  50. </template>
  51. <template #column-genres="slotProps">
  52. <span :title="slotProps.item.genres.join(', ')">{{
  53. slotProps.item.genres.join(", ")
  54. }}</span>
  55. </template>
  56. <template #column-requestedBy="slotProps">
  57. <user-id-to-username
  58. :user-id="slotProps.item.requestedBy"
  59. :link="true"
  60. />
  61. </template>
  62. <template #bulk-actions="slotProps">
  63. <div class="song-bulk-actions">
  64. <i
  65. class="material-icons edit-songs-icon"
  66. @click.prevent="editMany(slotProps.item)"
  67. content="Edit Songs"
  68. v-tippy
  69. >
  70. edit
  71. </i>
  72. <i
  73. class="material-icons verify-songs-icon"
  74. @click.prevent="verifyMany(slotProps.item)"
  75. content="Verify Songs"
  76. v-tippy
  77. >
  78. check_circle
  79. </i>
  80. <i
  81. class="material-icons unverify-songs-icon"
  82. @click.prevent="unverifyMany(slotProps.item)"
  83. content="Unverify Songs"
  84. v-tippy
  85. >
  86. cancel
  87. </i>
  88. <i
  89. class="material-icons tag-songs-icon"
  90. @click.prevent="tagMany(slotProps.item)"
  91. content="Tag Songs"
  92. v-tippy
  93. >
  94. local_offer
  95. </i>
  96. <i
  97. class="material-icons artists-songs-icon"
  98. @click.prevent="setArtists(slotProps.item)"
  99. content="Set Artists"
  100. v-tippy
  101. >
  102. group
  103. </i>
  104. <i
  105. class="material-icons genres-songs-icon"
  106. @click.prevent="setGenres(slotProps.item)"
  107. content="Set Genres"
  108. v-tippy
  109. >
  110. theater_comedy
  111. </i>
  112. <i
  113. class="material-icons delete-songs-icon"
  114. @click.prevent="
  115. confirmAction({
  116. message:
  117. 'Removing this song will remove it from all playlists and cause a ratings recalculation.',
  118. action: 'deleteMany',
  119. params: slotProps.item
  120. })
  121. "
  122. content="Delete Songs"
  123. v-tippy
  124. >
  125. delete_forever
  126. </i>
  127. </div>
  128. </template>
  129. <!-- <template #bulk-actions-right="slotProps">
  130. </template> -->
  131. </advanced-table>
  132. </div>
  133. <edit-song v-if="modals.editSong" song-type="songs" :key="song._id" />
  134. <report v-if="modals.report" />
  135. <confirm v-if="modals.confirm" @confirmed="handleConfirmed()" />
  136. </div>
  137. </template>
  138. <script>
  139. import { mapState, mapActions } from "vuex";
  140. import { defineAsyncComponent } from "vue";
  141. import Toast from "toasters";
  142. import AdvancedTable from "@/components/AdvancedTable.vue";
  143. import UserIdToUsername from "@/components/UserIdToUsername.vue";
  144. export default {
  145. components: {
  146. AdvancedTable,
  147. UserIdToUsername,
  148. EditSong: defineAsyncComponent(() =>
  149. import("@/components/modals/EditSong")
  150. ),
  151. Report: defineAsyncComponent(() =>
  152. import("@/components/modals/Report.vue")
  153. ),
  154. Confirm: defineAsyncComponent(() =>
  155. import("@/components/modals/Confirm.vue")
  156. )
  157. },
  158. data() {
  159. return {
  160. columnDefault: {
  161. sortable: true,
  162. hidable: true,
  163. defaultVisibility: "shown",
  164. draggable: true,
  165. resizable: true,
  166. minWidth: 200,
  167. maxWidth: 600
  168. },
  169. columns: [
  170. {
  171. name: "thumbnailImage",
  172. displayName: "Thumb",
  173. properties: ["thumbnail"],
  174. sortable: false,
  175. minWidth: 75,
  176. defaultWidth: 75,
  177. maxWidth: 75,
  178. resizable: false
  179. },
  180. {
  181. name: "_id",
  182. displayName: "Musare ID",
  183. properties: ["_id"],
  184. sortProperty: "_id",
  185. minWidth: 215,
  186. defaultWidth: 215
  187. },
  188. {
  189. name: "youtubeId",
  190. displayName: "YouTube ID",
  191. properties: ["youtubeId"],
  192. sortProperty: "youtubeId",
  193. minWidth: 120,
  194. defaultWidth: 120
  195. },
  196. {
  197. name: "title",
  198. displayName: "Title",
  199. properties: ["title"],
  200. sortProperty: "title"
  201. },
  202. {
  203. name: "artists",
  204. displayName: "Artists",
  205. properties: ["artists"],
  206. sortable: false
  207. },
  208. {
  209. name: "genres",
  210. displayName: "Genres",
  211. properties: ["genres"],
  212. sortable: false
  213. },
  214. {
  215. name: "thumbnailUrl",
  216. displayName: "Thumbnail (URL)",
  217. properties: ["thumbnail"],
  218. sortProperty: "thumbnail",
  219. defaultVisibility: "hidden"
  220. },
  221. {
  222. name: "requestedBy",
  223. displayName: "Requested By",
  224. properties: ["requestedBy"],
  225. sortProperty: "requestedBy",
  226. defaultWidth: 200
  227. }
  228. ],
  229. filters: [
  230. {
  231. name: "_id",
  232. displayName: "Musare ID",
  233. property: "_id",
  234. filterTypes: ["exact"],
  235. defaultFilterType: "exact"
  236. },
  237. {
  238. name: "youtubeId",
  239. displayName: "YouTube ID",
  240. property: "youtubeId",
  241. filterTypes: ["contains", "exact", "regex"],
  242. defaultFilterType: "contains"
  243. },
  244. {
  245. name: "title",
  246. displayName: "Title",
  247. property: "title",
  248. filterTypes: ["contains", "exact", "regex"],
  249. defaultFilterType: "contains"
  250. },
  251. {
  252. name: "artists",
  253. displayName: "Artists",
  254. property: "artists",
  255. filterTypes: ["contains", "exact", "regex"],
  256. defaultFilterType: "contains"
  257. },
  258. {
  259. name: "genres",
  260. displayName: "Genres",
  261. property: "genres",
  262. filterTypes: ["contains", "exact", "regex"],
  263. defaultFilterType: "contains"
  264. },
  265. {
  266. name: "thumbnail",
  267. displayName: "Thumbnail",
  268. property: "thumbnail",
  269. filterTypes: ["contains", "exact", "regex"],
  270. defaultFilterType: "contains"
  271. },
  272. {
  273. name: "requestedBy",
  274. displayName: "Requested By",
  275. property: "requestedBy",
  276. filterTypes: ["contains", "exact", "regex"],
  277. defaultFilterType: "contains"
  278. }
  279. ],
  280. confirm: {
  281. message: "",
  282. action: "",
  283. params: null
  284. }
  285. };
  286. },
  287. computed: {
  288. ...mapState("modalVisibility", {
  289. modals: state => state.modals
  290. }),
  291. ...mapState("modals/editSong", {
  292. song: state => state.song
  293. })
  294. },
  295. mounted() {},
  296. beforeUnmount() {},
  297. methods: {
  298. editMany(selectedRows) {
  299. if (selectedRows.length === 1) {
  300. this.editSong(selectedRows[0]);
  301. this.openModal("editSong");
  302. } else {
  303. new Toast("Bulk editing not yet implemented.");
  304. }
  305. },
  306. verifyMany() {
  307. new Toast("Bulk verifying not yet implemented.");
  308. },
  309. unverifyMany() {
  310. new Toast("Bulk unverifying not yet implemented.");
  311. },
  312. tagMany() {
  313. new Toast("Bulk tagging not yet implemented.");
  314. },
  315. setArtists() {
  316. new Toast("Bulk setting artists not yet implemented.");
  317. },
  318. setGenres() {
  319. new Toast("Bulk setting genres not yet implemented.");
  320. },
  321. deleteMany() {
  322. new Toast("Bulk deleting not yet implemented.");
  323. },
  324. confirmAction(confirm) {
  325. this.confirm = confirm;
  326. this.updateConfirmMessage(confirm.message);
  327. this.openModal("confirm");
  328. },
  329. handleConfirmed() {
  330. const { action, params } = this.confirm;
  331. if (typeof this[action] === "function") {
  332. if (params) this[action](params);
  333. else this[action]();
  334. }
  335. this.confirm = {
  336. message: "",
  337. action: "",
  338. params: null
  339. };
  340. },
  341. ...mapActions("modals/editSong", ["editSong"]),
  342. ...mapActions("modals/confirm", ["updateConfirmMessage"]),
  343. ...mapActions("modalVisibility", ["openModal"])
  344. }
  345. };
  346. </script>
  347. <style lang="scss" scoped>
  348. .song-thumbnail {
  349. display: block;
  350. max-width: 50px;
  351. margin: 0 auto;
  352. }
  353. .bulk-popup {
  354. .song-bulk-actions {
  355. display: flex;
  356. flex-direction: row;
  357. width: 100%;
  358. justify-content: space-evenly;
  359. .material-icons {
  360. position: relative;
  361. top: 6px;
  362. margin-left: 5px;
  363. cursor: pointer;
  364. color: var(--primary-color);
  365. &:hover,
  366. &:focus {
  367. filter: brightness(90%);
  368. }
  369. }
  370. .verify-songs-icon {
  371. color: var(--green);
  372. }
  373. .unverify-songs-icon,
  374. .delete-songs-icon {
  375. color: var(--dark-red);
  376. }
  377. }
  378. }
  379. </style>