Skip to content

Commit 3035ada

Browse files
committed
pkp/pkp-lib#11576 Add backoffice UI for comment moderation
1 parent af709ef commit 3035ada

File tree

7 files changed

+700
-3
lines changed

7 files changed

+700
-3
lines changed

src/components/Container/Page.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import FailedJobDetailsPage from '@/pages/jobs/FailedJobDetailsPage.vue';
1111
import CounterReportsPage from '@/pages/counter/CounterReportsPage.vue';
1212
import UserInvitationPage from '@/pages/userInvitation/UserInvitationPage.vue';
1313
import AcceptInvitationPage from '@/pages/acceptInvitation/AcceptInvitationPage.vue';
14+
import UserCommentsPage from '@/pages/userComments/userCommentsPage.vue';
1415
1516
export default {
1617
name: 'Page',
@@ -25,6 +26,7 @@ export default {
2526
DashboardPage,
2627
UserInvitationPage,
2728
AcceptInvitationPage,
29+
UserCommentsPage,
2830
},
2931
extends: Container,
3032
data() {

src/components/Table/Table.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,8 @@ provide('tableContext', tableContext);
145145
<style lang="less">
146146
@import '../../styles/_import';
147147
148-
/**
149-
Keeping subset of table styling to support urn plugin, which inserts table via FieldHtml
148+
/**
149+
Keeping subset of table styling to support urn plugin, which inserts table via FieldHtml
150150
Better solution in future would be to implement custom Field, which would use our table component directly.
151151
*/
152152

src/pages/dashboard/dashboardPageStore.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ export const useDashboardPageStore = defineComponentStore(
186186
});
187187
});
188188

