|
@@ -1,110 +1,214 @@
|
|
|
<template>
|
|
|
<modal title="View Report">
|
|
|
<template #body v-if="report && report._id">
|
|
|
- <router-link
|
|
|
- v-if="$route.query.returnToSong"
|
|
|
- class="button is-dark back-to-song"
|
|
|
- :to="{
|
|
|
- path: '/admin/songs',
|
|
|
- query: { id: report.youtubeId }
|
|
|
- }"
|
|
|
- >
|
|
|
- <i class="material-icons">keyboard_return</i> Edit Song
|
|
|
- </router-link>
|
|
|
-
|
|
|
- <article class="message">
|
|
|
- <div class="message-body">
|
|
|
- <strong>Song ID:</strong>
|
|
|
- {{ report.song.youtubeId }} / {{ report.song._id }}
|
|
|
- <br />
|
|
|
- <strong>Author:</strong>
|
|
|
- <user-id-to-username
|
|
|
- :user-id="report.createdBy"
|
|
|
- :alt="report.createdBy"
|
|
|
- />
|
|
|
- <br />
|
|
|
- <strong>Time of report:</strong>
|
|
|
- <span
|
|
|
- :content="report.createdAt"
|
|
|
- v-tippy="{ theme: 'info' }"
|
|
|
+ <div class="report-item">
|
|
|
+ <div class="report-item-header">
|
|
|
+ <div class="report-item-info">
|
|
|
+ <div class="report-item-icon">
|
|
|
+ <profile-picture
|
|
|
+ :avatar="report.createdBy.avatar"
|
|
|
+ :name="
|
|
|
+ report.createdBy.name
|
|
|
+ ? report.createdBy.name
|
|
|
+ : report.createdBy.username
|
|
|
+ "
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="report-item-summary">
|
|
|
+ <p class="report-item-summary-title">
|
|
|
+ Reported by
|
|
|
+ <router-link
|
|
|
+ :to="{
|
|
|
+ path: `/u/${report.createdBy.username}`
|
|
|
+ }"
|
|
|
+ :title="report.createdBy._id"
|
|
|
+ >
|
|
|
+ {{ report.createdBy.username }}
|
|
|
+ </router-link>
|
|
|
+ </p>
|
|
|
+ <p class="report-item-summary-description">
|
|
|
+ {{
|
|
|
+ formatDistance(
|
|
|
+ new Date(report.createdAt),
|
|
|
+ new Date(),
|
|
|
+ {
|
|
|
+ addSuffix: true
|
|
|
+ }
|
|
|
+ )
|
|
|
+ }}
|
|
|
+ / YouTube:
|
|
|
+ <a
|
|
|
+ :href="
|
|
|
+ 'https://www.youtube.com/watch?v=' +
|
|
|
+ `${report.song.youtubeId}`
|
|
|
+ "
|
|
|
+ target="_blank"
|
|
|
+ >
|
|
|
+ {{ report.song.youtubeId }}</a
|
|
|
+ >
|
|
|
+ / Song ID: {{ report.song._id }}
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="report-sub-items">
|
|
|
+ <div
|
|
|
+ class="report-sub-item report-sub-item-unresolved"
|
|
|
+ :class="[
|
|
|
+ 'report',
|
|
|
+ issue.resolved
|
|
|
+ ? 'report-sub-item-resolved'
|
|
|
+ : 'report-sub-item-unresolved'
|
|
|
+ ]"
|
|
|
+ v-for="(issue, issueIndex) in report.issues"
|
|
|
+ :key="issueIndex"
|
|
|
>
|
|
|
- {{
|
|
|
- formatDistance(
|
|
|
- new Date(report.createdAt),
|
|
|
- new Date(),
|
|
|
- {
|
|
|
- addSuffix: true
|
|
|
- }
|
|
|
- )
|
|
|
- }}
|
|
|
- </span>
|
|
|
- <br />
|
|
|
- <span>
|
|
|
- <strong>Issues:</strong>
|
|
|
- <ul id="issues">
|
|
|
- <li
|
|
|
- v-for="(issue, index) in report.issues"
|
|
|
- :key="index"
|
|
|
+ <i
|
|
|
+ class="material-icons duration-icon report-sub-item-left-icon"
|
|
|
+ :content="issue.category"
|
|
|
+ v-tippy
|
|
|
+ >
|
|
|
+ {{ icons[issue.category] }}
|
|
|
+ </i>
|
|
|
+ <p class="report-sub-item-info">
|
|
|
+ <span class="report-sub-item-title">
|
|
|
+ {{ issue.title }}
|
|
|
+ </span>
|
|
|
+ <span
|
|
|
+ class="report-sub-item-description"
|
|
|
+ v-if="issue.description"
|
|
|
>
|
|
|
- <strong> {{ issue.category }}:</strong>
|
|
|
- {{ issue.info }}
|
|
|
- </li>
|
|
|
- </ul>
|
|
|
- </span>
|
|
|
+ {{ issue.description }}
|
|
|
+ </span>
|
|
|
+ </p>
|
|
|
+
|
|
|
+ <div
|
|
|
+ class="report-sub-item-actions universal-item-actions"
|
|
|
+ >
|
|
|
+ <i
|
|
|
+ class="material-icons resolve-icon"
|
|
|
+ content="Resolve"
|
|
|
+ v-tippy
|
|
|
+ v-if="!issue.resolved"
|
|
|
+ @click="toggleIssue(report._id, issue._id)"
|
|
|
+ >
|
|
|
+ done
|
|
|
+ </i>
|
|
|
+ <i
|
|
|
+ class="material-icons unresolve-icon"
|
|
|
+ content="Unresolve"
|
|
|
+ v-tippy
|
|
|
+ v-else
|
|
|
+ @click="toggleIssue(report._id, issue._id)"
|
|
|
+ >
|
|
|
+ remove
|
|
|
+ </i>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </article>
|
|
|
+ </div>
|
|
|
</template>
|
|
|
<template #footer v-if="report && report._id">
|
|
|
- <a class="button is-primary" href="#" @click="resolve(report._id)">
|
|
|
- <span>Resolve</span>
|
|
|
- </a>
|
|
|
<a
|
|
|
class="button is-primary"
|
|
|
:href="`/admin/songs?songId=${report.song._id}`"
|
|
|
target="_blank"
|
|
|
>
|
|
|
- <span>Go to song</span>
|
|
|
+ <i
|
|
|
+ class="material-icons icon-with-button"
|
|
|
+ content="Open Song in New Tab"
|
|
|
+ v-tippy
|
|
|
+ >
|
|
|
+ open_in_new
|
|
|
+ </i>
|
|
|
+ Open Song
|
|
|
+ </a>
|
|
|
+ <a class="button is-success" href="#" @click="resolve(report._id)">
|
|
|
+ <i
|
|
|
+ class="material-icons icon-with-button"
|
|
|
+ content="Resolve"
|
|
|
+ v-tippy
|
|
|
+ >
|
|
|
+ done_all
|
|
|
+ </i>
|
|
|
+ Resolve
|
|
|
</a>
|
|
|
</template>
|
|
|
</modal>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
-import { mapActions, mapGetters, mapState } from "vuex";
|
|
|
+import { mapActions, mapGetters } from "vuex";
|
|
|
import { formatDistance } from "date-fns";
|
|
|
import Toast from "toasters";
|
|
|
|
|
|
-import UserIdToUsername from "../UserIdToUsername.vue";
|
|
|
+import ProfilePicture from "@/components/ProfilePicture.vue";
|
|
|
import Modal from "../Modal.vue";
|
|
|
|
|
|
export default {
|
|
|
- components: { Modal, UserIdToUsername },
|
|
|
+ components: { Modal, ProfilePicture },
|
|
|
props: {
|
|
|
reportId: { type: String, default: "" },
|
|
|
sector: { type: String, default: "admin" }
|
|
|
},
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ icons: {
|
|
|
+ duration: "timer",
|
|
|
+ video: "tv",
|
|
|
+ thumbnail: "image",
|
|
|
+ artists: "record_voice_over",
|
|
|
+ title: "title",
|
|
|
+ custom: "lightbulb"
|
|
|
+ },
|
|
|
+ report: {}
|
|
|
+ };
|
|
|
+ },
|
|
|
computed: {
|
|
|
- ...mapState("modals/viewReport", {
|
|
|
- report: state => state.report
|
|
|
- }),
|
|
|
...mapGetters({
|
|
|
socket: "websockets/getSocket"
|
|
|
})
|
|
|
},
|
|
|
mounted() {
|
|
|
- if (this.$route.query.returnToSong) {
|
|
|
- this.closeModal("editSong");
|
|
|
- }
|
|
|
-
|
|
|
- this.socket.dispatch(`reports.findOne`, this.reportId, res => {
|
|
|
+ this.socket.dispatch("reports.findOne", this.reportId, res => {
|
|
|
if (res.status === "success") {
|
|
|
const { report } = res.data;
|
|
|
- this.viewReport(report);
|
|
|
+
|
|
|
+ this.socket.dispatch(
|
|
|
+ "apis.joinRoom",
|
|
|
+ `view-report.${report._id}`
|
|
|
+ );
|
|
|
+
|
|
|
+ this.report = report;
|
|
|
} else {
|
|
|
new Toast("Report with that ID not found");
|
|
|
this.closeModal("viewReport");
|
|
|
}
|
|
|
});
|
|
|
+
|
|
|
+ this.socket.on(
|
|
|
+ "event:admin.report.resolved",
|
|
|
+ () => this.closeModal("viewReport"),
|
|
|
+ { modal: "viewReport" }
|
|
|
+ );
|
|
|
+
|
|
|
+ this.socket.on(
|
|
|
+ "event:admin.report.issue.toggled",
|
|
|
+ res => {
|
|
|
+ if (this.report._id === res.data.reportId) {
|
|
|
+ const issue = this.report.issues.find(
|
|
|
+ issue => issue._id.toString() === res.data.issueId
|
|
|
+ );
|
|
|
+
|
|
|
+ issue.resolved = !issue.resolved;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ { modal: "viewReport" }
|
|
|
+ );
|
|
|
+ },
|
|
|
+ beforeUnmount() {
|
|
|
+ this.socket.dispatch("apis.leaveRoom", `view-report.${this.reportId}`);
|
|
|
},
|
|
|
methods: {
|
|
|
formatDistance,
|
|
@@ -115,31 +219,152 @@ export default {
|
|
|
})
|
|
|
.catch(err => new Toast(err.message));
|
|
|
},
|
|
|
- ...mapActions("modals/viewReport", ["viewReport", "resolveReport"]),
|
|
|
+ toggleIssue(reportId, issueId) {
|
|
|
+ this.socket.dispatch(
|
|
|
+ "reports.toggleIssue",
|
|
|
+ reportId,
|
|
|
+ issueId,
|
|
|
+ res => {
|
|
|
+ if (res.status !== "success") new Toast(res.message);
|
|
|
+ }
|
|
|
+ );
|
|
|
+ },
|
|
|
+ ...mapActions("admin/reports", ["indexReports", "resolveReport"]),
|
|
|
...mapActions("modalVisibility", ["closeModal"])
|
|
|
}
|
|
|
};
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
-.night-mode {
|
|
|
- .message,
|
|
|
- .table {
|
|
|
- color: var(--light-grey-2);
|
|
|
- background-color: var(--dark-grey-3);
|
|
|
+.report-item {
|
|
|
+ background-color: var(--white);
|
|
|
+ border: 0.5px solid var(--primary-color);
|
|
|
+ border-radius: 5px;
|
|
|
+ padding: 8px;
|
|
|
+
|
|
|
+ &:not(:first-of-type) {
|
|
|
+ margin-bottom: 16px;
|
|
|
}
|
|
|
|
|
|
- .table tr:hover {
|
|
|
- filter: brightness(95%);
|
|
|
+ .report-item-header {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ margin-bottom: 8px;
|
|
|
+ background-color: var(--light-grey);
|
|
|
+ // padding: 5px;
|
|
|
+ padding: 10px;
|
|
|
+ border-radius: 5px;
|
|
|
+
|
|
|
+ .report-item-info {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ .report-item-icon {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ .profile-picture,
|
|
|
+ i {
|
|
|
+ margin-right: 10px;
|
|
|
+ width: 45px;
|
|
|
+ height: 45px;
|
|
|
+ }
|
|
|
+
|
|
|
+ i {
|
|
|
+ font-size: 30px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .report-item-summary {
|
|
|
+ .report-item-summary-title {
|
|
|
+ // font-size: 14px;
|
|
|
+ font-size: 16px;
|
|
|
+ text-transform: capitalize;
|
|
|
+ }
|
|
|
+
|
|
|
+ .report-item-summary-description {
|
|
|
+ text-transform: capitalize;
|
|
|
+ // font-size: 12px;
|
|
|
+ font-size: 14px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .report-item-actions {
|
|
|
+ height: 24px;
|
|
|
+ margin-right: 4px;
|
|
|
+ }
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
-.back-to-song {
|
|
|
- display: flex;
|
|
|
- margin-bottom: 20px;
|
|
|
-}
|
|
|
+ .report-sub-items {
|
|
|
+ .report-sub-item {
|
|
|
+ border: 0.5px solid var(--black);
|
|
|
+ margin-top: -1px;
|
|
|
+ line-height: 24px;
|
|
|
+ display: flex;
|
|
|
+ // padding: 4px;
|
|
|
+ padding: 8px;
|
|
|
+ display: flex;
|
|
|
|
|
|
-#issues {
|
|
|
- list-style: inside;
|
|
|
+ &:first-child {
|
|
|
+ border-radius: 3px 3px 0 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ &:last-child {
|
|
|
+ border-radius: 0 0 3px 3px;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.report-sub-item-resolved {
|
|
|
+ .report-sub-item-description,
|
|
|
+ .report-sub-item-title {
|
|
|
+ text-decoration: line-through;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .report-sub-item-left-icon {
|
|
|
+ margin-right: 8px;
|
|
|
+ margin-top: auto;
|
|
|
+ margin-bottom: auto;
|
|
|
+ }
|
|
|
+
|
|
|
+ .report-sub-item-info {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+
|
|
|
+ .report-sub-item-title {
|
|
|
+ // font-size: 14px;
|
|
|
+ font-size: 16px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .report-sub-item-description {
|
|
|
+ // font-size: 12px;
|
|
|
+ font-size: 14px;
|
|
|
+ line-height: 16px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .report-sub-item-actions {
|
|
|
+ height: 24px;
|
|
|
+ margin-left: 8px;
|
|
|
+ margin-top: auto;
|
|
|
+ margin-bottom: auto;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .resolve-icon {
|
|
|
+ color: var(--green);
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+
|
|
|
+ .unresolve-icon {
|
|
|
+ color: var(--red);
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
}
|
|
|
</style>
|