Report.vue 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. <template>
  2. <modal title="Report">
  3. <div slot="body">
  4. <div class="columns song-types">
  5. <div
  6. v-if="previousSong !== null && localSong === null"
  7. class="column song-type"
  8. >
  9. <div
  10. class="card is-fullwidth"
  11. :class="{ 'is-highlight-active': isPreviousSongActive }"
  12. @click="highlight('previousSong')"
  13. >
  14. <header class="card-header">
  15. <p class="card-header-title">Previous Song</p>
  16. </header>
  17. <div class="card-content">
  18. <article class="media">
  19. <figure class="media-left">
  20. <song-thumbnail
  21. class="image is-64x64"
  22. :song="previousSong"
  23. />
  24. </figure>
  25. <div class="media-content">
  26. <div class="content">
  27. <p>
  28. <strong>{{
  29. previousSong.title
  30. }}</strong>
  31. <br />
  32. <small>{{
  33. previousSong.artists.join(", ")
  34. }}</small>
  35. </p>
  36. </div>
  37. </div>
  38. </article>
  39. </div>
  40. <a
  41. href="#"
  42. class="absolute-a"
  43. @click="highlight('previousSong')"
  44. />
  45. </div>
  46. </div>
  47. <div
  48. v-if="currentSong !== {} && localSong === null"
  49. class="column song-type"
  50. >
  51. <div
  52. class="card is-fullwidth"
  53. :class="{ 'is-highlight-active': isCurrentSongActive }"
  54. @click="highlight('currentSong')"
  55. >
  56. <header class="card-header">
  57. <p class="card-header-title">Current Song</p>
  58. </header>
  59. <div class="card-content">
  60. <article class="media">
  61. <figure class="media-left">
  62. <song-thumbnail
  63. class="image is-64x64"
  64. :song="currentSong"
  65. />
  66. </figure>
  67. <div class="media-content">
  68. <div class="content">
  69. <p>
  70. <strong>{{
  71. currentSong.title
  72. }}</strong>
  73. <br />
  74. <small>{{
  75. currentSong.artists.join(", ")
  76. }}</small>
  77. </p>
  78. </div>
  79. </div>
  80. </article>
  81. </div>
  82. <a
  83. href="#"
  84. class="absolute-a"
  85. @click="highlight('currentSong')"
  86. />
  87. </div>
  88. </div>
  89. <div v-if="localSong !== null" class="column song-type">
  90. <div class="card is-fullwidth">
  91. <header class="card-header">
  92. <p class="card-header-title">Song</p>
  93. </header>
  94. <div class="card-content">
  95. <article class="media">
  96. <figure class="media-left">
  97. <song-thumbnail
  98. class="image is-64x64"
  99. :song="localSong"
  100. />
  101. </figure>
  102. <div class="media-content">
  103. <div class="content">
  104. <p>
  105. <strong>{{
  106. localSong.title
  107. }}</strong>
  108. <br />
  109. <small>{{
  110. localSong.artists.join(", ")
  111. }}</small>
  112. </p>
  113. </div>
  114. </div>
  115. </article>
  116. </div>
  117. </div>
  118. </div>
  119. </div>
  120. <div class="edit-report-wrapper">
  121. <div class="columns is-multiline">
  122. <div
  123. v-for="(issue, issueIndex) in issues"
  124. class="column is-half"
  125. :key="issueIndex"
  126. >
  127. <label class="label">{{ issue.name }}</label>
  128. <p
  129. v-for="(reason, reasonIndex) in issue.reasons"
  130. class="control"
  131. :key="reasonIndex"
  132. >
  133. <label class="checkbox">
  134. <input
  135. type="checkbox"
  136. @click="toggleIssue(issue.name, reason)"
  137. />
  138. {{ reason }}
  139. </label>
  140. </p>
  141. </div>
  142. <div class="column">
  143. <label class="label">Other</label>
  144. <textarea
  145. v-model="report.description"
  146. class="textarea"
  147. maxlength="400"
  148. placeholder="Any other details..."
  149. />
  150. <div class="textarea-counter">
  151. {{ charactersRemaining }}
  152. </div>
  153. </div>
  154. </div>
  155. </div>
  156. </div>
  157. <div slot="footer">
  158. <a class="button is-success" @click="create()" href="#">
  159. <i class="material-icons save-changes">done</i>
  160. <span>&nbsp;Create</span>
  161. </a>
  162. <a
  163. class="button is-danger"
  164. href="#"
  165. @click="
  166. closeModal({
  167. sector: 'station',
  168. modal: 'report'
  169. })
  170. "
  171. >
  172. <span>&nbsp;Cancel</span>
  173. </a>
  174. </div>
  175. </modal>
  176. </template>
  177. <script>
  178. import { mapState, mapGetters, mapActions } from "vuex";
  179. import Toast from "toasters";
  180. import Modal from "../Modal.vue";
  181. import SongThumbnail from "../SongThumbnail.vue";
  182. export default {
  183. components: { Modal, SongThumbnail },
  184. data() {
  185. return {
  186. isPreviousSongActive: false,
  187. isCurrentSongActive: true,
  188. localSong: null,
  189. report: {
  190. resolved: false,
  191. songId: "",
  192. description: "",
  193. issues: [
  194. { name: "Video", reasons: [] },
  195. { name: "Title", reasons: [] },
  196. { name: "Duration", reasons: [] },
  197. { name: "Artists", reasons: [] },
  198. { name: "Thumbnail", reasons: [] }
  199. ]
  200. },
  201. issues: [
  202. {
  203. name: "Video",
  204. reasons: [
  205. "Doesn't exist",
  206. "It's private",
  207. "It's not available in my country",
  208. "Unofficial"
  209. ]
  210. },
  211. {
  212. name: "Title",
  213. reasons: ["Incorrect", "Inappropriate"]
  214. },
  215. {
  216. name: "Duration",
  217. reasons: [
  218. "Skips too soon",
  219. "Skips too late",
  220. "Starts too soon",
  221. "Starts too late"
  222. ]
  223. },
  224. {
  225. name: "Artists",
  226. reasons: ["Incorrect", "Inappropriate"]
  227. },
  228. {
  229. name: "Thumbnail",
  230. reasons: ["Incorrect", "Inappropriate", "Doesn't exist"]
  231. }
  232. ]
  233. };
  234. },
  235. computed: {
  236. charactersRemaining() {
  237. return 400 - this.report.description.length;
  238. },
  239. ...mapState({
  240. currentSong: state => state.station.currentSong,
  241. previousSong: state => state.station.previousSong,
  242. song: state => state.modals.report.song
  243. }),
  244. ...mapGetters({
  245. socket: "websockets/getSocket"
  246. })
  247. },
  248. mounted() {
  249. this.report.songId = this.currentSong.songId;
  250. if (this.song !== null) {
  251. this.localSong = this.song;
  252. this.report.songId = this.song.songId;
  253. this.reportSong(null);
  254. }
  255. },
  256. methods: {
  257. create() {
  258. this.socket.dispatch("reports.create", this.report, res => {
  259. new Toast(res.message);
  260. if (res.status === "success")
  261. this.closeModal({
  262. sector: "station",
  263. modal: "report"
  264. });
  265. });
  266. },
  267. highlight(type) {
  268. if (type === "currentSong") {
  269. this.report.songId = this.currentSong.songId;
  270. this.isPreviousSongActive = false;
  271. this.isCurrentSongActive = true;
  272. } else if (type === "previousSong") {
  273. this.report.songId = this.previousSong.songId;
  274. this.isCurrentSongActive = false;
  275. this.isPreviousSongActive = true;
  276. }
  277. },
  278. toggleIssue(name, reason) {
  279. for (let z = 0; z < this.report.issues.length; z += 1) {
  280. if (this.report.issues[z].name === name) {
  281. if (this.report.issues[z].reasons.indexOf(reason) > -1) {
  282. this.report.issues[z].reasons.splice(
  283. this.report.issues[z].reasons.indexOf(reason),
  284. 1
  285. );
  286. } else this.report.issues[z].reasons.push(reason);
  287. }
  288. }
  289. },
  290. ...mapActions("modals/report", ["reportSong"]),
  291. ...mapActions("modalVisibility", ["closeModal"])
  292. }
  293. };
  294. </script>
  295. <style lang="scss" scoped>
  296. h6 {
  297. margin-bottom: 15px;
  298. }
  299. .song-type:first-of-type {
  300. padding-left: 0;
  301. }
  302. .song-type:last-of-type {
  303. padding-right: 0;
  304. }
  305. .thumbnail.image.is-64x64 {
  306. min-width: 64px;
  307. margin: 0;
  308. }
  309. .media-content {
  310. display: flex;
  311. align-items: center;
  312. height: 64px;
  313. }
  314. .radio-controls .control {
  315. display: flex;
  316. align-items: center;
  317. }
  318. .textarea-counter {
  319. text-align: right;
  320. }
  321. @media screen and (min-width: 769px) {
  322. .radio-controls .control-label {
  323. padding-top: 0 !important;
  324. }
  325. }
  326. .edit-report-wrapper {
  327. padding: 20px;
  328. }
  329. .is-highlight-active {
  330. border: 3px var(--primary-color) solid;
  331. }
  332. </style>