277 lines
5.7 KiB
Vue
277 lines
5.7 KiB
Vue
<script setup>
|
|
import { format } from 'date-fns';
|
|
import { inject, ref } from 'vue';
|
|
|
|
import Admin from '#/components/admin/admin.vue';
|
|
import Checkbox from '#/components/form/checkbox.vue';
|
|
|
|
import { patch, post } from '#/src/api.js';
|
|
import reportReasons from '#/utils/report-reasons.js';
|
|
|
|
const { pageProps, user } = inject('pageContext');
|
|
const { completed } = pageProps;
|
|
|
|
const reports = ref(pageProps.reports);
|
|
|
|
async function updateReport(report, update) {
|
|
const updatedReport = await patch(`/reports/${report.id}`, update, {
|
|
errorFeedback: 'Failed to update report',
|
|
appendErrorMessage: true,
|
|
}).catch(() => null); // feedback is shown by the api module
|
|
|
|
if (updatedReport) {
|
|
reports.value = reports.value.map((listedReport) => (listedReport.id === report.id
|
|
? {
|
|
...listedReport,
|
|
isRead: updatedReport.isRead,
|
|
isCompleted: updatedReport.isCompleted,
|
|
}
|
|
: listedReport));
|
|
}
|
|
}
|
|
|
|
async function banReporter(report) {
|
|
if (!confirm(`Ban ${report.user.username} from submitting reports?`)) { // eslint-disable-line no-alert
|
|
return;
|
|
}
|
|
|
|
await post('/bans', {
|
|
userId: report.user.id,
|
|
banIp: true,
|
|
scope: 'reports',
|
|
}, {
|
|
successFeedback: `Banned ${report.user.username} from submitting reports`,
|
|
errorFeedback: `Failed to ban ${report.user.username}`,
|
|
appendErrorMessage: true,
|
|
}).catch(() => {}); // feedback is shown by the api module
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<Admin class="page">
|
|
<div class="header">
|
|
<h2 class="heading">{{ completed ? 'Completed reports' : 'Open reports' }}</h2>
|
|
|
|
<a
|
|
:href="completed ? '/admin/reports' : '/admin/reports?completed=true'"
|
|
class="link toggle-completed"
|
|
>{{ completed ? 'Show open reports' : 'Show completed reports' }}</a>
|
|
</div>
|
|
|
|
<p
|
|
v-if="reports.length === 0"
|
|
class="empty"
|
|
>No {{ completed ? 'completed' : 'open' }} reports.</p>
|
|
|
|
<table
|
|
v-else
|
|
class="table"
|
|
>
|
|
<thead class="table-header">
|
|
<tr>
|
|
<th class="table-heading">Site</th>
|
|
<th class="table-heading">Reasons</th>
|
|
<th class="table-heading">Comment</th>
|
|
<th class="table-heading">Reporter</th>
|
|
<th class="table-heading">Date</th>
|
|
<th class="table-heading table-check">Read</th>
|
|
<th class="table-heading table-check">Done</th>
|
|
<th
|
|
v-if="user.role === 'admin'"
|
|
class="table-heading"
|
|
>Ban</th>
|
|
</tr>
|
|
</thead>
|
|
|
|
<tbody>
|
|
<tr
|
|
v-for="report in reports"
|
|
:key="`report-${report.id}`"
|
|
class="report"
|
|
:class="{ unread: !report.isRead, completed: report.isCompleted !== completed }"
|
|
>
|
|
<td
|
|
:title="report.entity?.id"
|
|
class="table-cell table-name ellipsis"
|
|
>
|
|
<a
|
|
v-if="report.entity"
|
|
:href="`/${report.entity.type}/${report.entity.slug}`"
|
|
target="_blank"
|
|
class="link"
|
|
>{{ report.entity.name }}</a>
|
|
</td>
|
|
|
|
<td class="table-cell table-reasons">
|
|
<span
|
|
v-for="reason in report.reasons"
|
|
:key="`report-${report.id}-${reason}`"
|
|
class="reason"
|
|
:class="{ urgent: reason === 'inappropriate' }"
|
|
>{{ reportReasons[reason] || reason }}</span>
|
|
</td>
|
|
|
|
<td class="table-cell table-comment">{{ report.comment }}</td>
|
|
|
|
<td class="table-cell table-username ellipsis">
|
|
<a
|
|
v-if="report.user"
|
|
:href="`/user/${report.user.username}`"
|
|
target="_blank"
|
|
class="link"
|
|
>{{ report.user.username }}</a>
|
|
|
|
<span
|
|
v-else
|
|
class="deleted"
|
|
>deleted user</span>
|
|
</td>
|
|
|
|
<td class="table-cell table-date">{{ format(report.createdAt, 'yyyy-MM-dd HH:mm') }}</td>
|
|
|
|
<td class="table-cell table-check">
|
|
<Checkbox
|
|
:checked="report.isRead"
|
|
@change="(checked) => updateReport(report, { isRead: checked })"
|
|
/>
|
|
</td>
|
|
|
|
<td class="table-cell table-check">
|
|
<Checkbox
|
|
:checked="report.isCompleted"
|
|
@change="(checked) => updateReport(report, { isCompleted: checked })"
|
|
/>
|
|
</td>
|
|
|
|
<td
|
|
v-if="user.role === 'admin'"
|
|
class="table-cell table-actions"
|
|
>
|
|
<Icon
|
|
v-if="report.user"
|
|
v-tooltip="`Ban ${report.user.username} from submitting reports`"
|
|
icon="blocked"
|
|
class="ban"
|
|
@click="banReporter(report)"
|
|
/>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</Admin>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.page {
|
|
flex-grow: 1;
|
|
}
|
|
|
|
.header {
|
|
display: flex;
|
|
align-items: baseline;
|
|
gap: 1rem;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.heading {
|
|
margin: 0;
|
|
}
|
|
|
|
.empty {
|
|
color: var(--glass);
|
|
font-style: italic;
|
|
}
|
|
|
|
.table {
|
|
border-collapse: collapse;
|
|
border: 0;
|
|
}
|
|
|
|
.table-heading {
|
|
text-align: left;
|
|
padding: 0 .5rem .25rem .5rem;
|
|
}
|
|
|
|
.table-cell {
|
|
padding: .25rem .5rem .25rem .5rem;
|
|
border-collapse: collapse;
|
|
border: 0;
|
|
}
|
|
|
|
.table-name {
|
|
max-width: 12rem;
|
|
}
|
|
|
|
.table-comment {
|
|
min-width: 15rem;
|
|
white-space: pre-wrap;
|
|
overflow-wrap: anywhere;
|
|
}
|
|
|
|
.table-date {
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.table-check {
|
|
width: 3rem;
|
|
text-align: center;
|
|
|
|
:deep(.check-container) {
|
|
justify-content: center;
|
|
}
|
|
}
|
|
|
|
.report {
|
|
&:nth-child(2) {
|
|
background: var(--glass-weak-50);
|
|
}
|
|
|
|
&.unread .table-name .link {
|
|
color: var(--primary);
|
|
}
|
|
|
|
/* marked completed (or reopened) on this page, it moves to the other list on reload */
|
|
&.completed {
|
|
opacity: .5;
|
|
}
|
|
}
|
|
|
|
.reason {
|
|
display: inline-block;
|
|
padding: .25rem .5rem;
|
|
margin: 0 .25rem .25rem 0;
|
|
border-radius: .25rem;
|
|
background: var(--background-dark-20);
|
|
font-size: .9rem;
|
|
white-space: nowrap;
|
|
|
|
&.urgent {
|
|
background: var(--warn);
|
|
color: var(--text-light);
|
|
font-weight: bold;
|
|
}
|
|
}
|
|
|
|
.link {
|
|
color: var(--text);
|
|
}
|
|
|
|
.toggle-completed {
|
|
color: var(--primary);
|
|
}
|
|
|
|
.deleted {
|
|
color: var(--glass);
|
|
font-style: italic;
|
|
}
|
|
|
|
.ban {
|
|
fill: var(--glass);
|
|
|
|
&:hover {
|
|
fill: var(--warn);
|
|
cursor: pointer;
|
|
}
|
|
}
|
|
</style>
|