Reports.vue 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. <script setup lang="ts">
  2. import { defineAsyncComponent, ref } from "vue";
  3. import Toast from "toasters";
  4. import { useModalsStore } from "@/stores/modals";
  5. import { useUserAuthStore } from "@/stores/userAuth";
  6. import { useReports } from "@/composables/useReports";
  7. import { TableColumn, TableFilter, TableEvents } from "@/types/advancedTable";
  8. import utils from "@/utils";
  9. const AdvancedTable = defineAsyncComponent(
  10. () => import("@/components/AdvancedTable.vue")
  11. );
  12. const UserLink = defineAsyncComponent(
  13. () => import("@/components/UserLink.vue")
  14. );
  15. const columnDefault = ref<TableColumn>({
  16. sortable: true,
  17. hidable: true,
  18. defaultVisibility: "shown",
  19. draggable: true,
  20. resizable: true,
  21. minWidth: 150,
  22. maxWidth: 600
  23. });
  24. const columns = ref<TableColumn[]>([
  25. {
  26. name: "options",
  27. displayName: "Options",
  28. properties: ["_id", "resolved"],
  29. sortable: false,
  30. hidable: false,
  31. resizable: false,
  32. minWidth: 85,
  33. defaultWidth: 85
  34. },
  35. {
  36. name: "_id",
  37. displayName: "Report ID",
  38. properties: ["_id"],
  39. sortProperty: "_id",
  40. minWidth: 215,
  41. defaultWidth: 215
  42. },
  43. {
  44. name: "songId",
  45. displayName: "Song ID",
  46. properties: ["song"],
  47. sortProperty: "song._id",
  48. minWidth: 215,
  49. defaultWidth: 215
  50. },
  51. {
  52. name: "songYoutubeId",
  53. displayName: "Song media source",
  54. properties: ["song"],
  55. sortProperty: "song.mediaSource",
  56. minWidth: 165,
  57. defaultWidth: 165
  58. },
  59. {
  60. name: "resolved",
  61. displayName: "Resolved",
  62. properties: ["resolved"],
  63. sortProperty: "resolved"
  64. },
  65. {
  66. name: "categories",
  67. displayName: "Categories",
  68. properties: ["issues"],
  69. sortable: false
  70. },
  71. {
  72. name: "createdBy",
  73. displayName: "Created By",
  74. properties: ["createdBy"],
  75. sortProperty: "createdBy",
  76. defaultWidth: 150
  77. },
  78. {
  79. name: "createdAt",
  80. displayName: "Created At",
  81. properties: ["createdAt"],
  82. sortProperty: "createdAt",
  83. defaultWidth: 150
  84. }
  85. ]);
  86. const filters = ref<TableFilter[]>([
  87. {
  88. name: "_id",
  89. displayName: "Report ID",
  90. property: "_id",
  91. filterTypes: ["exact"],
  92. defaultFilterType: "exact"
  93. },
  94. {
  95. name: "songId",
  96. displayName: "Song ID",
  97. property: "song._id",
  98. filterTypes: ["exact"],
  99. defaultFilterType: "exact"
  100. },
  101. {
  102. name: "songYoutubeId",
  103. displayName: "Song media source",
  104. property: "song.mediaSource",
  105. filterTypes: ["contains", "exact", "regex"],
  106. defaultFilterType: "contains"
  107. },
  108. {
  109. name: "resolved",
  110. displayName: "Resolved",
  111. property: "resolved",
  112. filterTypes: ["boolean"],
  113. defaultFilterType: "boolean"
  114. },
  115. {
  116. name: "categories",
  117. displayName: "Categories",
  118. property: "issues.category",
  119. filterTypes: ["contains", "exact", "regex"],
  120. defaultFilterType: "contains"
  121. },
  122. {
  123. name: "createdBy",
  124. displayName: "Created By",
  125. property: "createdBy",
  126. filterTypes: ["contains", "exact", "regex"],
  127. defaultFilterType: "contains"
  128. },
  129. {
  130. name: "createdAt",
  131. displayName: "Created At",
  132. property: "createdAt",
  133. filterTypes: ["datetimeBefore", "datetimeAfter"],
  134. defaultFilterType: "datetimeBefore"
  135. }
  136. ]);
  137. const events = ref<TableEvents>({
  138. adminRoom: "reports",
  139. updated: {
  140. event: "admin.report.updated",
  141. id: "report._id",
  142. item: "report"
  143. },
  144. removed: {
  145. event: "admin.report.removed",
  146. id: "reportId"
  147. }
  148. });
  149. const { openModal } = useModalsStore();
  150. const { resolveReport } = useReports();
  151. const userAuthStore = useUserAuthStore();
  152. const { hasPermission } = userAuthStore;
  153. const resolve = (reportId, value) =>
  154. resolveReport({ reportId, value })
  155. .then((res: any) => {
  156. if (res.status !== "success") new Toast(res.message);
  157. })
  158. .catch(err => new Toast(err.message));
  159. </script>
  160. <template>
  161. <div class="admin-tab container">
  162. <page-metadata title="Admin | Reports" />
  163. <div class="card tab-info">
  164. <div class="info-row">
  165. <h1>Reports</h1>
  166. <p>Manage song reports</p>
  167. </div>
  168. </div>
  169. <advanced-table
  170. :column-default="columnDefault"
  171. :columns="columns"
  172. :filters="filters"
  173. data-action="reports.getData"
  174. name="admin-reports"
  175. :max-width="1200"
  176. :events="events"
  177. >
  178. <template #column-options="slotProps">
  179. <div class="row-options">
  180. <button
  181. class="button is-primary icon-with-button material-icons"
  182. @click="
  183. openModal({
  184. modal: 'viewReport',
  185. props: { reportId: slotProps.item._id }
  186. })
  187. "
  188. :disabled="slotProps.item.removed"
  189. content="View Report"
  190. v-tippy
  191. >
  192. open_in_full
  193. </button>
  194. <button
  195. v-if="
  196. slotProps.item.resolved &&
  197. hasPermission('reports.update')
  198. "
  199. class="button is-danger material-icons icon-with-button"
  200. @click="resolve(slotProps.item._id, false)"
  201. :disabled="slotProps.item.removed"
  202. content="Unresolve Report"
  203. v-tippy
  204. >
  205. remove_done
  206. </button>
  207. <button
  208. v-else-if="
  209. !slotProps.item.resolved &&
  210. hasPermission('reports.update')
  211. "
  212. class="button is-success material-icons icon-with-button"
  213. @click="resolve(slotProps.item._id, true)"
  214. :disabled="slotProps.item.removed"
  215. content="Resolve Report"
  216. v-tippy
  217. >
  218. done_all
  219. </button>
  220. </div>
  221. </template>
  222. <template #column-_id="slotProps">
  223. <span :title="slotProps.item._id">{{
  224. slotProps.item._id
  225. }}</span>
  226. </template>
  227. <template #column-songId="slotProps">
  228. <span :title="slotProps.item.song._id">{{
  229. slotProps.item.song._id
  230. }}</span>
  231. </template>
  232. <template #column-songYoutubeId="slotProps">
  233. <a
  234. :href="
  235. 'https://www.youtube.com/watch?v=' +
  236. `${slotProps.item.song.youtubeId}`
  237. "
  238. target="_blank"
  239. >
  240. {{ slotProps.item.song.mediaSource }}
  241. </a>
  242. </template>
  243. <template #column-resolved="slotProps">
  244. <span :title="slotProps.item.resolved">{{
  245. slotProps.item.resolved
  246. }}</span>
  247. </template>
  248. <template #column-categories="slotProps">
  249. <span
  250. :title="
  251. slotProps.item.issues
  252. .map(issue => issue.category)
  253. .join(', ')
  254. "
  255. >{{
  256. slotProps.item.issues
  257. .map(issue => issue.category)
  258. .join(", ")
  259. }}</span
  260. >
  261. </template>
  262. <template #column-createdBy="slotProps">
  263. <span v-if="slotProps.item.createdBy === 'Musare'">Musare</span>
  264. <user-link v-else :user-id="slotProps.item.createdBy" />
  265. </template>
  266. <template #column-createdAt="slotProps">
  267. <span :title="new Date(slotProps.item.createdAt).toString()">{{
  268. utils.getDateFormatted(slotProps.item.createdAt)
  269. }}</span>
  270. </template>
  271. </advanced-table>
  272. </div>
  273. </template>