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.
Files changed (53) hide show
  1. package/README.md +31 -5
  2. package/admin/src/components/DiscussionThread/index.js +10 -2
  3. package/admin/src/components/DiscussionThreadItem/index.js +25 -6
  4. package/admin/src/components/DiscussionThreadItem/styles.js +1 -0
  5. package/admin/src/components/DiscussionThreadItemActions/index.js +69 -56
  6. package/admin/src/components/DiscussionThreadItemActions/styles.js +0 -4
  7. package/admin/src/components/DiscussionThreadItemApprovalFlowActions/index.js +81 -0
  8. package/admin/src/components/DiscussionThreadItemApprovalFlowActions/styles.js +6 -0
  9. package/admin/src/components/DiscussionThreadItemWarnings/index.js +26 -16
  10. package/admin/src/components/Nav/index.js +5 -1
  11. package/admin/src/components/ReportsReviewModal/index.js +5 -2
  12. package/admin/src/components/ReportsReviewTable/index.js +69 -57
  13. package/admin/src/components/icons/index.js +6 -0
  14. package/admin/src/components/icons/lock.js +22 -0
  15. package/admin/src/components/icons/review.js +18 -0
  16. package/admin/src/components/icons/unlock.js +20 -0
  17. package/admin/src/index.js +1 -1
  18. package/admin/src/pages/App/index.js +20 -9
  19. package/admin/src/pages/App/reducer/index.js +0 -1
  20. package/admin/src/pages/App/reducer/selectors.js +1 -1
  21. package/admin/src/pages/App/utils/api.js +2 -10
  22. package/admin/src/pages/Details/components/DetailsEntity/index.js +66 -0
  23. package/admin/src/pages/Details/components/DetailsFilters/index.js +37 -0
  24. package/admin/src/pages/Details/components/DetailsFilters/styles.js +8 -0
  25. package/admin/src/pages/Details/index.js +75 -42
  26. package/admin/src/pages/Discover/components/DiscoverTableRow/index.js +210 -0
  27. package/admin/src/pages/Discover/components/DiscoverTableRow/styles.js +8 -0
  28. package/admin/src/pages/Discover/index.js +69 -102
  29. package/admin/src/pages/Discover/utils/api.js +2 -8
  30. package/admin/src/pages/{Details/utils → utils}/api.js +15 -3
  31. package/admin/src/permissions.js +10 -8
  32. package/admin/src/translations/en.json +17 -4
  33. package/admin/src/utils/constants.js +3 -6
  34. package/admin/src/utils/getMessage.js +10 -3
  35. package/admin/src/utils/getRelatedGroups.js +1 -3
  36. package/admin/src/utils/handleAPIError.js +1 -1
  37. package/admin/src/utils/index.js +4 -2
  38. package/admin/src/utils/renderEntryTitle.js +14 -0
  39. package/admin/src/utils/resolveCommentStatus.js +30 -0
  40. package/admin/src/utils/resolveCommentStatusColor.js +22 -0
  41. package/package.json +7 -3
  42. package/permissions.js +19 -0
  43. package/server/bootstrap.js +38 -2
  44. package/server/config/index.js +4 -3
  45. package/server/controllers/admin.js +3 -3
  46. package/server/controllers/client.js +11 -2
  47. package/server/services/admin.js +69 -13
  48. package/server/services/client.js +23 -15
  49. package/server/services/common.js +40 -69
  50. package/server/services/utils/functions.js +7 -14
  51. package/server/utils/constants.js +3 -3
  52. package/strapi-server.js +33 -0
  53. 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;
@@ -0,0 +1,8 @@
1
+ import styled from 'styled-components';
2
+ import { Link } from '@strapi/design-system/Link';
3
+
4
+ export const TableLink = styled(Link)`
5
+ display: block;
6
+ text-overflow: ellipsis;
7
+ overflow: hidden;
8
+ `
@@ -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 appReducer from '../App/reducer';
48
- import makeAppView, { selectConfig } from '../App/reducer/selectors';
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
- return { view: pluginPermissions.view };
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: { canView },
63
+ allowedActions: { canAccess, canModerate, canAccessReports, canReviewReports },
69
64
  } = useRBAC(viewPermissions);
70
65
 
