strapi-plugin-comments 1.0.2-beta.1 → 2.0.0-beta.2
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/.circleci/config.yml +2 -2
- package/README.md +71 -95
- package/admin/src/components/ConfirmationDialog/index.js +56 -0
- package/admin/src/components/DiscussionStatus/index.js +33 -0
- package/admin/src/components/DiscussionThread/index.js +58 -0
- package/admin/src/components/DiscussionThreadItem/index.js +87 -0
- package/admin/src/components/DiscussionThreadItem/styles.js +87 -0
- package/admin/src/components/DiscussionThreadItemActions/index.js +176 -0
- package/admin/src/components/DiscussionThreadItemActions/styles.js +22 -0
- package/admin/src/components/DiscussionThreadItemMeta/index.js +31 -0
- package/admin/src/components/DiscussionThreadItemWarnings/index.js +137 -0
- package/admin/src/components/DiscussionThreadItemWarnings/styles.js +7 -0
- package/admin/src/components/EntityDetails/index.js +53 -0
- package/admin/src/{containers → components}/Initializer/index.js +4 -4
- package/admin/src/components/LoadingIndicatorOverlay/index.js +15 -0
- package/admin/src/components/LoadingIndicatorOverlay/styles.js +20 -0
- package/admin/src/components/Nav/index.js +45 -0
- package/admin/src/components/PluginIcon/index.js +12 -0
- package/admin/src/components/ReportsReviewModal/index.js +45 -0
- package/admin/src/components/ReportsReviewTable/index.js +142 -0
- package/admin/src/components/TableFilters/index.js +53 -0
- package/admin/src/components/TablePagination/index.js +35 -0
- package/admin/src/components/icons/comments.js +9 -0
- package/admin/src/index.js +58 -42
- package/admin/src/pages/App/index.js +60 -0
- package/admin/src/pages/App/reducer/actions.js +8 -0
- package/admin/src/pages/App/reducer/constants.js +4 -0
- package/admin/src/pages/App/reducer/index.js +27 -0
- package/admin/src/pages/App/reducer/init.js +7 -0
- package/admin/src/pages/App/reducer/selectors.js +35 -0
- package/admin/src/pages/App/utils/api.js +20 -0
- package/admin/src/pages/ComingSoonPage/index.js +25 -0
- package/admin/src/pages/Details/index.js +109 -0
- package/admin/src/pages/Details/utils/api.js +41 -0
- package/admin/src/pages/Discover/index.js +203 -0
- package/admin/src/pages/Discover/utils/api.js +20 -0
- package/admin/src/pages/Discover/utils/filtersSchema.js +9 -0
- package/admin/src/permissions.js +13 -33
- package/admin/src/pluginId.js +2 -4
- package/admin/src/reducers.js +8 -0
- package/admin/src/translations/en.json +72 -33
- package/admin/src/translations/fr.json +1 -0
- package/admin/src/utils/axiosInstance.js +35 -0
- package/admin/src/utils/constants.js +28 -0
- package/admin/src/utils/getApiUrl.js +5 -0
- package/admin/src/utils/getMessage.js +14 -0
- package/admin/src/utils/getRelatedGroups.js +5 -0
- package/admin/src/utils/getUrl.js +5 -0
- package/admin/src/utils/handleAPIError.js +15 -0
- package/admin/src/utils/index.js +6 -0
- package/codecov.yml +2 -1
- package/content-types/comment.js +97 -0
- package/content-types/index.js +11 -0
- package/content-types/report.js +54 -0
- package/package.json +11 -25
- package/server/bootstrap.js +5 -0
- package/server/config/index.js +10 -0
- package/server/controllers/admin.js +134 -0
- package/server/controllers/client.js +109 -0
- package/server/controllers/index.js +9 -0
- package/{controllers → server/controllers}/utils/__tests__/functions.test.js +0 -0
- package/{controllers → server/controllers}/utils/functions.js +1 -1
- package/server/destroy.js +5 -0
- package/server/index.js +22 -0
- package/server/register.js +5 -0
- package/server/routes/admin.js +92 -0
- package/server/routes/client.js +52 -0
- package/server/routes/index.js +15 -0
- package/server/services/admin.js +186 -0
- package/server/services/client.js +227 -0
- package/server/services/common.js +191 -0
- package/server/services/index.js +11 -0
- package/{services → server/services}/utils/__tests__/functions.test.js +7 -9
- package/{services → server/services}/utils/functions.js +19 -8
- package/server/utils/constants.js +16 -0
- package/{services → server}/utils/error.js +0 -0
- package/server/utils/functions.js +19 -0
- package/strapi-admin.js +3 -0
- package/strapi-server.js +9 -0
- package/admin/src/assets/images/icon-cross-blue.svg +0 -1
- package/admin/src/assets/images/icon_remove.svg +0 -19
- package/admin/src/assets/images/logo.svg +0 -5
- package/admin/src/components/AbuseReportsPopUp/ItemPreviewContainer.js +0 -8
- package/admin/src/components/AbuseReportsPopUp/ModalFooter.js +0 -31
- package/admin/src/components/AbuseReportsPopUp/TableContainer.js +0 -63
- package/admin/src/components/AbuseReportsPopUp/index.js +0 -148
- package/admin/src/components/Container/index.js +0 -7
- package/admin/src/components/EmptyView/index.js +0 -25
- package/admin/src/components/Filters/index.js +0 -151
- package/admin/src/components/Filters/index.styled.js +0 -82
- package/admin/src/components/Filters/utils.js +0 -21
- package/admin/src/components/Item/CardItem.js +0 -30
- package/admin/src/components/Item/CardWrapper.js +0 -10
- package/admin/src/components/Item/index.js +0 -92
- package/admin/src/components/ItemDetails/CardItem.js +0 -33
- package/admin/src/components/ItemDetails/CardLevelCounter.js +0 -46
- package/admin/src/components/ItemDetails/CardLevelCounterLink.js +0 -24
- package/admin/src/components/ItemDetails/CardLevelWrapper.js +0 -22
- package/admin/src/components/ItemDetails/CardWrapper.js +0 -52
- package/admin/src/components/ItemDetails/index.js +0 -151
- package/admin/src/components/ItemFooter/CardItemAuthor.js +0 -10
- package/admin/src/components/ItemFooter/CardItemRelation.js +0 -18
- package/admin/src/components/ItemFooter/Wrapper.js +0 -20
- package/admin/src/components/ItemFooter/index.js +0 -87
- package/admin/src/components/ItemHeader/CardHeaderBlocked.js +0 -24
- package/admin/src/components/ItemHeader/CardHeaderIndicator.js +0 -30
- package/admin/src/components/ItemHeader/CardHeaderIndicatorBlue.js +0 -11
- package/admin/src/components/ItemHeader/CardHeaderIndicatorRed.js +0 -11
- package/admin/src/components/ItemHeader/CardHeaderIndicatorsContainer.js +0 -18
- package/admin/src/components/ItemHeader/CardHeaderReports.js +0 -40
- package/admin/src/components/ItemHeader/Wrapper.js +0 -16
- package/admin/src/components/ItemHeader/index.js +0 -80
- package/admin/src/components/ItemModeration/Wrapper.js +0 -19
- package/admin/src/components/ItemModeration/index.js +0 -47
- package/admin/src/components/List/Container.js +0 -20
- package/admin/src/components/List/index.js +0 -21
- package/admin/src/components/Search/index.js +0 -83
- package/admin/src/containers/App/Wrapper.js +0 -14
- package/admin/src/containers/App/index.js +0 -47
- package/admin/src/containers/DataManagerProvider/actions.js +0 -11
- package/admin/src/containers/DataManagerProvider/index.js +0 -280
- package/admin/src/containers/DataManagerProvider/init.js +0 -5
- package/admin/src/containers/DataManagerProvider/reducer.js +0 -59
- package/admin/src/containers/DetailsView/Wrapper.js +0 -18
- package/admin/src/containers/DetailsView/index.js +0 -100
- package/admin/src/containers/ListView/FadedWrapper.js +0 -42
- package/admin/src/containers/ListView/Footer.js +0 -53
- package/admin/src/containers/ListView/Wrapper.js +0 -17
- package/admin/src/containers/ListView/components.js +0 -138
- package/admin/src/containers/ListView/index.js +0 -45
- package/admin/src/containers/Panel/actions.js +0 -5
- package/admin/src/containers/Panel/index.js +0 -191
- package/admin/src/containers/Panel/index.styled.js +0 -6
- package/admin/src/containers/Panel/reducer.js +0 -44
- package/admin/src/contexts/DataManagerContext.js +0 -5
- package/admin/src/hooks/useDataManager.js +0 -6
- package/admin/src/lifecycles.js +0 -3
- package/admin/src/translations/index.js +0 -7
- package/admin/src/utils/getTrad.js +0 -5
- package/admin/src/utils/getTradId.js +0 -5
- package/config/functions/bootstrap.js +0 -48
- package/config/routes.json +0 -116
- package/config/schema.graphql.js +0 -270
- package/controllers/comments.js +0 -211
- package/models/comment.js +0 -8
- package/models/comment.settings.json +0 -98
- package/models/report.js +0 -8
- package/models/report.settings.json +0 -49
- package/services/comments.js +0 -486
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Entity Details
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { useState, useEffect } from 'react';
|
|
8
|
+
import { useIntl } from 'react-intl';
|
|
9
|
+
import PropTypes from 'prop-types';
|
|
10
|
+
import { isEmpty } from 'lodash';
|
|
11
|
+
import { Badge } from '@strapi/design-system/Badge';
|
|
12
|
+
import { Button } from '@strapi/design-system/Button';
|
|
13
|
+
import { BaseCheckbox } from '@strapi/design-system/BaseCheckbox';
|
|
14
|
+
import { Flex } from '@strapi/design-system/Flex';
|
|
15
|
+
import { Table, Thead, Tbody, TFooter, Tr, Th, Td } from '@strapi/design-system/Table';
|
|
16
|
+
import { Typography } from '@strapi/design-system/Typography';
|
|
17
|
+
import { VisuallyHidden } from '@strapi/design-system/VisuallyHidden';
|
|
18
|
+
import { Check } from '@strapi/icons';
|
|
19
|
+
import { useOverlayBlocker } from '@strapi/helper-plugin';
|
|
20
|
+
import { getMessage } from '../../utils';
|
|
21
|
+
import { REPORT_REASON, REPORT_STATUS } from '../../utils/constants';
|
|
22
|
+
|
|
23
|
+
const ReportsReviewTable = ({
|
|
24
|
+
commentId,
|
|
25
|
+
items,
|
|
26
|
+
mutation
|
|
27
|
+
}) => {
|
|
28
|
+
|
|
29
|
+
const [storedItems, setStoredItems] = useState([]);
|
|
30
|
+
|
|
31
|
+
const { formatDate } = useIntl();
|
|
32
|
+
|
|
33
|
+
const { lockApp } = useOverlayBlocker();
|
|
34
|
+
|
|
35
|
+
const renderStatus = resolved => {
|
|
36
|
+
const status = resolved ? REPORT_STATUS.RESOLVED : REPORT_STATUS.OPEN;
|
|
37
|
+
let color = 'alternative';
|
|
38
|
+
switch (status) {
|
|
39
|
+
case REPORT_STATUS.RESOLVED:
|
|
40
|
+
color = 'success';
|
|
41
|
+
break;
|
|
42
|
+
case REPORT_STATUS.OPEN:
|
|
43
|
+
color = 'danger';
|
|
44
|
+
};
|
|
45
|
+
return (<Badge backgroundColor={`${color}100`} textColor={`${color}600`}>{ getMessage(`page.details.panel.discussion.warnings.reports.dialog.status.${status}`, status) }</Badge>);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const renderReason = reason => {
|
|
49
|
+
let color = 'neutral';
|
|
50
|
+
switch (reason) {
|
|
51
|
+
case REPORT_REASON.DISCRIMINATION:
|
|
52
|
+
color = 'danger';
|
|
53
|
+
break;
|
|
54
|
+
case REPORT_REASON.BAD_LANGUAGE:
|
|
55
|
+
color = 'warning';
|
|
56
|
+
};
|
|
57
|
+
return (<Badge backgroundColor={`${color}100`} textColor={`${color}600`}>{ getMessage(`page.details.panel.discussion.warnings.reports.dialog.reason.${reason}`, reason) }</Badge>);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const handleClickResolve = async (reportId) => {
|
|
61
|
+
lockApp();
|
|
62
|
+
const item = await mutation.mutateAsync({
|
|
63
|
+
id: commentId,
|
|
64
|
+
reportId,
|
|
65
|
+
});
|
|
66
|
+
console.log(item);
|
|
67
|
+
if (item) {
|
|
68
|
+
setStoredItems(storedItems.map(_ => ({
|
|
69
|
+
..._,
|
|
70
|
+
resolved: reportId === _.id ? true : _.resolved,
|
|
71
|
+
})));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
setStoredItems(items);
|
|
77
|
+
}, []);
|
|
78
|
+
|
|
79
|
+
if (isEmpty(storedItems)) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const COL_COUNT = 6;
|
|
84
|
+
|
|
85
|
+
return (<Table colCount={COL_COUNT} rowCount={storedItems.length}>
|
|
86
|
+
<Thead>
|
|
87
|
+
<Tr>
|
|
88
|
+
<Th>
|
|
89
|
+
<BaseCheckbox aria-label="Select all entries" />
|
|
90
|
+
</Th>
|
|
91
|
+
<Th>
|
|
92
|
+
<Typography variant="sigma">{ getMessage('page.details.panel.discussion.warnings.reports.dialog.reason') }</Typography>
|
|
93
|
+
</Th>
|
|
94
|
+
<Th>
|
|
95
|
+
<Typography variant="sigma">{ getMessage('page.details.panel.discussion.warnings.reports.dialog.content') }</Typography>
|
|
96
|
+
</Th>
|
|
97
|
+
<Th>
|
|
98
|
+
<Typography variant="sigma">{ getMessage('page.details.panel.discussion.warnings.reports.dialog.createdAt') }</Typography>
|
|
99
|
+
</Th>
|
|
100
|
+
<Th>
|
|
101
|
+
<Typography variant="sigma">{ getMessage('page.details.panel.discussion.warnings.reports.dialog.status') }</Typography>
|
|
102
|
+
</Th>
|
|
103
|
+
<Th>
|
|
104
|
+
<VisuallyHidden>{ getMessage('page.details.panel.discussion.warnings.reports.dialog.actions') }</VisuallyHidden>
|
|
105
|
+
</Th>
|
|
106
|
+
</Tr>
|
|
107
|
+
</Thead>
|
|
108
|
+
<Tbody>
|
|
109
|
+
{storedItems.map(entry => <Tr key={entry.id}>
|
|
110
|
+
<Td>
|
|
111
|
+
<BaseCheckbox aria-label={`Select report`} />
|
|
112
|
+
</Td>
|
|
113
|
+
<Td>
|
|
114
|
+
<Typography textColor="neutral800">{renderReason(entry.reason)}</Typography>
|
|
115
|
+
</Td>
|
|
116
|
+
<Td>
|
|
117
|
+
<Typography textColor="neutral800">{entry.content}</Typography>
|
|
118
|
+
</Td>
|
|
119
|
+
<Td>
|
|
120
|
+
<Typography textColor="neutral800">{ formatDate(entry.createdAt, { dateStyle: 'short', timeStyle: 'short' }) }</Typography>
|
|
121
|
+
</Td>
|
|
122
|
+
<Td>
|
|
123
|
+
<Typography textColor="neutral800">{ renderStatus(entry.resolved) }</Typography>
|
|
124
|
+
</Td>
|
|
125
|
+
<Td>
|
|
126
|
+
{ !entry.resolved && (<Flex direction="column" alignItems="flex-end">
|
|
127
|
+
<Button onClick={() => handleClickResolve(entry.id)} startIcon={<Check />} variant="success">{ getMessage('page.details.panel.discussion.warnings.reports.dialog.actions.resolve', 'resolve') }</Button>
|
|
128
|
+
</Flex>) }
|
|
129
|
+
</Td>
|
|
130
|
+
</Tr>)}
|
|
131
|
+
</Tbody>
|
|
132
|
+
{/* <TFooter /> */}
|
|
133
|
+
</Table>)
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
ReportsReviewTable.propTypes = {
|
|
137
|
+
commentId: PropTypes.string.isRequired,
|
|
138
|
+
items: PropTypes.array.isRequired,
|
|
139
|
+
mutation: PropTypes.func.isRequired,
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
export default ReportsReviewTable;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React, { useRef, useState } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { Button } from '@strapi/design-system/Button';
|
|
4
|
+
import { Box } from '@strapi/design-system/Box';
|
|
5
|
+
import { Filter } from '@strapi/icons';
|
|
6
|
+
import { FilterListURLQuery, FilterPopoverURLQuery } from '@strapi/helper-plugin';
|
|
7
|
+
import { getMessage } from '../../utils';
|
|
8
|
+
|
|
9
|
+
const TableFilters = ({ displayedFilters }) => {
|
|
10
|
+
const [isVisible, setIsVisible] = useState(false);
|
|
11
|
+
const buttonRef = useRef();
|
|
12
|
+
|
|
13
|
+
const handleToggle = () => {
|
|
14
|
+
setIsVisible(prev => !prev);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<>
|
|
19
|
+
<Box paddingTop={1} paddingBottom={1}>
|
|
20
|
+
<Button
|
|
21
|
+
variant="tertiary"
|
|
22
|
+
ref={buttonRef}
|
|
23
|
+
startIcon={<Filter />}
|
|
24
|
+
onClick={handleToggle}
|
|
25
|
+
size="S"
|
|
26
|
+
>
|
|
27
|
+
{getMessage('page.discover.table.filters')}
|
|
28
|
+
</Button>
|
|
29
|
+
{isVisible && (
|
|
30
|
+
<FilterPopoverURLQuery
|
|
31
|
+
displayedFilters={displayedFilters}
|
|
32
|
+
isVisible={isVisible}
|
|
33
|
+
onToggle={handleToggle}
|
|
34
|
+
source={buttonRef}
|
|
35
|
+
/>
|
|
36
|
+
)}
|
|
37
|
+
</Box>
|
|
38
|
+
<FilterListURLQuery filtersSchema={displayedFilters} />
|
|
39
|
+
</>
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
TableFilters.propTypes = {
|
|
44
|
+
displayedFilters: PropTypes.arrayOf(
|
|
45
|
+
PropTypes.shape({
|
|
46
|
+
name: PropTypes.string.isRequired,
|
|
47
|
+
metadatas: PropTypes.shape({ label: PropTypes.string }),
|
|
48
|
+
fieldSchema: PropTypes.shape({ type: PropTypes.string }),
|
|
49
|
+
})
|
|
50
|
+
).isRequired,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export default TableFilters;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { Box } from '@strapi/design-system/Box';
|
|
4
|
+
import { Flex } from '@strapi/design-system/Flex';
|
|
5
|
+
import { PaginationURLQuery, PageSizeURLQuery } from '@strapi/helper-plugin';
|
|
6
|
+
|
|
7
|
+
const TablePagination = ({ pagination }) => {
|
|
8
|
+
return (
|
|
9
|
+
<Box paddingTop={4}>
|
|
10
|
+
<Flex alignItems="flex-end" justifyContent="space-between">
|
|
11
|
+
<PageSizeURLQuery trackedEvent="willChangeNumberOfEntriesPerPage" />
|
|
12
|
+
<PaginationURLQuery pagination={pagination} />
|
|
13
|
+
</Flex>
|
|
14
|
+
</Box>
|
|
15
|
+
);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
TablePagination.defaultProps = {
|
|
19
|
+
pagination: {
|
|
20
|
+
pageCount: 0,
|
|
21
|
+
pageSize: 10,
|
|
22
|
+
total: 0,
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
TablePagination.propTypes = {
|
|
27
|
+
pagination: PropTypes.shape({
|
|
28
|
+
page: PropTypes.number,
|
|
29
|
+
pageCount: PropTypes.number,
|
|
30
|
+
pageSize: PropTypes.number,
|
|
31
|
+
total: PropTypes.number,
|
|
32
|
+
}),
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export default TablePagination;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
const initSize = 576;
|
|
5
|
+
|
|
6
|
+
const CommentsIcon = ({ width = 24, height = 24 }) =>
|
|
7
|
+
<svg viewBox={`0 0 ${width} ${height}`} xmlns="http://www.w3.org/2000/svg"><g style={ {transform: `scale(${width/initSize})` } }><path d="M416 192c0-88.4-93.1-160-208-160S0 103.6 0 192c0 34.3 14.1 65.9 38 92-13.4 30.2-35.5 54.2-35.8 54.5-2.2 2.3-2.8 5.7-1.5 8.7S4.8 352 8 352c36.6 0 66.9-12.3 88.7-25 32.2 15.7 70.3 25 111.3 25 114.9 0 208-71.6 208-160zm122 220c23.9-26 38-57.7 38-92 0-66.9-53.5-124.2-129.3-148.1.9 6.6 1.3 13.3 1.3 20.1 0 105.9-107.7 192-240 192-10.8 0-21.3-.8-31.7-1.9C207.8 439.6 281.8 480 368 480c41 0 79.1-9.2 111.3-25 21.8 12.7 52.1 25 88.7 25 3.2 0 6.1-1.9 7.3-4.8 1.3-2.9.7-6.3-1.5-8.7-.3-.3-22.4-24.2-35.8-54.5z"/></g></svg>;
|
|
8
|
+
|
|
9
|
+
export default CommentsIcon;
|
package/admin/src/index.js
CHANGED
|
@@ -1,49 +1,65 @@
|
|
|
1
|
+
import { prefixPluginTranslations } from '@strapi/helper-plugin';
|
|
1
2
|
import pluginPkg from '../../package.json';
|
|
2
3
|
import pluginId from './pluginId';
|
|
4
|
+
import Initializer from './components/Initializer';
|
|
5
|
+
import PluginIcon from './components/PluginIcon';
|
|
3
6
|
import pluginPermissions from './permissions';
|
|
4
|
-
import
|
|
5
|
-
import App from './containers/App';
|
|
6
|
-
import Initializer from './containers/Initializer';
|
|
7
|
-
import lifecycles from './lifecycles';
|
|
8
|
-
import trads from './translations';
|
|
7
|
+
import reducers from './reducers';
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
const pluginDescription = pluginPkg.strapi.description || pluginPkg.description;
|
|
9
|
+
const name = pluginPkg.strapi.name;
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
layout: null,
|
|
26
|
-
lifecycles,
|
|
27
|
-
mainComponent: App,
|
|
28
|
-
name,
|
|
29
|
-
pluginLogo,
|
|
30
|
-
preventComponentRendering: false,
|
|
31
|
-
trads,
|
|
32
|
-
menu: {
|
|
33
|
-
pluginsSectionLinks: [
|
|
34
|
-
{
|
|
35
|
-
destination: `/plugins/${pluginId}`,
|
|
36
|
-
icon,
|
|
37
|
-
name,
|
|
38
|
-
label: {
|
|
39
|
-
id: `${pluginId}.plugin.name`,
|
|
40
|
-
defaultMessage: 'COMMENTS',
|
|
41
|
-
},
|
|
42
|
-
permissions: pluginPermissions.main,
|
|
43
|
-
},
|
|
44
|
-
],
|
|
45
|
-
},
|
|
46
|
-
};
|
|
11
|
+
export default {
|
|
12
|
+
register(app) {
|
|
13
|
+
app.addMenuLink({
|
|
14
|
+
to: `/plugins/${pluginId}/discover`,
|
|
15
|
+
badgeContent: 1,
|
|
16
|
+
icon: PluginIcon,
|
|
17
|
+
intlLabel: {
|
|
18
|
+
id: `${pluginId}.plugin.name`,
|
|
19
|
+
defaultMessage: name,
|
|
20
|
+
},
|
|
21
|
+
Component: async () => {
|
|
22
|
+
const component = await import(/* webpackChunkName: "[request]" */ './pages/App');
|
|
47
23
|
|
|
48
|
-
|
|
24
|
+
return component;
|
|
25
|
+
},
|
|
26
|
+
permissions: pluginPermissions,
|
|
27
|
+
});
|
|
28
|
+
app.addReducers(reducers);
|
|
29
|
+
app.registerPlugin({
|
|
30
|
+
id: pluginId,
|
|
31
|
+
initializer: Initializer,
|
|
32
|
+
isReady: false,
|
|
33
|
+
name,
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
bootstrap(app) {
|
|
38
|
+
// app.injectContentManagerComponent('editView', 'informations', {
|
|
39
|
+
// name: 'comments-link',
|
|
40
|
+
// Component: () => 'TODO: Comments count',
|
|
41
|
+
// });
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
async registerTrads({ locales }) {
|
|
45
|
+
const importedTrads = await Promise.all(
|
|
46
|
+
locales.map(locale => {
|
|
47
|
+
return import(`./translations/${locale}.json`)
|
|
48
|
+
.then(({ default: data }) => {
|
|
49
|
+
return {
|
|
50
|
+
data: prefixPluginTranslations(data, pluginId),
|
|
51
|
+
locale,
|
|
52
|
+
};
|
|
53
|
+
})
|
|
54
|
+
.catch(() => {
|
|
55
|
+
return {
|
|
56
|
+
data: {},
|
|
57
|
+
locale,
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
})
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
return Promise.resolve(importedTrads);
|
|
64
|
+
},
|
|
49
65
|
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* This component is the skeleton around the actual pages, and should only
|
|
4
|
+
* contain code that should be seen on all pages. (e.g. navigation bar)
|
|
5
|
+
*
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, { useReducer } from 'react';
|
|
9
|
+
import { Switch, Route } from 'react-router-dom';
|
|
10
|
+
import { useQuery } from 'react-query';
|
|
11
|
+
import {
|
|
12
|
+
NotFound,
|
|
13
|
+
LoadingIndicatorPage,
|
|
14
|
+
useNotification } from '@strapi/helper-plugin';
|
|
15
|
+
|
|
16
|
+
import ComingSoonPage from '../ComingSoonPage';
|
|
17
|
+
import getUrl from '../../utils/getUrl';
|
|
18
|
+
import Discover from '../Discover';
|
|
19
|
+
import Details from '../Details';
|
|
20
|
+
import { fetchConfig } from './utils/api';
|
|
21
|
+
import initReducer from './reducer/init';
|
|
22
|
+
import reducer, { initialState } from './reducer';
|
|
23
|
+
import { setConfig } from './reducer/actions';
|
|
24
|
+
|
|
25
|
+
const App = () => {
|
|
26
|
+
|
|
27
|
+
const toggleNotification = useNotification();
|
|
28
|
+
const [reducerState, dispatch] = useReducer(reducer, initialState, () =>
|
|
29
|
+
initReducer(initialState)
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const { isLoading, isFetching } = useQuery(
|
|
33
|
+
'get-config', () => fetchConfig(toggleNotification),
|
|
34
|
+
{
|
|
35
|
+
initialData: { },
|
|
36
|
+
onSuccess: (response) => {
|
|
37
|
+
dispatch(setConfig(response));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
if (isLoading || isFetching) {
|
|
43
|
+
return (<LoadingIndicatorPage />)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<div>
|
|
48
|
+
<Switch>
|
|
49
|
+
<Route path={ getUrl('dashboard') } component={ComingSoonPage} exact />
|
|
50
|
+
<Route path={ getUrl('discover') } component={Discover} exact />
|
|
51
|
+
<Route path={ getUrl('discover/:id') } component={Details} exact />
|
|
52
|
+
<Route path={ getUrl('reports') } component={ComingSoonPage} exact />
|
|
53
|
+
<Route path={ getUrl('settings') } component={ComingSoonPage} exact />
|
|
54
|
+
<Route component={NotFound} />
|
|
55
|
+
</Switch>
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export default App;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import produce from 'immer'; // current
|
|
2
|
+
import { set } from 'lodash';
|
|
3
|
+
import { GET_CONFIG, SET_CONFIG } from './constants';
|
|
4
|
+
|
|
5
|
+
const initialState = {
|
|
6
|
+
config: {},
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const reducer = (state = initialState, action) =>
|
|
10
|
+
// eslint-disable-next-line consistent-return
|
|
11
|
+
produce(state, draftState => {
|
|
12
|
+
switch (action.type) {
|
|
13
|
+
case SET_CONFIG: {
|
|
14
|
+
set(draftState, 'config', action.data);
|
|
15
|
+
break;
|
|
16
|
+
}
|
|
17
|
+
case GET_CONFIG: {
|
|
18
|
+
return draftState?.config;
|
|
19
|
+
}
|
|
20
|
+
default:
|
|
21
|
+
console.log('draftState', draftState);
|
|
22
|
+
return draftState;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
export default reducer;
|
|
27
|
+
export { initialState };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
import { createSelector } from 'reselect';
|
|
4
|
+
import { initialState } from './';
|
|
5
|
+
import { REDUCER_NAME } from './constants';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Direct selector to the listView state domain
|
|
9
|
+
*/
|
|
10
|
+
const appView = () => state => state[REDUCER_NAME] || initialState;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Other specific selectors
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Default selector used by listView
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const makeAppView = () =>
|
|
21
|
+
createSelector(
|
|
22
|
+
appView(),
|
|
23
|
+
substate => {
|
|
24
|
+
return substate;
|
|
25
|
+
}
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const selectConfig = state => {
|
|
29
|
+
const { config } = state[REDUCER_NAME];
|
|
30
|
+
|
|
31
|
+
return config;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default makeAppView;
|
|
35
|
+
export { appView, selectConfig };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { stringify } from 'qs';
|
|
2
|
+
import { isEmpty } from 'lodash';
|
|
3
|
+
|
|
4
|
+
import { getApiURL, axiosInstance } from '../../../utils';
|
|
5
|
+
|
|
6
|
+
// eslint-disable-next-line import/prefer-default-export
|
|
7
|
+
export const fetchConfig = async (toggleNotification) => {
|
|
8
|
+
try {
|
|
9
|
+
const { data } = await axiosInstance.get(getApiURL(`moderate/config`));
|
|
10
|
+
|
|
11
|
+
return data;
|
|
12
|
+
} catch (err) {
|
|
13
|
+
toggleNotification({
|
|
14
|
+
type: 'warning',
|
|
15
|
+
message: { id: 'notification.error' },
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
throw new Error('error');
|
|
19
|
+
}
|
|
20
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/*
|
|
2
|
+
*
|
|
3
|
+
* Coming soon page
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { memo } from 'react';
|
|
8
|
+
import { Box } from '@strapi/design-system/Box';
|
|
9
|
+
import { EmptyStateLayout } from '@strapi/design-system/EmptyStateLayout';
|
|
10
|
+
import { Layout } from '@strapi/design-system/Layout';
|
|
11
|
+
import { Illo } from '@strapi/icons';
|
|
12
|
+
import getMessage from '../../utils/getMessage';
|
|
13
|
+
import Nav from '../../components/Nav';
|
|
14
|
+
|
|
15
|
+
const ComingSoonPage = () => {
|
|
16
|
+
return (<Box background="neutral100">
|
|
17
|
+
<Layout sideNav={<Nav />}>
|
|
18
|
+
<Box padding={8} background="neutral100">
|
|
19
|
+
<EmptyStateLayout icon={<Illo />} content={getMessage('page.coming.soon')} />
|
|
20
|
+
</Box>
|
|
21
|
+
</Layout>
|
|
22
|
+
</Box>);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default memo(ComingSoonPage);
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/*
|
|
2
|
+
*
|
|
3
|
+
* Details
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { useRef, useMemo } from 'react';
|
|
8
|
+
import { useQuery } from 'react-query';
|
|
9
|
+
import { useRouteMatch } from 'react-router-dom';
|
|
10
|
+
import { Layout, HeaderLayout, TwoColsLayout, ContentLayout } from '@strapi/design-system/Layout';
|
|
11
|
+
import { Box } from '@strapi/design-system/Box';
|
|
12
|
+
import { Link } from '@strapi/design-system/Link';
|
|
13
|
+
import { Loader } from '@strapi/design-system/Loader';
|
|
14
|
+
import { Stack } from '@strapi/design-system/Stack';
|
|
15
|
+
import { useNotifyAT } from '@strapi/design-system/LiveRegions';
|
|
16
|
+
import { ArrowLeft } from '@strapi/icons';
|
|
17
|
+
|
|
18
|
+
import { isEmpty } from 'lodash';
|
|
19
|
+
import {
|
|
20
|
+
LoadingIndicatorPage,
|
|
21
|
+
useTracking,
|
|
22
|
+
useNotification,
|
|
23
|
+
useRBAC,
|
|
24
|
+
useFocusWhenNavigate,
|
|
25
|
+
} from '@strapi/helper-plugin';
|
|
26
|
+
import getMessage from '../../utils/getMessage';
|
|
27
|
+
import { fetchDetailsData, fetchContentTypeData } from './utils/api';
|
|
28
|
+
import pluginPermissions from '../../permissions';
|
|
29
|
+
import { getUrl } from '../../utils';
|
|
30
|
+
import Nav from '../../components/Nav';
|
|
31
|
+
import { REGEX } from '../../utils/constants';
|
|
32
|
+
import EntityDetails from '../../components/EntityDetails';
|
|
33
|
+
import DiscussionThread from '../../components/DiscussionThread';
|
|
34
|
+
import DiscussionStatus from '../../components/DiscussionStatus';
|
|
35
|
+
|
|
36
|
+
const Details = () => {
|
|
37
|
+
useFocusWhenNavigate();
|
|
38
|
+
|
|
39
|
+
const {
|
|
40
|
+
params: { id },
|
|
41
|
+
} = useRouteMatch(getUrl(`discover/:id`));
|
|
42
|
+
|
|
43
|
+
const { notifyStatus } = useNotifyAT();
|
|
44
|
+
const { trackUsage } = useTracking();
|
|
45
|
+
const trackUsageRef = useRef(trackUsage);
|
|
46
|
+
const toggleNotification = useNotification();
|
|
47
|
+
|
|
48
|
+
const viewPermissions = useMemo(() => {
|
|
49
|
+
return { view: pluginPermissions.view };
|
|
50
|
+
}, []);
|
|
51
|
+
|
|
52
|
+
const {
|
|
53
|
+
isLoading: isLoadingForPermissions,
|
|
54
|
+
allowedActions: { canView },
|
|
55
|
+
} = useRBAC(viewPermissions);
|
|
56
|
+
|
|
57
|
+
const { isLoading: isLoadingForData, data, isFetching } = useQuery(
|
|
58
|
+
['get-details-data', id],
|
|
59
|
+
() => fetchDetailsData(id, toggleNotification),
|
|
60
|
+
{
|
|
61
|
+
initialData: { },
|
|
62
|
+
}
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const { entity, level, selected } = data;
|
|
66
|
+
const entityUidValid = entity?.uid && REGEX.uid.test(entity?.uid);
|
|
67
|
+
|
|
68
|
+
const { data: contentTypeData } = useQuery(
|
|
69
|
+
['get-additional-data', entity?.uid],
|
|
70
|
+
() => fetchContentTypeData(entity?.uid, toggleNotification),
|
|
71
|
+
{
|
|
72
|
+
enabled: !!entity?.uid,
|
|
73
|
+
suspense: !entityUidValid,
|
|
74
|
+
}
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
const isLoading = isLoadingForData || isFetching;
|
|
78
|
+
|
|
79
|
+
return <Box background="neutral100">
|
|
80
|
+
<Layout sideNav={<Nav />}>
|
|
81
|
+
{(isLoading || isLoadingForPermissions) && isEmpty(data) ? (<LoadingIndicatorPage />) : (
|
|
82
|
+
<>
|
|
83
|
+
<HeaderLayout
|
|
84
|
+
navigationAction={
|
|
85
|
+
<Link startIcon={<ArrowLeft />} to={getUrl(`discover`)}>
|
|
86
|
+
{ getMessage('button.back.label', '', false) }
|
|
87
|
+
</Link>}
|
|
88
|
+
title={ getMessage('page.details.header') }
|
|
89
|
+
subtitle={ getMessage('page.details.header.description') } as="h2" />
|
|
90
|
+
<ContentLayout>
|
|
91
|
+
<TwoColsLayout
|
|
92
|
+
startCol={<DiscussionThread level={level} selected={selected} isReloading={isLoading} />}
|
|
93
|
+
endCol={ <Stack size={2}>
|
|
94
|
+
<DiscussionStatus item={selected} />
|
|
95
|
+
{ !contentTypeData?.schema ? (<Box padding={4}>
|
|
96
|
+
<Loader small>Fetching entity...</Loader>
|
|
97
|
+
</Box>) :
|
|
98
|
+
(<EntityDetails data={entity} schema={contentTypeData?.schema} />)
|
|
99
|
+
}
|
|
100
|
+
</Stack>
|
|
101
|
+
}
|
|
102
|
+
/>
|
|
103
|
+
</ContentLayout>
|
|
104
|
+
</>) }
|
|
105
|
+
</Layout>
|
|
106
|
+
</Box>;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export default Details;
|