Report.vue 6.6 KB

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