strapi-plugin-comments 2.0.0-beta.2 → 2.0.0-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -5
- package/admin/src/components/DiscussionThread/index.js +10 -2
- package/admin/src/components/DiscussionThreadItem/index.js +25 -6
- package/admin/src/components/DiscussionThreadItem/styles.js +1 -0
- package/admin/src/components/DiscussionThreadItemActions/index.js +69 -56
- package/admin/src/components/DiscussionThreadItemActions/styles.js +0 -4
- package/admin/src/components/DiscussionThreadItemApprovalFlowActions/index.js +81 -0
- package/admin/src/components/DiscussionThreadItemApprovalFlowActions/styles.js +6 -0
- package/admin/src/components/DiscussionThreadItemWarnings/index.js +26 -16
- package/admin/src/components/Nav/index.js +5 -1
- package/admin/src/components/ReportsReviewModal/index.js +5 -2
- package/admin/src/components/ReportsReviewTable/index.js +69 -57
- package/admin/src/components/icons/index.js +6 -0
- package/admin/src/components/icons/lock.js +22 -0
- package/admin/src/components/icons/review.js +18 -0
- package/admin/src/components/icons/unlock.js +20 -0
- package/admin/src/index.js +1 -1
- package/admin/src/pages/App/index.js +20 -9
- package/admin/src/pages/App/reducer/index.js +0 -1
- package/admin/src/pages/App/reducer/selectors.js +1 -1
- package/admin/src/pages/App/utils/api.js +2 -10
- package/admin/src/pages/Details/components/DetailsEntity/index.js +66 -0
- package/admin/src/pages/Details/components/DetailsFilters/index.js +37 -0
- package/admin/src/pages/Details/components/DetailsFilters/styles.js +8 -0
- package/admin/src/pages/Details/index.js +75 -42
- package/admin/src/pages/Discover/components/DiscoverTableRow/index.js +210 -0
- package/admin/src/pages/Discover/components/DiscoverTableRow/styles.js +8 -0
- package/admin/src/pages/Discover/index.js +69 -102
- package/admin/src/pages/Discover/utils/api.js +2 -8
- package/admin/src/pages/{Details/utils → utils}/api.js +15 -3
- package/admin/src/permissions.js +10 -8
- package/admin/src/translations/en.json +17 -4
- package/admin/src/utils/constants.js +3 -6
- package/admin/src/utils/getMessage.js +10 -3
- package/admin/src/utils/getRelatedGroups.js +1 -3
- package/admin/src/utils/handleAPIError.js +1 -1
- package/admin/src/utils/index.js +4 -2
- package/admin/src/utils/renderEntryTitle.js +14 -0
- package/admin/src/utils/resolveCommentStatus.js +30 -0
- package/admin/src/utils/resolveCommentStatusColor.js +22 -0
- package/package.json +7 -3
- package/permissions.js +19 -0
- package/server/bootstrap.js +38 -2
- package/server/config/index.js +4 -3
- package/server/controllers/admin.js +3 -3
- package/server/controllers/client.js +11 -2
- package/server/services/admin.js +69 -13
- package/server/services/client.js +23 -15
- package/server/services/common.js +40 -69
- package/server/services/utils/functions.js +7 -14
- package/server/utils/constants.js +3 -3
- package/strapi-server.js +33 -0
- package/admin/src/components/EntityDetails/index.js +0 -53
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { useIntl } from 'react-intl';
|
|
4
|
+
import { useMutation, useQueryClient } from 'react-query';
|
|
5
|
+
import { isNil, isEmpty, orderBy } from 'lodash';
|
|
6
|
+
import { Badge } from '@strapi/design-system/Badge';
|
|
7
|
+
import { Box } from '@strapi/design-system/Box';
|
|
8
|
+
import { Button } from '@strapi/design-system/Button';
|
|
9
|
+
import { Divider } from '@strapi/design-system/Divider';
|
|
10
|
+
import { Flex } from '@strapi/design-system/Flex';
|
|
11
|
+
import { IconButton } from '@strapi/design-system/IconButton';
|
|
12
|
+
import { Link } from '@strapi/design-system/Link';
|
|
13
|
+
import { Stack } from '@strapi/design-system/Stack';
|
|
14
|
+
import { Tr, Td } from '@strapi/design-system/Table';
|
|
15
|
+
import { Typography } from '@strapi/design-system/Typography';
|
|
16
|
+
import { Eye } from '@strapi/icons';
|
|
17
|
+
import { useNotification, useOverlayBlocker } from '@strapi/helper-plugin';
|
|
18
|
+
import { getMessage, getUrl, handleAPIError, resolveCommentStatus, resolveCommentStatusColor } from '../../../../utils';
|
|
19
|
+
import ReportsReviewModal from '../../../../components/ReportsReviewModal';
|
|
20
|
+
import ReportsReviewTable from '../../../../components/ReportsReviewTable';
|
|
21
|
+
import { blockItem, blockItemThread, resolveReport } from '../../../utils/api';
|
|
22
|
+
import pluginId from '../../../../pluginId';
|
|
23
|
+
import { ReviewIcon, LockIcon } from '../../../../components/icons';
|
|
24
|
+
import { TableLink } from './styles';
|
|
25
|
+
import renderEntryTitle from '../../../../utils/renderEntryTitle';
|
|
26
|
+
import DiscussionThreadItemApprovalFlowActions from '../../../../components/DiscussionThreadItemApprovalFlowActions';
|
|
27
|
+
|
|
28
|
+
const DiscoverTableRow = ({ config, item, allowedActions: { canModerate, canAccessReports, canReviewReports }, onClick }) => {
|
|
29
|
+
|
|
30
|
+
const { id, reports } = item;
|
|
31
|
+
|
|
32
|
+
const [reportsReviewVisible, setReportsReviewVisible] = useState(false);
|
|
33
|
+
const [blockButtonsDisabled, setBlockButtonsDisabled] = useState(false);
|
|
34
|
+
|
|
35
|
+
const { formatDate } = useIntl();
|
|
36
|
+
const toggleNotification = useNotification();
|
|
37
|
+
const queryClient = useQueryClient();
|
|
38
|
+
const { lockApp, unlockApp } = useOverlayBlocker();
|
|
39
|
+
|
|
40
|
+
const onSuccess = (message, stateAction = () => {}, indalidate = true) => async () => {
|
|
41
|
+
if (indalidate) {
|
|
42
|
+
await queryClient.invalidateQueries('get-data');
|
|
43
|
+
}
|
|
44
|
+
toggleNotification({
|
|
45
|
+
type: 'success',
|
|
46
|
+
message: `${pluginId}.${message}`,
|
|
47
|
+
});
|
|
48
|
+
stateAction(false);
|
|
49
|
+
unlockApp();
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const onError = err => { handleAPIError(err, toggleNotification); };
|
|
53
|
+
|
|
54
|
+
const resolveReportMutation = useMutation(resolveReport, {
|
|
55
|
+
onSuccess: onSuccess('page.details.panel.discussion.warnings.reports.dialog.confirmation.success', () => {}, false),
|
|
56
|
+
onError,
|
|
57
|
+
refetchActive: false,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const blockItemMutation = useMutation(blockItem, {
|
|
61
|
+
onSuccess: onSuccess(
|
|
62
|
+
'page.details.actions.comment.block.confirmation.success',
|
|
63
|
+
setReportsReviewVisible
|
|
64
|
+
),
|
|
65
|
+
onError,
|
|
66
|
+
refetchActive: false,
|
|
67
|
+
});
|
|
68
|
+
const blockItemThreadMutation = useMutation(blockItemThread, {
|
|
69
|
+
onSuccess: onSuccess(
|
|
70
|
+
'page.details.actions.thread.block.confirmation.success',
|
|
71
|
+
setReportsReviewVisible
|
|
72
|
+
),
|
|
73
|
+
onError,
|
|
74
|
+
refetchActive: false,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const handleReportsReviewClick = () => {
|
|
78
|
+
if (canAccessReports) {
|
|
79
|
+
setReportsReviewVisible(true);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
const handleBlockItemClick = async () => {
|
|
83
|
+
if (canModerate) {
|
|
84
|
+
lockApp();
|
|
85
|
+
blockItemMutation.mutate(id);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
const handleBlockItemThreadClick = async () => {
|
|
89
|
+
if (canModerate) {
|
|
90
|
+
lockApp();
|
|
91
|
+
blockItemThreadMutation.mutate(id);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
const handleReportsReviewClose = async () => {
|
|
95
|
+
await queryClient.invalidateQueries('get-data');
|
|
96
|
+
setReportsReviewVisible(false);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const isLoading = blockItemMutation.isLoading || blockItemThreadMutation.isLoading || resolveReportMutation.isLoading;
|
|
100
|
+
const openReports = reports?.filter(_ => !_.resolved);
|
|
101
|
+
const hasReports = !isEmpty(openReports);
|
|
102
|
+
const reviewFlowEnabled = (canAccessReports && hasReports) && !(item.blocked || item.blockedThread);
|
|
103
|
+
|
|
104
|
+
const handleClick = () => onClick();
|
|
105
|
+
const handleBlockButtonsStateChange = disabled => setBlockButtonsDisabled(disabled);
|
|
106
|
+
|
|
107
|
+
const renderStatus = (props) => {
|
|
108
|
+
const status = resolveCommentStatus({ ...props, reviewFlowEnabled });
|
|
109
|
+
const color = resolveCommentStatusColor(status);
|
|
110
|
+
|
|
111
|
+
return (<Badge backgroundColor={`${color}100`} textColor={`${color}600`}>{ getMessage({
|
|
112
|
+
id: `page.common.item.status.${status}`,
|
|
113
|
+
props: {
|
|
114
|
+
count: openReports.length
|
|
115
|
+
}
|
|
116
|
+
}, status) }</Badge>);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const renderEntryUrl = entry => `/content-manager/collectionType/${entry.uid}/${entry.id}`;
|
|
120
|
+
const renderDetailsUrl = entry => getUrl(`discover/${entry.id}`);
|
|
121
|
+
|
|
122
|
+
const gotApprovalFlow = !isNil(item.approvalStatus);
|
|
123
|
+
const needsApproval = gotApprovalFlow && (item.approvalStatus === 'PENDING');
|
|
124
|
+
|
|
125
|
+
let actionItemsCount = 1;
|
|
126
|
+
if(reviewFlowEnabled || needsApproval) {
|
|
127
|
+
actionItemsCount = 2;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<Tr key={item.id}>
|
|
132
|
+
<Td>
|
|
133
|
+
<Typography textColor="neutral800" fontWeight="bold">#{ item.id }</Typography>
|
|
134
|
+
</Td>
|
|
135
|
+
<Td style={{ maxWidth: '30vw' }}>
|
|
136
|
+
<Typography textColor="neutral800" ellipsis>{item.content}</Typography>
|
|
137
|
+
</Td>
|
|
138
|
+
<Td>
|
|
139
|
+
{ item.threadOf?.id ? (<Link to={ renderDetailsUrl(item.threadOf) }>{ getMessage({
|
|
140
|
+
id: 'page.discover.table.cell.thread',
|
|
141
|
+
props: { id: item.threadOf.id },
|
|
142
|
+
}, '#' + item.threadOf.id) }</Link>) : '-' }
|
|
143
|
+
</Td>
|
|
144
|
+
<Td style={{ maxWidth: '15vw' }}>
|
|
145
|
+
<TableLink to={ renderEntryUrl(item.related) }>{ renderEntryTitle(item.related, config) }</TableLink>
|
|
146
|
+
</Td>
|
|
147
|
+
<Td>
|
|
148
|
+
<Typography textColor="neutral800">{ formatDate(item.updatedAt || item.createdAt, { dateStyle: 'long', timeStyle: 'short' }) }</Typography>
|
|
149
|
+
</Td>
|
|
150
|
+
<Td>
|
|
151
|
+
<Typography textColor="neutral800">{ renderStatus(item) }</Typography>
|
|
152
|
+
</Td>
|
|
153
|
+
<Td>
|
|
154
|
+
<Flex direction="column" alignItems="flex-end">
|
|
155
|
+
<Stack size={actionItemsCount} horizontal>
|
|
156
|
+
{ reviewFlowEnabled && (<IconButton onClick={handleReportsReviewClick} label={ getMessage('page.discover.table.reports.review') } icon={<ReviewIcon />} />)}
|
|
157
|
+
{ (canModerate && needsApproval) && (
|
|
158
|
+
<DiscussionThreadItemApprovalFlowActions
|
|
159
|
+
id={item.id}
|
|
160
|
+
allowedActions={{ canModerate }}
|
|
161
|
+
queryToInvalidate="get-data" />
|
|
162
|
+
)}
|
|
163
|
+
<IconButton onClick={handleClick} label={ getMessage('page.discover.table.action.display') } icon={<Eye />} />
|
|
164
|
+
</Stack>
|
|
165
|
+
</Flex>
|
|
166
|
+
{ reviewFlowEnabled && (<ReportsReviewModal
|
|
167
|
+
isVisible={reportsReviewVisible}
|
|
168
|
+
isActionAsync={isLoading}
|
|
169
|
+
allowedActions={{ canModerate, canAccessReports, canReviewReports }}
|
|
170
|
+
onClose={handleReportsReviewClose}
|
|
171
|
+
startActions={canModerate && (<>
|
|
172
|
+
<Button onClick={handleBlockItemClick} variant="danger-light" startIcon={<LockIcon />} disabled={blockButtonsDisabled}>
|
|
173
|
+
{ getMessage(`page.details.actions.comment.block`, 'Block comment') }
|
|
174
|
+
</Button>
|
|
175
|
+
{ item.gotThread && (<Button onClick={handleBlockItemThreadClick} variant="danger" startIcon={<LockIcon />} disabled={blockButtonsDisabled}>
|
|
176
|
+
{ getMessage(`page.details.actions.thread.block`, 'Block thread') }
|
|
177
|
+
</Button>) }
|
|
178
|
+
</>)}
|
|
179
|
+
item={item}>
|
|
180
|
+
<Typography variant="sigma" textColor="neutral600" id="reports-list">
|
|
181
|
+
{ getMessage('page.details.panel.discussion.warnings.reports.list', 'List') }
|
|
182
|
+
</Typography>
|
|
183
|
+
<Box paddingTop={2} paddingBottom={6}>
|
|
184
|
+
<Divider />
|
|
185
|
+
</Box>
|
|
186
|
+
<ReportsReviewTable
|
|
187
|
+
commentId={item.id}
|
|
188
|
+
items={orderBy(reports, ['resolved', 'createdAt'], ['DESC', 'DESC'])}
|
|
189
|
+
mutation={resolveReportMutation}
|
|
190
|
+
allowedActions={{ canAccessReports, canReviewReports }}
|
|
191
|
+
onBlockButtonsStateChange={handleBlockButtonsStateChange}
|
|
192
|
+
/>
|
|
193
|
+
</ReportsReviewModal>) }
|
|
194
|
+
</Td>
|
|
195
|
+
</Tr>
|
|
196
|
+
);
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
DiscoverTableRow.propTypes = {
|
|
200
|
+
config: PropTypes.object.isRequired,
|
|
201
|
+
item: PropTypes.object.isRequired,
|
|
202
|
+
allowedActions: PropTypes.shape({
|
|
203
|
+
canModerate: PropTypes.bool,
|
|
204
|
+
canAccessReports: PropTypes.bool,
|
|
205
|
+
canReviewReports: PropTypes.bool,
|
|
206
|
+
}),
|
|
207
|
+
onClick: PropTypes.func.isRequired,
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
export default DiscoverTableRow;
|
|
@@ -8,21 +8,15 @@ import React, { memo, useRef, useMemo } from 'react';
|
|
|
8
8
|
import { connect } from 'react-redux';
|
|
9
9
|
import { bindActionCreators, compose } from 'redux';
|
|
10
10
|
import isEqual from 'react-fast-compare';
|
|
11
|
-
import { useIntl } from 'react-intl';
|
|
12
11
|
import { useQuery } from 'react-query';
|
|
13
12
|
import { useHistory } from 'react-router-dom';
|
|
14
13
|
import { isEmpty } from 'lodash';
|
|
15
|
-
import { Badge } from '@strapi/design-system/Badge';
|
|
16
|
-
import { BaseCheckbox } from '@strapi/design-system/BaseCheckbox';
|
|
17
14
|
import { Box } from '@strapi/design-system/Box';
|
|
18
|
-
import { Flex } from '@strapi/design-system/Flex';
|
|
19
|
-
import { IconButton } from '@strapi/design-system/IconButton';
|
|
20
15
|
import { Layout, HeaderLayout, ActionLayout, ContentLayout } from '@strapi/design-system/Layout';
|
|
21
16
|
import { Table, Thead, Tbody, Tr, Th, Td } from '@strapi/design-system/Table';
|
|
22
17
|
import { Typography } from '@strapi/design-system/Typography';
|
|
23
18
|
import { VisuallyHidden } from '@strapi/design-system/VisuallyHidden';
|
|
24
19
|
import { useNotifyAT } from '@strapi/design-system/LiveRegions';
|
|
25
|
-
import { Eye } from '@strapi/icons';
|
|
26
20
|
|
|
27
21
|
import {
|
|
28
22
|
EmptyStateLayout,
|
|
@@ -40,12 +34,11 @@ import pluginPermissions from '../../permissions';
|
|
|
40
34
|
|
|
41
35
|
import getUrl from '../../utils/getUrl';
|
|
42
36
|
import Nav from '../../components/Nav';
|
|
43
|
-
import { COMMENT_STATUS } from '../../utils/constants';
|
|
44
37
|
import TablePagination from '../../components/TablePagination';
|
|
45
38
|
import filtersSchema from './utils/filtersSchema';
|
|
46
39
|
import TableFilters from '../../components/TableFilters';
|
|
47
|
-
import
|
|
48
|
-
import
|
|
40
|
+
import makeAppView from '../App/reducer/selectors';
|
|
41
|
+
import DiscoverTableRow from './components/DiscoverTableRow';
|
|
49
42
|
|
|
50
43
|
const Discover = ({ config }) => {
|
|
51
44
|
useFocusWhenNavigate();
|
|
@@ -55,21 +48,23 @@ const Discover = ({ config }) => {
|
|
|
55
48
|
const { trackUsage } = useTracking();
|
|
56
49
|
const trackUsageRef = useRef(trackUsage);
|
|
57
50
|
const toggleNotification = useNotification();
|
|
58
|
-
const { formatDate } = useIntl();
|
|
59
51
|
const [{ query: queryParams }] = useQueryParams();
|
|
60
52
|
const _q = queryParams?._q || '';
|
|
61
53
|
|
|
62
|
-
const viewPermissions = useMemo(() => {
|
|
63
|
-
|
|
64
|
-
|
|
54
|
+
const viewPermissions = useMemo(() => ({
|
|
55
|
+
access: pluginPermissions.access,
|
|
56
|
+
moderate: pluginPermissions.moderate,
|
|
57
|
+
accessReports: pluginPermissions.reports,
|
|
58
|
+
reviewReports: pluginPermissions.reportsReview,
|
|
59
|
+
}), []);
|
|
65
60
|
|
|
66
61
|
const {
|
|
67
62
|
isLoading: isLoadingForPermissions,
|
|
68
|
-
allowedActions: {
|
|
63
|
+
allowedActions: { canAccess, canModerate, canAccessReports, canReviewReports },
|
|
69
64
|
} = useRBAC(viewPermissions);
|
|
70
65
|
|
|
71
66
|
const { isLoading: isLoadingForData, data: { result, pagination = {} }, isFetching } = useQuery(
|
|
72
|
-
['get-
|
|
67
|
+
['get-data', queryParams, canAccess],
|
|
73
68
|
() => fetchData(queryParams, toggleNotification),
|
|
74
69
|
{
|
|
75
70
|
initialData: { },
|
|
@@ -94,97 +89,69 @@ const Discover = ({ config }) => {
|
|
|
94
89
|
},
|
|
95
90
|
};
|
|
96
91
|
|
|
97
|
-
const renderStatus = ({ blocked, blockedThread }) => {
|
|
98
|
-
const status = blocked || blockedThread ? COMMENT_STATUS.BLOCKED : COMMENT_STATUS.OPEN;
|
|
99
|
-
let color = 'secondary';
|
|
100
|
-
switch (status) {
|
|
101
|
-
case COMMENT_STATUS.OPEN:
|
|
102
|
-
color = 'success';
|
|
103
|
-
break;
|
|
104
|
-
case COMMENT_STATUS.BLOCKED:
|
|
105
|
-
color = 'danger';
|
|
106
|
-
};
|
|
107
|
-
return (<Badge backgroundColor={`${color}100`} textColor={`${color}600`}>{ getMessage(`page.discover.table.header.status.${status}`, status) }</Badge>);
|
|
108
|
-
};
|
|
109
|
-
|
|
110
92
|
const emptyContent = _q ? 'search' : 'comments';
|
|
111
93
|
|
|
112
94
|
const COL_COUNT = 7;
|
|
113
95
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
<
|
|
130
|
-
<
|
|
131
|
-
<
|
|
132
|
-
<
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
<
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
<
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
<
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
<
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
<
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
<
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
</Td>
|
|
174
|
-
<Td>
|
|
175
|
-
<Flex direction="column" alignItems="flex-end">
|
|
176
|
-
<IconButton onClick={() => handleClickDisplay(entry.id)} label={ getMessage('page.discover.table.action.display') } icon={<Eye />} />
|
|
177
|
-
</Flex>
|
|
178
|
-
</Td>
|
|
179
|
-
</Tr>)}
|
|
180
|
-
</Tbody>
|
|
181
|
-
</Table>
|
|
182
|
-
<TablePagination pagination={{ pageCount: pagination?.pageCount || 1 }} />
|
|
183
|
-
</>) : (<EmptyStateLayout content={emptyLayout[emptyContent]} />) }
|
|
184
|
-
</ContentLayout>
|
|
185
|
-
</>) }
|
|
186
|
-
</Layout>
|
|
187
|
-
</Box>;
|
|
96
|
+
if (canAccess) {
|
|
97
|
+
return <Box background="neutral100">
|
|
98
|
+
<Layout>
|
|
99
|
+
{isLoading || isLoadingForPermissions ? (<LoadingIndicatorPage />) : (
|
|
100
|
+
<>
|
|
101
|
+
<HeaderLayout title={ getMessage('page.discover.header') } subtitle={ `${total} ${ getMessage('page.discover.header.count')}` } as="h2" />
|
|
102
|
+
<ActionLayout
|
|
103
|
+
startActions={<>
|
|
104
|
+
<SearchURLQuery
|
|
105
|
+
label={ getMessage('search.label', 'Search', false) }
|
|
106
|
+
/>
|
|
107
|
+
<TableFilters displayedFilters={filtersSchema} />
|
|
108
|
+
</>} />
|
|
109
|
+
<ContentLayout>
|
|
110
|
+
{ !isEmpty(result) ? (<>
|
|
111
|
+
<Table colCount={COL_COUNT} rowCount={result.length}>
|
|
112
|
+
<Thead>
|
|
113
|
+
<Tr>
|
|
114
|
+
<Th>
|
|
115
|
+
<Typography variant="sigma">{ getMessage('page.discover.table.header.id') }</Typography>
|
|
116
|
+
</Th>
|
|
117
|
+
<Th>
|
|
118
|
+
<Typography variant="sigma">{ getMessage('page.discover.table.header.message') }</Typography>
|
|
119
|
+
</Th>
|
|
120
|
+
<Th>
|
|
121
|
+
<Typography variant="sigma">{ getMessage('page.discover.table.header.thread') }</Typography>
|
|
122
|
+
</Th>
|
|
123
|
+
<Th>
|
|
124
|
+
<Typography variant="sigma">{ getMessage('page.discover.table.header.entry') }</Typography>
|
|
125
|
+
</Th>
|
|
126
|
+
<Th>
|
|
127
|
+
<Typography variant="sigma">{ getMessage('page.discover.table.header.lastUpdate') }</Typography>
|
|
128
|
+
</Th>
|
|
129
|
+
<Th>
|
|
130
|
+
<Typography variant="sigma">{ getMessage('page.discover.table.header.status') }</Typography>
|
|
131
|
+
</Th>
|
|
132
|
+
<Th>
|
|
133
|
+
<VisuallyHidden>{ getMessage('page.discover.table.header.actions') }</VisuallyHidden>
|
|
134
|
+
</Th>
|
|
135
|
+
</Tr>
|
|
136
|
+
</Thead>
|
|
137
|
+
<Tbody>
|
|
138
|
+
{result.map(entry => (<DiscoverTableRow
|
|
139
|
+
key={`comment-${entry.id}`}
|
|
140
|
+
config={config}
|
|
141
|
+
item={entry}
|
|
142
|
+
allowedActions={{ canModerate, canAccessReports, canReviewReports }}
|
|
143
|
+
onClick={() => handleClickDisplay(entry.id)}
|
|
144
|
+
/>))}
|
|
145
|
+
</Tbody>
|
|
146
|
+
</Table>
|
|
147
|
+
<TablePagination pagination={{ pageCount: pagination?.pageCount || 1 }} />
|
|
148
|
+
</>) : (<EmptyStateLayout content={emptyLayout[emptyContent]} />) }
|
|
149
|
+
</ContentLayout>
|
|
150
|
+
</>) }
|
|
151
|
+
</Layout>
|
|
152
|
+
</Box>;
|
|
153
|
+
}
|
|
154
|
+
return null;
|
|
188
155
|
}
|
|
189
156
|
|
|
190
157
|
const mapStateToProps = makeAppView();
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { stringify } from 'qs';
|
|
2
|
-
import { isEmpty } from 'lodash';
|
|
3
2
|
|
|
4
|
-
import { getApiURL, axiosInstance } from '../../../utils';
|
|
3
|
+
import { getApiURL, axiosInstance, handleAPIError } from '../../../utils';
|
|
5
4
|
|
|
6
5
|
// eslint-disable-next-line import/prefer-default-export
|
|
7
6
|
export const fetchData = async (queryParams, toggleNotification) => {
|
|
@@ -10,11 +9,6 @@ export const fetchData = async (queryParams, toggleNotification) => {
|
|
|
10
9
|
|
|
11
10
|
return data;
|
|
12
11
|
} catch (err) {
|
|
13
|
-
toggleNotification
|
|
14
|
-
type: 'warning',
|
|
15
|
-
message: { id: 'notification.error' },
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
throw new Error('error');
|
|
12
|
+
handleAPIError(err, toggleNotification);
|
|
19
13
|
}
|
|
20
14
|
};
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { stringify } from 'qs';
|
|
2
|
+
import { isEmpty } from 'lodash';
|
|
3
|
+
|
|
4
|
+
import { getApiURL, axiosInstance, handleAPIError } from '../../utils';
|
|
2
5
|
|
|
3
6
|
// eslint-disable-next-line import/prefer-default-export
|
|
4
|
-
export const fetchDetailsData = async (id, toggleNotification) => {
|
|
7
|
+
export const fetchDetailsData = async (id, queryParams, toggleNotification) => {
|
|
5
8
|
try {
|
|
6
|
-
const
|
|
9
|
+
const stringifiedProps = !isEmpty(queryParams) ? `?${stringify(queryParams, { encode: false })}` : '';
|
|
10
|
+
const { data } = await axiosInstance.get(getApiURL(`moderate/single/${id}${stringifiedProps}`));
|
|
7
11
|
|
|
8
12
|
return data;
|
|
9
13
|
} catch (err) {
|
|
@@ -28,6 +32,14 @@ export const unblockItem = id => {
|
|
|
28
32
|
return axiosInstance.patch(getApiURL(`moderate/single/${id}/unblock`));
|
|
29
33
|
};
|
|
30
34
|
|
|
35
|
+
export const approveItem = id => {
|
|
36
|
+
return axiosInstance.patch(getApiURL(`moderate/single/${id}/approve`));
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const rejectItem = id => {
|
|
40
|
+
return axiosInstance.patch(getApiURL(`moderate/single/${id}/reject`));
|
|
41
|
+
};
|
|
42
|
+
|
|
31
43
|
export const blockItemThread = id => {
|
|
32
44
|
return axiosInstance.patch(getApiURL(`moderate/thread/${id}/block`));
|
|
33
45
|
};
|
package/admin/src/permissions.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
+
const permissions = require('./../../permissions');
|
|
2
|
+
|
|
1
3
|
const pluginPermissions = {
|
|
2
4
|
access: [
|
|
3
|
-
{ action:
|
|
4
|
-
{ action:
|
|
5
|
-
{ action: 'plugin::comments.
|
|
6
|
-
{ action:
|
|
5
|
+
{ action: permissions.render(permissions.comments.read), subject: null },
|
|
6
|
+
{ action: permissions.render(permissions.reports.read), subject: null },
|
|
7
|
+
// { action: 'plugin::comments.discover.read', subject: null },
|
|
8
|
+
{ action: permissions.render(permissions.settings.read), subject: null },
|
|
7
9
|
],
|
|
8
|
-
discover: [{ action: 'plugin::comments.read', subject: null }],
|
|
9
|
-
moderate: [{ action:
|
|
10
|
-
reports: [{ action:
|
|
11
|
-
reportsReview: [{ action:
|
|
10
|
+
// discover: [{ action: 'plugin::comments.discover.read', subject: null }],
|
|
11
|
+
moderate: [{ action: permissions.render(permissions.comments.moderate), subject: null }],
|
|
12
|
+
reports: [{ action: permissions.render(permissions.reports.read), subject: null }],
|
|
13
|
+
reportsReview: [{ action: permissions.render(permissions.reports.action), subject: null }],
|
|
12
14
|
};
|
|
13
15
|
|
|
14
16
|
export default pluginPermissions;
|
|
@@ -7,23 +7,29 @@
|
|
|
7
7
|
"nav.item.discover": "Discover",
|
|
8
8
|
"nav.item.reports": "Reports",
|
|
9
9
|
"nav.item.settings": "Settings",
|
|
10
|
+
"page.common.item.status.OPEN": "OPEN",
|
|
11
|
+
"page.common.item.status.BLOCKED": "BLOCKED",
|
|
12
|
+
"page.common.item.status.APPROVED": "APPROVED",
|
|
13
|
+
"page.common.item.status.TO_REVIEW": "TO REVIEW ({count, number})",
|
|
14
|
+
"page.common.item.status.REJECTED": "REJECTED",
|
|
15
|
+
"page.common.item.status.REMOVED": "REMOVED BY AUTHOR",
|
|
10
16
|
"page.updates.header": "Recent updates",
|
|
11
17
|
"page.discover.header": "Discover threads",
|
|
12
18
|
"page.discover.header.count": "entries found",
|
|
19
|
+
"page.discover.table.header.id": "ID",
|
|
13
20
|
"page.discover.table.header.message": "Message",
|
|
14
21
|
"page.discover.table.header.thread": "Thread of",
|
|
15
22
|
"page.discover.table.header.entry": "Entry",
|
|
16
23
|
"page.discover.table.header.lastUpdate": "Last update",
|
|
17
24
|
"page.discover.table.header.status": "Status",
|
|
18
|
-
"page.discover.table.header.
|
|
19
|
-
"page.discover.table.
|
|
20
|
-
"page.discover.table.header.status.TO_REVIEW": "TO REVIEW",
|
|
21
|
-
"page.discover.table.header.actions": "Status",
|
|
25
|
+
"page.discover.table.header.actions": "Actions",
|
|
26
|
+
"page.discover.table.cell.thread": "Comment #{id}",
|
|
22
27
|
"page.discover.table.action.display": "Display",
|
|
23
28
|
"page.discover.table.action.reports": "Reports",
|
|
24
29
|
"page.discover.table.empty": "You don't have any comments yet.",
|
|
25
30
|
"page.discover.table.empty.search": "No comments match the search.",
|
|
26
31
|
"page.discover.table.filters": "Filtering",
|
|
32
|
+
"page.discover.table.reports.review": "Review reports",
|
|
27
33
|
"page.details.header": "Discussion panel",
|
|
28
34
|
"page.details.header.description": "Drilldown and manage discussion related to the entity",
|
|
29
35
|
"page.details.panel.discussion": "Discussion",
|
|
@@ -51,6 +57,7 @@
|
|
|
51
57
|
"page.details.panel.status.open": "OPEN",
|
|
52
58
|
"page.details.panel.status.blocked": "BLOCKED",
|
|
53
59
|
"page.details.panel.status.pending": "PENDING",
|
|
60
|
+
"page.details.panel.loading": "Fetching entity...",
|
|
54
61
|
"page.details.actions.comment.block": "Block comment",
|
|
55
62
|
"page.details.actions.comment.block.confirmation.header": "Moderation: Block comment",
|
|
56
63
|
"page.details.actions.comment.block.confirmation.description": "Do you really want to block this comment?",
|
|
@@ -66,6 +73,12 @@
|
|
|
66
73
|
"page.details.actions.thread.block.confirmation.success": "Thread has been blocked",
|
|
67
74
|
"page.details.actions.thread.unblock": "Unblock thread",
|
|
68
75
|
"page.details.actions.thread.unblock.confirmation.success": "Thread has been unblocked",
|
|
76
|
+
"page.details.actions.comment.approve.confirmation.success": "Comment has been approved",
|
|
77
|
+
"page.details.actions.comment.reject.confirmation.success": "Comment has been rejected",
|
|
78
|
+
"page.details.filters.label": "View",
|
|
79
|
+
"page.details.filters.removed.visibility": "Comments visibility",
|
|
80
|
+
"page.details.filters.removed.show": "Show removed",
|
|
81
|
+
"page.details.filters.removed.hide": "Hide removed",
|
|
69
82
|
"page.coming.soon": "Wait for what is coming soon...",
|
|
70
83
|
"compontents.confirmation.dialog.header": "Confirmation",
|
|
71
84
|
"compontents.confirmation.dialog.description": "Do you really want to perform this action?",
|
|
@@ -16,13 +16,10 @@ export const REPORT_REASON = {
|
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
export const COMMENT_STATUS = {
|
|
19
|
+
'UNKNOWN': 'UNKNOWN',
|
|
19
20
|
'BLOCKED': 'BLOCKED',
|
|
20
21
|
'OPEN': 'OPEN',
|
|
22
|
+
'REMOVED': 'REMOVED',
|
|
21
23
|
'TO_REVIEW': 'TO_REVIEW',
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
export const REGEX = {
|
|
25
|
-
uid: new RegExp(/^(?<type>[a-z]+)\:{2}(?<api>[a-z]+)\.{1}(?<contentType>[a-z]+)$/gmi),
|
|
26
|
-
relatedUid: new RegExp(/^(?<uid>[a-z]+\:{2}[a-z]+\.[a-z]+)\:{1}(?<id>[0-9]+)$/gmi),
|
|
27
|
-
email: new RegExp(/\S+@\S+\.\S+/),
|
|
24
|
+
...APPROVAL_STATUS,
|
|
28
25
|
};
|
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
|
|
2
2
|
import { useIntl } from 'react-intl';
|
|
3
|
+
import { isString } from 'lodash';
|
|
3
4
|
|
|
4
5
|
import pluginId from '../pluginId';
|
|
5
6
|
|
|
6
|
-
const getMessage = (
|
|
7
|
+
const getMessage = (input, defaultMessage = '', inPluginScope = true) => {
|
|
7
8
|
const { formatMessage } = useIntl();
|
|
9
|
+
let formattedId = ''
|
|
10
|
+
if (isString(input)) {
|
|
11
|
+
formattedId = input;
|
|
12
|
+
} else {
|
|
13
|
+
formattedId = input?.id;
|
|
14
|
+
}
|
|
8
15
|
return formatMessage({
|
|
9
|
-
id: `${inPluginScope ? pluginId : 'app.
|
|
16
|
+
id: `${inPluginScope ? pluginId : 'app.components'}.${formattedId}`,
|
|
10
17
|
defaultMessage,
|
|
11
|
-
})
|
|
18
|
+
}, input?.props || undefined)
|
|
12
19
|
};
|
|
13
20
|
|
|
14
21
|
export default getMessage;
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const getRelatedGroups = related => related.split(REGEX.relatedUid).filter(s => s && s.length > 0);
|
|
1
|
+
const getRelatedGroups = (related, config) => related.split(config.regex.relatedUid).filter(s => s && s.length > 0);
|
|
4
2
|
|
|
5
3
|
export default getRelatedGroups;
|