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.
Files changed (149) hide show
  1. package/.circleci/config.yml +2 -2
  2. package/README.md +71 -95
  3. package/admin/src/components/ConfirmationDialog/index.js +56 -0
  4. package/admin/src/components/DiscussionStatus/index.js +33 -0
  5. package/admin/src/components/DiscussionThread/index.js +58 -0
  6. package/admin/src/components/DiscussionThreadItem/index.js +87 -0
  7. package/admin/src/components/DiscussionThreadItem/styles.js +87 -0
  8. package/admin/src/components/DiscussionThreadItemActions/index.js +176 -0
  9. package/admin/src/components/DiscussionThreadItemActions/styles.js +22 -0
  10. package/admin/src/components/DiscussionThreadItemMeta/index.js +31 -0
  11. package/admin/src/components/DiscussionThreadItemWarnings/index.js +137 -0
  12. package/admin/src/components/DiscussionThreadItemWarnings/styles.js +7 -0
  13. package/admin/src/components/EntityDetails/index.js +53 -0
  14. package/admin/src/{containers → components}/Initializer/index.js +4 -4
  15. package/admin/src/components/LoadingIndicatorOverlay/index.js +15 -0
  16. package/admin/src/components/LoadingIndicatorOverlay/styles.js +20 -0
  17. package/admin/src/components/Nav/index.js +45 -0
  18. package/admin/src/components/PluginIcon/index.js +12 -0
  19. package/admin/src/components/ReportsReviewModal/index.js +45 -0
  20. package/admin/src/components/ReportsReviewTable/index.js +142 -0
  21. package/admin/src/components/TableFilters/index.js +53 -0
  22. package/admin/src/components/TablePagination/index.js +35 -0
  23. package/admin/src/components/icons/comments.js +9 -0
  24. package/admin/src/index.js +58 -42
  25. package/admin/src/pages/App/index.js +60 -0
  26. package/admin/src/pages/App/reducer/actions.js +8 -0
  27. package/admin/src/pages/App/reducer/constants.js +4 -0
  28. package/admin/src/pages/App/reducer/index.js +27 -0
  29. package/admin/src/pages/App/reducer/init.js +7 -0
  30. package/admin/src/pages/App/reducer/selectors.js +35 -0
  31. package/admin/src/pages/App/utils/api.js +20 -0
  32. package/admin/src/pages/ComingSoonPage/index.js +25 -0
  33. package/admin/src/pages/Details/index.js +109 -0
  34. package/admin/src/pages/Details/utils/api.js +41 -0
  35. package/admin/src/pages/Discover/index.js +203 -0
  36. package/admin/src/pages/Discover/utils/api.js +20 -0
  37. package/admin/src/pages/Discover/utils/filtersSchema.js +9 -0
  38. package/admin/src/permissions.js +13 -33
  39. package/admin/src/pluginId.js +2 -4
  40. package/admin/src/reducers.js +8 -0
  41. package/admin/src/translations/en.json +72 -33
  42. package/admin/src/translations/fr.json +1 -0
  43. package/admin/src/utils/axiosInstance.js +35 -0
  44. package/admin/src/utils/constants.js +28 -0
  45. package/admin/src/utils/getApiUrl.js +5 -0
  46. package/admin/src/utils/getMessage.js +14 -0
  47. package/admin/src/utils/getRelatedGroups.js +5 -0
  48. package/admin/src/utils/getUrl.js +5 -0
  49. package/admin/src/utils/handleAPIError.js +15 -0
  50. package/admin/src/utils/index.js +6 -0
  51. package/codecov.yml +2 -1
  52. package/content-types/comment.js +97 -0
  53. package/content-types/index.js +11 -0
  54. package/content-types/report.js +54 -0
  55. package/package.json +11 -25
  56. package/server/bootstrap.js +5 -0
  57. package/server/config/index.js +10 -0
  58. package/server/controllers/admin.js +134 -0
  59. package/server/controllers/client.js +109 -0
  60. package/server/controllers/index.js +9 -0
  61. package/{controllers → server/controllers}/utils/__tests__/functions.test.js +0 -0
  62. package/{controllers → server/controllers}/utils/functions.js +1 -1
  63. package/server/destroy.js +5 -0
  64. package/server/index.js +22 -0
  65. package/server/register.js +5 -0
  66. package/server/routes/admin.js +92 -0
  67. package/server/routes/client.js +52 -0
  68. package/server/routes/index.js +15 -0
  69. package/server/services/admin.js +186 -0
  70. package/server/services/client.js +227 -0
  71. package/server/services/common.js +191 -0
  72. package/server/services/index.js +11 -0
  73. package/{services → server/services}/utils/__tests__/functions.test.js +7 -9
  74. package/{services → server/services}/utils/functions.js +19 -8
  75. package/server/utils/constants.js +16 -0
  76. package/{services → server}/utils/error.js +0 -0
  77. package/server/utils/functions.js +19 -0
  78. package/strapi-admin.js +3 -0
  79. package/strapi-server.js +9 -0
  80. package/admin/src/assets/images/icon-cross-blue.svg +0 -1
  81. package/admin/src/assets/images/icon_remove.svg +0 -19
  82. package/admin/src/assets/images/logo.svg +0 -5
  83. package/admin/src/components/AbuseReportsPopUp/ItemPreviewContainer.js +0 -8
  84. package/admin/src/components/AbuseReportsPopUp/ModalFooter.js +0 -31
  85. package/admin/src/components/AbuseReportsPopUp/TableContainer.js +0 -63
  86. package/admin/src/components/AbuseReportsPopUp/index.js +0 -148
  87. package/admin/src/components/Container/index.js +0 -7
  88. package/admin/src/components/EmptyView/index.js +0 -25
  89. package/admin/src/components/Filters/index.js +0 -151
  90. package/admin/src/components/Filters/index.styled.js +0 -82
  91. package/admin/src/components/Filters/utils.js +0 -21
  92. package/admin/src/components/Item/CardItem.js +0 -30
  93. package/admin/src/components/Item/CardWrapper.js +0 -10
  94. package/admin/src/components/Item/index.js +0 -92
  95. package/admin/src/components/ItemDetails/CardItem.js +0 -33
  96. package/admin/src/components/ItemDetails/CardLevelCounter.js +0 -46
  97. package/admin/src/components/ItemDetails/CardLevelCounterLink.js +0 -24
  98. package/admin/src/components/ItemDetails/CardLevelWrapper.js +0 -22
  99. package/admin/src/components/ItemDetails/CardWrapper.js +0 -52
  100. package/admin/src/components/ItemDetails/index.js +0 -151
  101. package/admin/src/components/ItemFooter/CardItemAuthor.js +0 -10
  102. package/admin/src/components/ItemFooter/CardItemRelation.js +0 -18
  103. package/admin/src/components/ItemFooter/Wrapper.js +0 -20
  104. package/admin/src/components/ItemFooter/index.js +0 -87
  105. package/admin/src/components/ItemHeader/CardHeaderBlocked.js +0 -24
  106. package/admin/src/components/ItemHeader/CardHeaderIndicator.js +0 -30
  107. package/admin/src/components/ItemHeader/CardHeaderIndicatorBlue.js +0 -11
  108. package/admin/src/components/ItemHeader/CardHeaderIndicatorRed.js +0 -11
  109. package/admin/src/components/ItemHeader/CardHeaderIndicatorsContainer.js +0 -18
  110. package/admin/src/components/ItemHeader/CardHeaderReports.js +0 -40
  111. package/admin/src/components/ItemHeader/Wrapper.js +0 -16
  112. package/admin/src/components/ItemHeader/index.js +0 -80
  113. package/admin/src/components/ItemModeration/Wrapper.js +0 -19
  114. package/admin/src/components/ItemModeration/index.js +0 -47
  115. package/admin/src/components/List/Container.js +0 -20
  116. package/admin/src/components/List/index.js +0 -21
  117. package/admin/src/components/Search/index.js +0 -83
  118. package/admin/src/containers/App/Wrapper.js +0 -14
  119. package/admin/src/containers/App/index.js +0 -47
  120. package/admin/src/containers/DataManagerProvider/actions.js +0 -11
  121. package/admin/src/containers/DataManagerProvider/index.js +0 -280
  122. package/admin/src/containers/DataManagerProvider/init.js +0 -5
  123. package/admin/src/containers/DataManagerProvider/reducer.js +0 -59
  124. package/admin/src/containers/DetailsView/Wrapper.js +0 -18
  125. package/admin/src/containers/DetailsView/index.js +0 -100
  126. package/admin/src/containers/ListView/FadedWrapper.js +0 -42
  127. package/admin/src/containers/ListView/Footer.js +0 -53
  128. package/admin/src/containers/ListView/Wrapper.js +0 -17
  129. package/admin/src/containers/ListView/components.js +0 -138
  130. package/admin/src/containers/ListView/index.js +0 -45
  131. package/admin/src/containers/Panel/actions.js +0 -5
  132. package/admin/src/containers/Panel/index.js +0 -191
  133. package/admin/src/containers/Panel/index.styled.js +0 -6
  134. package/admin/src/containers/Panel/reducer.js +0 -44
  135. package/admin/src/contexts/DataManagerContext.js +0 -5
  136. package/admin/src/hooks/useDataManager.js +0 -6
  137. package/admin/src/lifecycles.js +0 -3
  138. package/admin/src/translations/index.js +0 -7
  139. package/admin/src/utils/getTrad.js +0 -5
  140. package/admin/src/utils/getTradId.js +0 -5
  141. package/config/functions/bootstrap.js +0 -48
  142. package/config/routes.json +0 -116
  143. package/config/schema.graphql.js +0 -270
  144. package/controllers/comments.js +0 -211
  145. package/models/comment.js +0 -8
  146. package/models/comment.settings.json +0 -98
  147. package/models/report.js +0 -8
  148. package/models/report.settings.json +0 -49
  149. 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;