189-
// Teoretically initFiltersFormFromQueryParams could be called only on the page load.
189+
// Theoretically initFiltersFormFromQueryParams could be called only on the page load.
190190
// Motivation to use watch here is to keep using the url as source of the truth, to
191191
// catch bugs early, without testing all possible filters being loaded just from the url.
192192
watch(
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<template>
2+
<div>
3+
<SideModalBody>
4+
<template #title>{{ t('manager.userComment.reportDetails') }}</template>
5+
<SideModalLayoutBasic>
6+
<div class="p-6 shadow">
7+
<div class="mb-4 flex items-center">
8+
<InitialsAvatar :initials="report.userInitials" />
9+
<div class="ml-2">
10+
<p class="font-semibold">{{ report.userName }}</p>
11+
<p>
12+
{{ formatShortDate(report.createdAt) }}
13+
</p>
14+
</div>
15+
</div>
16+
<p
17+
v-strip-unsafe-html="report.note"
18+
class="font-bold text-negative"
19+
></p>
20+
</div>
21+
22+
<p class="mt-4">{{ t('manager.userComment.reportedComment') }}</p>
23+
<div class="mt-2 border-t pt-4 text-base-normal">
24+
<div class="mb-3 flex">
25+
<InitialsAvatar :initials="comment.userInitials" :shrink="true" />
26+
<div class="ml-2">
27+
<p>
28+
{{ comment.userName }}
29+
</p>
30+
<p>
31+
{{ formatShortDate(comment.createdAt) }}
32+
</p>
33+
</div>
34+
</div>
35+
<p v-strip-unsafe-html="comment.commentText"></p>
36+
</div>
37+
</SideModalLayoutBasic>
38+
</SideModalBody>
39+
</div>
40+
</template>
41+
42+
<script setup>
43+
defineProps({
44+
comment: {
45+
type: Object,
46+
required: true,
47+
},
48+
report: {
49+
type: Object,
50+
required: true,
51+
},
52+
});
53+
54+
import SideModalLayoutBasic from '@/components/Modal/SideModalLayoutBasic.vue';
55+
import InitialsAvatar from '@/components/InitialsAvatar/InitialsAvatar.vue';
56+
import SideModalBody from '@/components/Modal/SideModalBody.vue';
57+
import {useDate} from '@/composables/useDate';
58+
59+
const {formatShortDate} = useDate();
60+
</script>
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
<template>
2+
<div>
3+
<SideModalBody>
4+
<template #title>{{ t('manager.userComment.commentDetails') }}</template>
5+
<SideModalLayoutBasic>
6+
<div class="p-6 shadow">
7+
<div class="mb-4 flex items-center">
8+
<InitialsAvatar :initials="comment.userInitials" />
9+
<div class="ml-3">
10+
<p class="font-semibold">{{ comment.userName }}</p>
11+
<p class="text-sm">
12+
{{ formatShortDate(comment.createdAt) }}
13+
</p>
14+
</div>
15+
</div>
16+
<p v-strip-unsafe-html="comment.commentText" class="mb-6"></p>
17+
</div>
18+
19+
<div class="mt-4">
20+
<FieldOptions
21+
:label="t('manager.userComment.approveThisComment')"
22+
:description="
23+
t('manager.userComment.approveThisComment.description')
24+
"
25+
type="checkbox"
26+
:options="userCommentStore.commentApprovalOptions"
27+
:value="comment.isApproved"
28+
@change="
29+
(fieldName, propName, newValue) =>
30+
userCommentStore.toggleCommentApproval(newValue)
31+
"
32+
/>
33+
</div>
34+
<div v-if="comment.isReported" class="mt-6 p-6 shadow">
35+
<div>
36+
<h3 class="mb-2 font-semibold">
37+
{{ t('manager.userComment.reports') }}
38+
</h3>
39+
<PkpTable>
40+
<TableHeader>
41+
<TableColumn
42+
v-for="(column, i) in userCommentStore.reportsTableColumns"
43+
:key="i"
44+
>
45+
<span :class="column.headerSrOnly ? 'sr-only' : ''">
46+
{{ column.header }}
47+
</span>
48+
</TableColumn>
49+
</TableHeader>
50+
<TableBody>
51+
<TableRow
52+
v-for="(report, i) in userCommentStore.currentCommentReports"
53+
:key="i"
54+
>
55+
<TableCell>
56+
{{ report.note }}
57+
</TableCell>
58+
<TableCell>{{ report.userName }}</TableCell>
59+
<TableCell>{{ formatShortDate(report.createdAt) }}</TableCell>
60+
<TableCell>
61+
<PkpButton
62+
:is-link="true"
63+
@click="userCommentStore.openReport(report)"
64+
>
65+
{{ t('common.view') }}
66+
</PkpButton>
67+
</TableCell>
68+
<TableCell>
69+
<DropdownActions
70+
:label="t('common.moreActions')"
71+
button-variant="ellipsis"
72+
:actions="userCommentStore.getReportItemActions(report)"
73+
@action="
74+
(actionName) => userCommentStore[actionName](report)
75+
"
76+
/>
77+
</TableCell>
78+
</TableRow>
79+
</TableBody>
80+
</PkpTable>
81+
</div>
82+
<TablePagination
83+
:pagination="userCommentStore.currentCommentReportsPagination"
84+
@set-page="
85+
(...args) => userCommentStore.setCurrentReportsPage(...args)
86+
"
87+
></TablePagination>
88+
</div>
89+
</SideModalLayoutBasic>
90+
</SideModalBody>
91+
</div>
92+
</template>
93+
94+
<script setup>
95+
import SideModalBody from '@/components/Modal/SideModalBody.vue';
96+
import SideModalLayoutBasic from '@/components/Modal/SideModalLayoutBasic.vue';
97+
import FieldOptions from '@/components/Form/fields/FieldOptions.vue';
98+
import InitialsAvatar from '@/components/InitialsAvatar/InitialsAvatar.vue';
99+
import PkpTable from '@/components/Table/Table.vue';
100+
import TableHeader from '@/components/Table/TableHeader.vue';
101+
import TableColumn from '@/components/Table/TableColumn.vue';
102+
import TableBody from '@/components/Table/TableBody.vue';
103+
import TableRow from '@/components/Table/TableRow.vue';
104+
import TableCell from '@/components/Table/TableCell.vue';
105+
import DropdownActions from '@/components/DropdownActions/DropdownActions.vue';
106+
import PkpButton from '@/components/Button/Button.vue';
107+
import {useUserCommentStore} from '@/pages/userComments/userCommentStore';
108+
import TablePagination from '@/components/Table/TablePagination.vue';
109+
import {useLocalize} from '@/composables/useLocalize';
110+
import {useDate} from '@/composables/useDate';
111+
112+
const {formatShortDate} = useDate();
113+
const {t} = useLocalize();
114+
115+
const props = defineProps({
116+
comment: {
117+
type: Object,
118+
required: true,
119+
},
120+
});
121+
122+
const userCommentStore = useUserCommentStore(props);
123+
</script>

0 commit comments

Comments
 (0)