ViewReport.vue 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. <template>
  2. <modal title="View Report">
  3. <template #body v-if="report && report._id">
  4. <div class="report-item">
  5. <div class="report-item-header">
  6. <div class="report-item-info">
  7. <div class="report-item-icon">
  8. <profile-picture
  9. :avatar="report.createdBy.avatar"
  10. :name="
  11. report.createdBy.name
  12. ? report.createdBy.name
  13. : report.createdBy.username
  14. "
  15. />
  16. </div>
  17. <div class="report-item-summary">
  18. <p class="report-item-summary-title">
  19. Reported by
  20. <router-link
  21. :to="{
  22. path: `/u/${report.createdBy.username}`
  23. }"
  24. :title="report.createdBy._id"
  25. >
  26. {{ report.createdBy.username }}
  27. </router-link>
  28. </p>
  29. <p class="report-item-summary-description">
  30. {{
  31. formatDistance(
  32. new Date(report.createdAt),
  33. new Date(),
  34. {
  35. addSuffix: true
  36. }
  37. )
  38. }}
  39. / YouTube:
  40. <a
  41. :href="
  42. 'https://www.youtube.com/watch?v=' +
  43. `${report.song.youtubeId}`
  44. "
  45. target="_blank"
  46. >
  47. {{ report.song.youtubeId }}</a
  48. >
  49. / Song ID: {{ report.song._id }}
  50. </p>
  51. </div>
  52. </div>
  53. </div>
  54. <div class="report-sub-items">
  55. <div
  56. class="report-sub-item report-sub-item-unresolved"
  57. :class="[
  58. 'report',
  59. issue.resolved
  60. ? 'report-sub-item-resolved'
  61. : 'report-sub-item-unresolved'
  62. ]"
  63. v-for="(issue, issueIndex) in report.issues"
  64. :key="issueIndex"
  65. >
  66. <i
  67. class="material-icons duration-icon report-sub-item-left-icon"
  68. :content="issue.category"
  69. v-tippy
  70. >
  71. {{ icons[issue.category] }}
  72. </i>
  73. <p class="report-sub-item-info">
  74. <span class="report-sub-item-title">
  75. {{ issue.title }}
  76. </span>
  77. <span
  78. class="report-sub-item-description"
  79. v-if="issue.description"
  80. >
  81. {{ issue.description }}
  82. </span>
  83. </p>
  84. <div
  85. class="report-sub-item-actions universal-item-actions"
  86. >
  87. <i
  88. class="material-icons resolve-icon"
  89. content="Resolve"
  90. v-tippy
  91. v-if="!issue.resolved"
  92. @click="toggleIssue(report._id, issue._id)"
  93. >
  94. done
  95. </i>
  96. <i
  97. class="material-icons unresolve-icon"
  98. content="Unresolve"
  99. v-tippy
  100. v-else
  101. @click="toggleIssue(report._id, issue._id)"
  102. >
  103. remove
  104. </i>
  105. </div>
  106. </div>
  107. </div>
  108. </div>
  109. </template>
  110. <template #footer v-if="report && report._id">
  111. <a class="button is-primary" @click="openSong()">
  112. <i
  113. class="material-icons icon-with-button"
  114. content="Edit Song"
  115. v-tippy
  116. >
  117. edit
  118. </i>
  119. Edit Song
  120. </a>
  121. <a class="button is-success" href="#" @click="resolve(report._id)">
  122. <i
  123. class="material-icons icon-with-button"
  124. content="Resolve"
  125. v-tippy
  126. >
  127. done_all
  128. </i>
  129. Resolve
  130. </a>
  131. </template>
  132. </modal>
  133. </template>
  134. <script>
  135. import { mapActions, mapGetters } from "vuex";
  136. import { formatDistance } from "date-fns";
  137. import Toast from "toasters";
  138. import ProfilePicture from "@/components/ProfilePicture.vue";
  139. import Modal from "../Modal.vue";
  140. export default {
  141. components: { Modal, ProfilePicture },
  142. props: {
  143. reportId: { type: String, default: "" },
  144. sector: { type: String, default: "admin" }
  145. },
  146. data() {
  147. return {
  148. icons: {
  149. duration: "timer",
  150. video: "tv",
  151. thumbnail: "image",
  152. artists: "record_voice_over",
  153. title: "title",
  154. custom: "lightbulb"
  155. },
  156. report: {}
  157. };
  158. },
  159. computed: {
  160. ...mapGetters({
  161. socket: "websockets/getSocket"
  162. })
  163. },
  164. mounted() {
  165. this.socket.dispatch("reports.findOne", this.reportId, res => {
  166. if (res.status === "success") {
  167. const { report } = res.data;
  168. this.socket.dispatch(
  169. "apis.joinRoom",
  170. `view-report.${report._id}`
  171. );
  172. this.report = report;
  173. } else {
  174. new Toast("Report with that ID not found");
  175. this.closeModal("viewReport");
  176. }
  177. });
  178. this.socket.on(
  179. "event:admin.report.resolved",
  180. () => this.closeModal("viewReport"),
  181. { modal: "viewReport" }
  182. );
  183. this.socket.on(
  184. "event:admin.report.issue.toggled",
  185. res => {
  186. if (this.report._id === res.data.reportId) {
  187. const issue = this.report.issues.find(
  188. issue => issue._id.toString() === res.data.issueId
  189. );
  190. issue.resolved = !issue.resolved;
  191. }
  192. },
  193. { modal: "viewReport" }
  194. );
  195. },
  196. beforeUnmount() {
  197. this.socket.dispatch("apis.leaveRoom", `view-report.${this.reportId}`);
  198. },
  199. methods: {
  200. formatDistance,
  201. resolve(reportId) {
  202. return this.resolveReport(reportId)
  203. .then(res => {
  204. if (res.status === "success") this.closeModal("viewReport");
  205. })
  206. .catch(err => new Toast(err.message));
  207. },
  208. toggleIssue(reportId, issueId) {
  209. this.socket.dispatch(
  210. "reports.toggleIssue",
  211. reportId,
  212. issueId,
  213. res => {
  214. if (res.status !== "success") new Toast(res.message);
  215. }
  216. );
  217. },
  218. openSong() {
  219. this.editSong({ _id: this.report.song._id });
  220. this.openModal("editSong");
  221. },
  222. ...mapActions("admin/reports", ["indexReports", "resolveReport"]),
  223. ...mapActions("modals/editSong", ["editSong"]),
  224. ...mapActions("modalVisibility", ["closeModal", "openModal"])
  225. }
  226. };
  227. </script>
  228. <style lang="scss" scoped>
  229. .night-mode {
  230. .report-item {
  231. background-color: var(--dark-grey-2) !important;
  232. }
  233. .report-item-header {
  234. background-color: var(--dark-grey-3) !important;
  235. }
  236. }
  237. .report-item {
  238. background-color: var(--white);
  239. border: 0.5px solid var(--primary-color);
  240. border-radius: 5px;
  241. padding: 8px;
  242. &:not(:first-of-type) {
  243. margin-bottom: 16px;
  244. }
  245. .report-item-header {
  246. display: flex;
  247. align-items: center;
  248. justify-content: space-between;
  249. margin-bottom: 8px;
  250. background-color: var(--light-grey);
  251. // padding: 5px;
  252. padding: 10px;
  253. border-radius: 5px;
  254. .report-item-info {
  255. display: flex;
  256. align-items: center;
  257. .report-item-icon {
  258. display: flex;
  259. align-items: center;
  260. .profile-picture,
  261. i {
  262. margin-right: 10px;
  263. width: 45px;
  264. height: 45px;
  265. }
  266. i {
  267. font-size: 30px;
  268. display: flex;
  269. align-items: center;
  270. justify-content: center;
  271. }
  272. }
  273. .report-item-summary {
  274. .report-item-summary-title {
  275. // font-size: 14px;
  276. font-size: 16px;
  277. text-transform: capitalize;
  278. }
  279. .report-item-summary-description {
  280. text-transform: capitalize;
  281. // font-size: 12px;
  282. font-size: 14px;
  283. }
  284. }
  285. }
  286. .report-item-actions {
  287. height: 24px;
  288. margin-right: 4px;
  289. }
  290. }
  291. .report-sub-items {
  292. .report-sub-item {
  293. border: 0.5px solid var(--black);
  294. margin-top: -1px;
  295. line-height: 24px;
  296. display: flex;
  297. // padding: 4px;
  298. padding: 8px;
  299. display: flex;
  300. &:first-child {
  301. border-radius: 3px 3px 0 0;
  302. }
  303. &:last-child {
  304. border-radius: 0 0 3px 3px;
  305. }
  306. &.report-sub-item-resolved {
  307. .report-sub-item-description,
  308. .report-sub-item-title {
  309. text-decoration: line-through;
  310. }
  311. }
  312. .report-sub-item-left-icon {
  313. margin-right: 8px;
  314. margin-top: auto;
  315. margin-bottom: auto;
  316. }
  317. .report-sub-item-info {
  318. flex: 1;
  319. display: flex;
  320. flex-direction: column;
  321. .report-sub-item-title {
  322. // font-size: 14px;
  323. font-size: 16px;
  324. }
  325. .report-sub-item-description {
  326. // font-size: 12px;
  327. font-size: 14px;
  328. line-height: 16px;
  329. }
  330. }
  331. .report-sub-item-actions {
  332. height: 24px;
  333. margin-left: 8px;
  334. margin-top: auto;
  335. margin-bottom: auto;
  336. }
  337. }
  338. }
  339. .resolve-icon {
  340. color: var(--green);
  341. cursor: pointer;
  342. }
  343. .unresolve-icon {
  344. color: var(--red);
  345. cursor: pointer;
  346. }
  347. }
  348. </style>