@@ -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 pluginLogo from './assets/images/logo.svg';
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
- export default strapi => {
11
- const pluginDescription = pluginPkg.strapi.description || pluginPkg.description;
9
+ const name = pluginPkg.strapi.name;
12
10
 
13
- const icon = pluginPkg.strapi.icon;
14
- const name = pluginPkg.strapi.name;
15
- const plugin = {
16
- blockerComponent: null,
17
- blockerComponentProps: {},
18
- description: pluginDescription,
19
- icon,
20
- id: pluginId,
21
- initializer: Initializer,
22
- injectedComponents: [],
23
- isReady: false,
24
- isRequired: pluginPkg.strapi.required || false,
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
- return strapi.registerPlugin(plugin);
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,8 @@
1
+ import { GET_CONFIG, SET_CONFIG } from "./constants";
2
+
3
+ export const setConfig = data => ({
4
+ data,
5
+ type: SET_CONFIG,
6
+ });
7
+
8
+ export const getConfig = () => ({ type: GET_CONFIG });
@@ -0,0 +1,4 @@
1
+ export const REDUCER_NAME = 'plugin-comments_app';
2
+
3
+ export const SET_CONFIG = 'SET_CONFIG';
4
+ export const GET_CONFIG = 'GET_CONFIG';
@@ -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,7 @@
1
+ const initReducer = (initialState) => {
2
+ return {
3
+ ...initialState,
4
+ };
5
+ };
6
+
7
+ export default initReducer;
@@ -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;