71
66
  const { isLoading: isLoadingForData, data: { result, pagination = {} }, isFetching } = useQuery(
72
- ['get-providers', queryParams],
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
- return <Box background="neutral100">
115
- <Layout sideNav={<Nav />}>
116
- {isLoading || isLoadingForPermissions ? (<LoadingIndicatorPage />) : (
117
- <>
118
- <HeaderLayout title={ getMessage('page.discover.header') } subtitle={ `${total} ${ getMessage('page.discover.header.count')}` } as="h2" />
119
- <ActionLayout
120
- startActions={<>
121
- <SearchURLQuery
122
- label={ getMessage('search.label', 'Search', false) }
123
- />
124
- <TableFilters displayedFilters={filtersSchema} />
125
- </>} />
126
- <ContentLayout>
127
- { !isEmpty(result) ? (<>
128
- <Table colCount={COL_COUNT} rowCount={result.length}>
129
- <Thead>
130
- <Tr>
131
- <Th>
132
- <BaseCheckbox aria-label="Select all entries" />
133
- </Th>
134
- <Th>
135
- <Typography variant="sigma">{ getMessage('page.discover.table.header.message') }</Typography>
136
- </Th>
137
- <Th>
138
- <Typography variant="sigma">{ getMessage('page.discover.table.header.thread') }</Typography>
139
- </Th>
140
- <Th>
141
- <Typography variant="sigma">{ getMessage('page.discover.table.header.entry') }</Typography>
142
- </Th>
143
- <Th>
144
- <Typography variant="sigma">{ getMessage('page.discover.table.header.lastUpdate') }</Typography>
145
- </Th>
146
- <Th>
147
- <Typography variant="sigma">{ getMessage('page.discover.table.header.status') }</Typography>
148
- </Th>
149
- <Th>
150
- <VisuallyHidden>{ getMessage('page.discover.table.header.actions') }</VisuallyHidden>
151
- </Th>
152
- </Tr>
153
- </Thead>
154
- <Tbody>
155
- {result.map(entry => <Tr key={entry.id}>
156
- <Td>
157
- <BaseCheckbox aria-label={`Select ${entry.content}`} />
158
- </Td>
159
- <Td style={{ maxWidth: '40vw' }}>
160
- <Typography textColor="neutral800" ellipsis>{entry.content}</Typography>
161
- </Td>
162
- <Td>
163
- <Typography textColor="neutral800">{entry.threadOf?.id || '-'}</Typography>
164
- </Td>
165
- <Td>
166
- <Typography textColor="neutral800">{entry.related?.id}</Typography>
167
- </Td>
168
- <Td>
169
- <Typography textColor="neutral800">{ formatDate(entry.updatedAt || entry.createdAt, { dateStyle: 'long', timeStyle: 'short' }) }</Typography>
170
- </Td>
171
- <Td>
172
- <Typography textColor="neutral800">{ renderStatus(entry) }</Typography>
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 { getApiURL, axiosInstance, handleAPIError } from '../../../utils';
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 { data } = await axiosInstance.get(getApiURL(`moderate/single/${id}`));
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
  };
@@ -1,14 +1,16 @@
1
+ const permissions = require('./../../permissions');
2
+
1
3
  const pluginPermissions = {
2
4
  access: [
3
- { action: 'plugin::comments.read', subject: null },
4
- { action: 'plugin::comments.reports.read', subject: null },
5
- { action: 'plugin::comments.discovery.read', subject: null },
6
- { action: 'plugin::comments.settings.read', subject: null },
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: 'plugin::comments.moderate', subject: null }],
10
- reports: [{ action: 'plugin::comments.reports.read', subject: null }],
11
- reportsReview: [{ action: 'plugin::comments.reports.action', subject: null }],
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.status.OPEN": "OPEN",
19
- "page.discover.table.header.status.BLOCKED": "BLOCKED",
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 = (id, defaultMessage, inPluginScope = true) => {
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.component'}.${id}`,
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
- import { REGEX } from "./constants";
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;
@@ -2,7 +2,7 @@
2
2
  const handleAPIError = (err = null, toggleNotification = null) => {
3
3
  toggleNotification({
4
4
  type: 'warning',
5
- message: { id: 'notification.error' },
5
+ message: 'app.components.notification.error',
6
6
  });
7
7
 
8
8
  if (err) {