@strapi/admin 4.12.6 → 4.13.0-alpha.0

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 (211) hide show
  1. package/admin/src/StrapiApp.js +1 -1
  2. package/admin/src/components/AuthenticatedApp/index.js +118 -0
  3. package/admin/src/components/AuthenticatedApp/utils/api.js +85 -0
  4. package/admin/src/components/AuthenticatedApp/utils/checkLatestStrapiVersion.js +11 -0
  5. package/admin/src/components/GuidedTour/Modal/index.js +3 -1
  6. package/admin/src/components/NpsSurvey/hooks/useNpsSurveySettings.js +17 -0
  7. package/admin/src/components/NpsSurvey/index.js +365 -0
  8. package/admin/src/components/PluginsInitializer/index.js +68 -0
  9. package/admin/src/components/PluginsInitializer/init.js +11 -0
  10. package/admin/src/components/PluginsInitializer/reducer.js +22 -0
  11. package/admin/src/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/getTableColumns.js +2 -0
  12. package/admin/src/content-manager/components/DynamicZone/components/DynamicZoneLabel.js +1 -1
  13. package/admin/src/content-manager/components/EditViewDataManagerProvider/reducer.js +8 -1
  14. package/admin/src/content-manager/components/Filter/CustomInputs/AdminUsersFilter.js +42 -0
  15. package/admin/src/content-manager/components/Filter/Filter.js +54 -0
  16. package/admin/src/content-manager/components/Filter/index.js +1 -0
  17. package/admin/src/content-manager/components/InputUID/index.js +222 -216
  18. package/admin/src/content-manager/components/RelationInput/RelationInput.js +4 -0
  19. package/admin/src/content-manager/components/RepeatableComponent/index.js +32 -2
  20. package/admin/src/content-manager/components/Wysiwyg/Editor.js +432 -68
  21. package/admin/src/content-manager/components/Wysiwyg/WysiwygStyles.js +0 -7
  22. package/admin/src/content-manager/components/Wysiwyg/index.js +147 -152
  23. package/admin/src/content-manager/hooks/useAllowedAttributes.js +47 -0
  24. package/admin/src/content-manager/pages/App/index.js +16 -5
  25. package/admin/src/content-manager/pages/EditView/Information/index.js +9 -8
  26. package/admin/src/content-manager/pages/ListSettingsView/components/Settings.js +40 -7
  27. package/admin/src/content-manager/pages/ListSettingsView/index.js +6 -2
  28. package/admin/src/content-manager/pages/ListView/components/BulkActionButtons/SelectedEntriesModal/index.js +2 -2
  29. package/admin/src/content-manager/pages/ListView/components/FieldPicker/index.js +67 -69
  30. package/admin/src/content-manager/pages/ListView/components/TableRows/index.js +1 -1
  31. package/admin/src/content-manager/pages/ListView/components/ViewSettingsMenu/index.js +80 -0
  32. package/admin/src/content-manager/pages/ListView/index.js +236 -67
  33. package/admin/src/content-manager/utils/getDisplayName.js +33 -0
  34. package/admin/src/content-manager/utils/index.js +1 -0
  35. package/admin/src/hooks/useSettingsForm/index.js +3 -14
  36. package/admin/src/hooks/useSettingsMenu/index.js +2 -2
  37. package/admin/src/hooks/useSettingsMenu/utils/formatLinks.js +3 -1
  38. package/admin/src/hooks/useSettingsMenu/utils/sortLinks.js +3 -1
  39. package/admin/src/index.js +1 -1
  40. package/admin/src/layouts/AppLayout/index.js +33 -0
  41. package/admin/src/pages/Admin/Onboarding/index.js +3 -1
  42. package/admin/src/pages/Admin/index.js +73 -77
  43. package/admin/src/pages/App/constants.js +1 -1
  44. package/admin/src/pages/App/index.js +122 -160
  45. package/admin/src/pages/AuthPage/components/Register/index.js +5 -0
  46. package/admin/src/pages/AuthPage/index.js +4 -2
  47. package/admin/src/pages/HomePage/index.js +3 -1
  48. package/admin/src/pages/InstalledPluginsPage/index.js +3 -1
  49. package/admin/src/pages/{InternalErrorPage.js → InternalErrorPage/index.js} +4 -3
  50. package/admin/src/pages/{NotFoundPage.js → NotFoundPage/index.js} +3 -1
  51. package/admin/src/pages/ProfilePage/index.js +4 -2
  52. package/admin/src/pages/SettingsPage/constants.js +132 -67
  53. package/admin/src/pages/SettingsPage/index.js +18 -23
  54. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/index.js +1 -1
  55. package/admin/src/pages/SettingsPage/pages/TransferTokens/EditView/index.js +1 -1
  56. package/admin/src/pages/SettingsPage/pages/Users/EditPage/index.js +1 -2
  57. package/admin/src/pages/SettingsPage/pages/Users/ListPage/DynamicTable/TableRows/index.js +1 -1
  58. package/admin/src/pages/UseCasePage/index.js +175 -0
  59. package/admin/src/plugins.js +7 -8
  60. package/admin/src/translations/en.json +10 -1
  61. package/admin/src/utils/checkFormValidity.js +15 -0
  62. package/admin/src/utils/createRoute.js +7 -5
  63. package/admin/src/utils/formatAPIErrors.js +3 -1
  64. package/admin/src/utils/getAttributesToDisplay.js +19 -0
  65. package/admin/src/utils/getExistingActions.js +32 -0
  66. package/admin/src/utils/getFullName.js +1 -1
  67. package/admin/src/utils/index.js +9 -0
  68. package/admin/src/utils/makeUniqueRoutes.js +6 -0
  69. package/admin/src/utils/sortLinks.js +3 -1
  70. package/admin/src/utils/uniqueAdminHash.js +9 -2
  71. package/build/{1049.9d69d231.chunk.js → 1049.ec69f5e0.chunk.js} +1 -1
  72. package/build/1227.9f37e1dc.chunk.js +1 -0
  73. package/build/1386.ea73b677.chunk.js +7 -0
  74. package/build/{2225.33287e1b.chunk.js → 2225.649fb7bc.chunk.js} +2 -2
  75. package/build/{2237.03792b63.chunk.js → 2237.b832ae6e.chunk.js} +4 -4
  76. package/build/{2379.401f56f3.chunk.js → 2379.1f98a31a.chunk.js} +1 -1
  77. package/build/{2395.e6a79fbb.chunk.js → 2395.0e5e8ded.chunk.js} +1 -1
  78. package/build/{2801.31393ffe.chunk.js → 2801.8e1aa82a.chunk.js} +1 -1
  79. package/build/3483.19381b40.chunk.js +1 -0
  80. package/build/3739.63e352f1.chunk.js +103 -0
  81. package/build/4174.f1f39e40.chunk.js +1 -0
  82. package/build/448.829e1344.chunk.js +1 -0
  83. package/build/4546.a5946d22.chunk.js +1 -0
  84. package/build/4724.aea5c8c1.chunk.js +6 -0
  85. package/build/{502.8dd074ff.chunk.js → 502.7bba43b1.chunk.js} +1 -1
  86. package/build/6158.c3c13c20.chunk.js +1 -0
  87. package/build/6691.4985ef22.chunk.js +105 -0
  88. package/build/{7464.592a9295.chunk.js → 7464.eb057bec.chunk.js} +1 -1
  89. package/build/78.dcc6df5c.chunk.js +1 -0
  90. package/build/{8276.e519a707.chunk.js → 8276.be3ed581.chunk.js} +2 -2
  91. package/build/{2747.d1442a90.chunk.js → 9806.5d5a0e8d.chunk.js} +64 -72
  92. package/build/9944.7af075a5.chunk.js +26 -0
  93. package/build/Admin-authenticatedApp.43b6ec9a.chunk.js +79 -0
  94. package/build/Admin_InternalErrorPage.38155af3.chunk.js +1 -0
  95. package/build/Admin_homePage.6f128523.chunk.js +81 -0
  96. package/build/Admin_marketplace.061a6e5a.chunk.js +55 -0
  97. package/build/Admin_pluginsPage.16f837b8.chunk.js +6 -0
  98. package/build/Admin_profilePage.678bce24.chunk.js +13 -0
  99. package/build/Admin_settingsPage.af7309e4.chunk.js +111 -0
  100. package/build/{Upload_ConfigureTheView.345ac1e0.chunk.js → Upload_ConfigureTheView.3fc1c100.chunk.js} +1 -1
  101. package/build/admin-app.d63bd229.chunk.js +36 -0
  102. package/build/{admin-edit-roles-page.24bdf746.chunk.js → admin-edit-roles-page.38a6c863.chunk.js} +3 -3
  103. package/build/admin-edit-users.545fc882.chunk.js +10 -0
  104. package/build/{admin-roles-list.23ddff26.chunk.js → admin-roles-list.1e2e814d.chunk.js} +1 -1
  105. package/build/admin-users.b8ea5677.chunk.js +11 -0
  106. package/build/{api-tokens-create-page.46c2ea84.chunk.js → api-tokens-create-page.e0c15627.chunk.js} +1 -1
  107. package/build/{api-tokens-edit-page.58139df9.chunk.js → api-tokens-edit-page.9f2dce47.chunk.js} +1 -1
  108. package/build/{api-tokens-list-page.0af7d431.chunk.js → api-tokens-list-page.d747051c.chunk.js} +2 -2
  109. package/build/{audit-logs-settings-page.0f73ccf8.chunk.js → audit-logs-settings-page.96f9d608.chunk.js} +1 -1
  110. package/build/content-manager.ccff1078.chunk.js +1097 -0
  111. package/build/{content-type-builder-list-view.bf9be456.chunk.js → content-type-builder-list-view.b71cf240.chunk.js} +1 -1
  112. package/build/{content-type-builder.66066281.chunk.js → content-type-builder.e5669749.chunk.js} +18 -18
  113. package/build/{email-settings-page.2f7e35c0.chunk.js → email-settings-page.2809f0bf.chunk.js} +1 -1
  114. package/build/en-json.e12fd5fc.chunk.js +1 -0
  115. package/build/{i18n-settings-page.47f78016.chunk.js → i18n-settings-page.5f716172.chunk.js} +1 -1
  116. package/build/index.html +1 -1
  117. package/build/main.c6c9e04c.js +2859 -0
  118. package/build/{review-workflows-settings-create-view.d24a32b9.chunk.js → review-workflows-settings-create-view.4a156a19.chunk.js} +1 -1
  119. package/build/{review-workflows-settings-edit-view.6044b022.chunk.js → review-workflows-settings-edit-view.ce984d1f.chunk.js} +1 -1
  120. package/build/{review-workflows-settings-list-view.3f0ef4bc.chunk.js → review-workflows-settings-list-view.419b8deb.chunk.js} +2 -2
  121. package/build/runtime~main.dcf1cb45.js +2 -0
  122. package/build/{sso-settings-page.4dba0670.chunk.js → sso-settings-page.45153df5.chunk.js} +1 -1
  123. package/build/{transfer-tokens-create-page.1597e6ab.chunk.js → transfer-tokens-create-page.ebba16d8.chunk.js} +1 -1
  124. package/build/{transfer-tokens-edit-page.8741529f.chunk.js → transfer-tokens-edit-page.d7bb2b3e.chunk.js} +1 -1
  125. package/build/{transfer-tokens-list-page.d6986b03.chunk.js → transfer-tokens-list-page.cfe1736c.chunk.js} +2 -2
  126. package/build/{upload-settings.7f93d4c0.chunk.js → upload-settings.cc5ad813.chunk.js} +1 -1
  127. package/build/{upload.37488080.chunk.js → upload.756efc28.chunk.js} +1 -1
  128. package/build/{users-advanced-settings-page.17052d72.chunk.js → users-advanced-settings-page.818d84eb.chunk.js} +1 -1
  129. package/build/{users-email-settings-page.3de8ea50.chunk.js → users-email-settings-page.c1967c09.chunk.js} +1 -1
  130. package/build/{users-providers-settings-page.0eaa916d.chunk.js → users-providers-settings-page.11893e08.chunk.js} +1 -1
  131. package/build/{users-roles-settings-page.957ad48b.chunk.js → users-roles-settings-page.2b051e6a.chunk.js} +1 -1
  132. package/build/webhook-edit-page.de45c635.chunk.js +33 -0
  133. package/build/{webhook-list-page.65e1b5bb.chunk.js → webhook-list-page.ca91df8b.chunk.js} +1 -1
  134. package/ee/admin/content-manager/components/Filter/CustomInputs/ReviewWorkflows/AssigneeFilter.js +42 -0
  135. package/ee/admin/content-manager/components/Filter/CustomInputs/ReviewWorkflows/StageFilter.js +70 -0
  136. package/ee/admin/content-manager/components/Filter/CustomInputs/ReviewWorkflows/constants.js +71 -0
  137. package/ee/admin/content-manager/pages/EditView/InformationBox/InformationBoxEE.js +9 -217
  138. package/ee/admin/content-manager/pages/EditView/InformationBox/components/AssigneeSelect/AssigneeSelect.js +147 -0
  139. package/ee/admin/content-manager/pages/EditView/InformationBox/components/AssigneeSelect/index.js +1 -0
  140. package/ee/admin/content-manager/pages/EditView/InformationBox/components/StageSelect/StageSelect.js +243 -0
  141. package/ee/admin/content-manager/pages/EditView/InformationBox/components/StageSelect/index.js +1 -0
  142. package/ee/admin/content-manager/pages/EditView/InformationBox/constants.js +2 -0
  143. package/ee/admin/content-manager/pages/ListSettingsView/constants.js +7 -0
  144. package/ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn/ReviewWorkflowsAssigneeEE.js +21 -0
  145. package/ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn/constants.js +44 -17
  146. package/ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn/index.js +1 -0
  147. package/ee/admin/pages/App/constants.js +5 -6
  148. package/ee/admin/pages/SettingsPage/constants.js +42 -27
  149. package/ee/server/constants/workflows.js +1 -0
  150. package/ee/server/controllers/index.js +1 -0
  151. package/ee/server/controllers/workflows/assignees/index.js +44 -0
  152. package/ee/server/routes/review-workflows.js +17 -0
  153. package/ee/server/services/index.js +1 -0
  154. package/ee/server/services/review-workflows/assignees.js +54 -0
  155. package/ee/server/services/review-workflows/metrics/index.js +5 -0
  156. package/ee/server/services/review-workflows/review-workflows.js +20 -11
  157. package/ee/server/validation/review-workflows.js +8 -0
  158. package/index.js +2 -6
  159. package/package.json +9 -9
  160. package/scripts/build.js +15 -15
  161. package/scripts/create-dev-plugins-file.js +5 -38
  162. package/server/controllers/role.js +2 -0
  163. package/server/controllers/user.js +2 -0
  164. package/server/services/permission/permissions-manager/index.js +3 -1
  165. package/server/services/permission/permissions-manager/sanitize.js +19 -7
  166. package/server/services/permission/permissions-manager/validate.js +218 -0
  167. package/utils/create-cache-dir.js +62 -16
  168. package/utils/create-plugins-exclude-path.js +3 -23
  169. package/utils/get-plugins.js +110 -0
  170. package/utils/index.js +1 -1
  171. package/webpack.config.js +10 -13
  172. package/admin/src/components/AuthenticatedApp.js +0 -229
  173. package/admin/src/content-manager/components/AttributeFilter/Filters.js +0 -58
  174. package/admin/src/content-manager/components/AttributeFilter/hooks/useAllowedAttributes.js +0 -42
  175. package/admin/src/content-manager/components/AttributeFilter/index.js +0 -40
  176. package/admin/src/content-manager/components/Wysiwyg/EditorStylesContainer.js +0 -344
  177. package/admin/src/pages/UseCasePage.js +0 -174
  178. package/build/2166.c837469a.chunk.js +0 -1
  179. package/build/3483.8517171f.chunk.js +0 -1
  180. package/build/3984.dda474f7.chunk.js +0 -1
  181. package/build/4546.7a3c0d03.chunk.js +0 -1
  182. package/build/5483.5bfbb00d.chunk.js +0 -6
  183. package/build/6158.c974fd83.chunk.js +0 -1
  184. package/build/748.fd2e5afd.chunk.js +0 -105
  185. package/build/773.6381d62d.chunk.js +0 -18
  186. package/build/7826.399afe81.chunk.js +0 -103
  187. package/build/8261.2525d35c.chunk.js +0 -7
  188. package/build/8299.62b67c72.chunk.js +0 -1
  189. package/build/Admin-AuthPage.90d64342.chunk.js +0 -35
  190. package/build/Admin-AuthenticatedApp.379ac945.chunk.js +0 -24
  191. package/build/Admin-UseCasePage.1f757db5.chunk.js +0 -13
  192. package/build/Admin_GuidedTourModal.8ccf1fbc.chunk.js +0 -12
  193. package/build/Admin_InternalErrorPage.9de92c6d.chunk.js +0 -9
  194. package/build/Admin_NotFoundPage.21620424.chunk.js +0 -9
  195. package/build/Admin_Onboarding.dbfa32f6.chunk.js +0 -43
  196. package/build/Admin_homePage.2000cbe9.chunk.js +0 -86
  197. package/build/Admin_marketplace.ec80e29b.chunk.js +0 -63
  198. package/build/Admin_pluginsPage.0c6851f8.chunk.js +0 -14
  199. package/build/Admin_profilePage.78cd8495.chunk.js +0 -21
  200. package/build/Admin_settingsPage.1760c3ce.chunk.js +0 -119
  201. package/build/StrapiApp.221fac30.chunk.js +0 -5
  202. package/build/admin-edit-users.5d10d444.chunk.js +0 -10
  203. package/build/admin-users.2b3e4305.chunk.js +0 -11
  204. package/build/content-manager.fb0833bd.chunk.js +0 -1099
  205. package/build/en-json.08c05fcf.chunk.js +0 -1
  206. package/build/main.ee3c1938.js +0 -2859
  207. package/build/runtime~main.397ee447.js +0 -2
  208. package/build/webhook-edit-page.665210af.chunk.js +0 -33
  209. package/scripts/create-plugins-file.js +0 -92
  210. package/utils/get-plugins-path.js +0 -41
  211. /package/server/services/permission/permissions-manager/{query-builers.js → query-builders.js} +0 -0
@@ -1,81 +1,93 @@
1
1
  import React from 'react';
2
2
 
3
- import { Select, Option, Box } from '@strapi/design-system';
4
- import { useTracking } from '@strapi/helper-plugin';
3
+ import { Flex, BaseCheckbox, TextButton, Typography } from '@strapi/design-system';
4
+ import { useCollator, useTracking } from '@strapi/helper-plugin';
5
5
  import PropTypes from 'prop-types';
6
6
  import { useIntl } from 'react-intl';
7
7
  import { useDispatch, useSelector } from 'react-redux';
8
+ import styled from 'styled-components';
8
9
 
9
- import { getTrad, checkIfAttributeIsDisplayable } from '../../../../utils';
10
- import { onChangeListHeaders } from '../../actions';
10
+ import { checkIfAttributeIsDisplayable } from '../../../../utils';
11
+ import { onChangeListHeaders, onResetListHeaders } from '../../actions';
11
12
  import { selectDisplayedHeaders } from '../../selectors';
12
13
 
14
+ const ChackboxWrapper = styled(Flex)`
15
+ :hover {
16
+ background-color: ${(props) => props.theme.colors.primary100};
17
+ }
18
+ `;
19
+
13
20
  export const FieldPicker = ({ layout }) => {
14
21
  const dispatch = useDispatch();
15
22
  const displayedHeaders = useSelector(selectDisplayedHeaders);
16
23
  const { trackUsage } = useTracking();
17
- const { formatMessage } = useIntl();
18
-
19
- const allAllowedHeaders = getAllAllowedHeaders(layout.contentType.attributes).map((attrName) => {
20
- const metadatas = layout.contentType.metadatas[attrName].list;
21
-
22
- return {
23
- name: attrName,
24
- intlLabel: { id: metadatas.label, defaultMessage: metadatas.label },
25
- };
24
+ const { formatMessage, locale } = useIntl();
25
+ const formatter = useCollator(locale, {
26
+ sensitivity: 'base',
26
27
  });
27
28
 
28
- const values = displayedHeaders.map(({ name }) => name);
29
+ const columns = Object.keys(layout.contentType.attributes)
30
+ .filter((name) => checkIfAttributeIsDisplayable(layout.contentType.attributes[name]))
31
+ .map((name) => ({
32
+ name,
33
+ label: layout.contentType.metadatas[name].list.label,
34
+ }))
35
+ .sort((a, b) => formatter.compare(a.label, b.label));
29
36
 
30
- const handleChange = (updatedValues) => {
31
- trackUsage('didChangeDisplayedFields');
37
+ const displayedHeaderKeys = displayedHeaders.map(({ name }) => name);
32
38
 
33
- // removing a header
34
- if (updatedValues.length < values.length) {
35
- const removedHeader = values.filter((value) => {
36
- return updatedValues.indexOf(value) === -1;
37
- });
38
-
39
- dispatch(onChangeListHeaders({ name: removedHeader[0], value: true }));
40
- } else {
41
- const addedHeader = updatedValues.filter((value) => {
42
- return values.indexOf(value) === -1;
43
- });
39
+ const handleChange = (name) => {
40
+ trackUsage('didChangeDisplayedFields');
41
+ dispatch(onChangeListHeaders({ name, value: displayedHeaderKeys.includes(name) }));
42
+ };
44
43
 
45
- dispatch(onChangeListHeaders({ name: addedHeader[0], value: false }));
46
- }
44
+ const handleReset = () => {
45
+ dispatch(onResetListHeaders());
47
46
  };
48
47
 
49
48
  return (
50
- <Box paddingTop={1} paddingBottom={1}>
51
- <Select
52
- aria-label="change displayed fields"
53
- value={values}
54
- onChange={handleChange}
55
- customizeContent={(values) =>
56
- formatMessage(
57
- {
58
- id: getTrad('select.currently.selected'),
59
- defaultMessage: '{count} currently selected',
60
- },
61
- { count: values.length }
62
- )
63
- }
64
- multi
65
- size="S"
66
- >
67
- {allAllowedHeaders.map((header) => {
49
+ <Flex as="fieldset" direction="column" alignItems="stretch" gap={3}>
50
+ <Flex justifyContent="space-between">
51
+ <Typography as="legend" variant="pi" fontWeight="bold">
52
+ {formatMessage({
53
+ id: 'containers.ListPage.displayedFields',
54
+ defaultMessage: 'Displayed fields',
55
+ })}
56
+ </Typography>
57
+
58
+ <TextButton onClick={handleReset}>
59
+ {formatMessage({
60
+ id: 'app.components.Button.reset',
61
+ defaultMessage: 'Reset',
62
+ })}
63
+ </TextButton>
64
+ </Flex>
65
+
66
+ <Flex direction="column" alignItems="stretch">
67
+ {columns.map((header) => {
68
+ const isActive = displayedHeaderKeys.includes(header.name);
69
+
68
70
  return (
69
- <Option key={header.name} value={header.name}>
70
- {formatMessage({
71
- id: header.intlLabel.id || header.name,
72
- defaultMessage: header.intlLabel.defaultMessage || header.name,
73
- })}
74
- </Option>
71
+ <ChackboxWrapper
72
+ wrap="wrap"
73
+ gap={2}
74
+ as="label"
75
+ background={isActive ? 'primary100' : 'transparent'}
76
+ hasRadius
77
+ padding={2}
78
+ key={header.name}
79
+ >
80
+ <BaseCheckbox
81
+ onChange={() => handleChange(header.name)}
82
+ value={isActive}
83
+ name={header.name}
84
+ />
85
+ <Typography fontSize={1}>{header.label}</Typography>
86
+ </ChackboxWrapper>
75
87
  );
76
88
  })}
77
- </Select>
78
- </Box>
89
+ </Flex>
90
+ </Flex>
79
91
  );
80
92
  };
81
93
 
@@ -92,17 +104,3 @@ FieldPicker.propTypes = {
92
104
  }).isRequired,
93
105
  }).isRequired,
94
106
  };
95
-
96
- const getAllAllowedHeaders = (attributes) => {
97
- const allowedAttributes = Object.keys(attributes).reduce((acc, current) => {
98
- const attribute = attributes[current];
99
-
100
- if (checkIfAttributeIsDisplayable(attribute)) {
101
- acc.push(current);
102
- }
103
-
104
- return acc;
105
- }, []);
106
-
107
- return allowedAttributes.sort();
108
- };
@@ -23,7 +23,7 @@ import { useIntl } from 'react-intl';
23
23
  import { Link, useHistory } from 'react-router-dom';
24
24
 
25
25
  import { useEnterprise } from '../../../../../hooks/useEnterprise';
26
- import { getFullName } from '../../../../../utils/getFullName';
26
+ import { getFullName } from '../../../../../utils';
27
27
  import { usePluginsQueryParams } from '../../../../hooks';
28
28
  import { getTrad } from '../../../../utils';
29
29
  import CellContent from '../CellContent';
@@ -0,0 +1,80 @@
1
+ import React from 'react';
2
+
3
+ import { Flex, IconButton, Popover } from '@strapi/design-system';
4
+ import { CheckPermissions, LinkButton } from '@strapi/helper-plugin';
5
+ import { Cog, Layer } from '@strapi/icons';
6
+ import PropTypes from 'prop-types';
7
+ import { useIntl } from 'react-intl';
8
+ import { useSelector } from 'react-redux';
9
+
10
+ import { selectAdminPermissions } from '../../../../../pages/App/selectors';
11
+ import { FieldPicker } from '../FieldPicker';
12
+
13
+ export const ViewSettingsMenu = ({ slug, layout }) => {
14
+ const [isVisible, setIsVisible] = React.useState(false);
15
+ const cogButtonRef = React.useRef();
16
+ const permissions = useSelector(selectAdminPermissions);
17
+ const { formatMessage } = useIntl();
18
+
19
+ const handleToggle = () => {
20
+ setIsVisible((prev) => !prev);
21
+ };
22
+
23
+ return (
24
+ <>
25
+ <IconButton
26
+ icon={<Cog />}
27
+ label={formatMessage({
28
+ id: 'components.ViewSettings.tooltip',
29
+ defaultMessage: 'View Settings',
30
+ })}
31
+ ref={cogButtonRef}
32
+ onClick={handleToggle}
33
+ />
34
+ {isVisible && (
35
+ <Popover
36
+ placement="bottom-end"
37
+ source={cogButtonRef}
38
+ onDismiss={handleToggle}
39
+ spacing={4}
40
+ padding={3}
41
+ >
42
+ <Flex alignItems="stretch" direction="column" gap={3}>
43
+ <CheckPermissions
44
+ permissions={permissions.contentManager.collectionTypesConfigurations}
45
+ >
46
+ <LinkButton
47
+ size="S"
48
+ startIcon={<Layer />}
49
+ to={`${slug}/configurations/list`}
50
+ variant="secondary"
51
+ >
52
+ {formatMessage({
53
+ id: 'app.links.configure-view',
54
+ defaultMessage: 'Configure the view',
55
+ })}
56
+ </LinkButton>
57
+ </CheckPermissions>
58
+
59
+ <FieldPicker layout={layout} />
60
+ </Flex>
61
+ </Popover>
62
+ )}
63
+ </>
64
+ );
65
+ };
66
+
67
+ ViewSettingsMenu.propTypes = {
68
+ slug: PropTypes.string.isRequired,
69
+ layout: PropTypes.shape({
70
+ contentType: PropTypes.shape({
71
+ attributes: PropTypes.object.isRequired,
72
+ metadatas: PropTypes.object.isRequired,
73
+ layouts: PropTypes.shape({
74
+ list: PropTypes.array.isRequired,
75
+ }).isRequired,
76
+ options: PropTypes.object.isRequired,
77
+ settings: PropTypes.object.isRequired,
78
+ }).isRequired,
79
+ }).isRequired,
80
+ };
@@ -1,9 +1,7 @@
1
1
  import * as React from 'react';
2
2
 
3
3
  import {
4
- IconButton,
5
4
  Main,
6
- Box,
7
5
  ActionLayout,
8
6
  Button,
9
7
  ContentLayout,
@@ -17,8 +15,8 @@ import {
17
15
  lightTheme,
18
16
  } from '@strapi/design-system';
19
17
  import {
18
+ findMatchingPermissions,
20
19
  NoPermissions,
21
- CheckPermissions,
22
20
  SearchURLQuery,
23
21
  useFetchClient,
24
22
  useFocusWhenNavigate,
@@ -28,12 +26,13 @@ import {
28
26
  useTracking,
29
27
  Link,
30
28
  useAPIErrorHandler,
29
+ useCollator,
31
30
  useStrapiApp,
32
31
  Table,
33
32
  PaginationURLQuery,
34
33
  PageSizeURLQuery,
35
34
  } from '@strapi/helper-plugin';
36
- import { ArrowLeft, Cog, Plus } from '@strapi/icons';
35
+ import { ArrowLeft, Plus } from '@strapi/icons';
37
36
  import axios, { AxiosError } from 'axios';
38
37
  import isEqual from 'lodash/isEqual';
39
38
  import PropTypes from 'prop-types';
@@ -43,33 +42,29 @@ import { useMutation } from 'react-query';
43
42
  import { connect, useSelector } from 'react-redux';
44
43
  import { useHistory, useLocation, Link as ReactRouterLink } from 'react-router-dom';
45
44
  import { bindActionCreators, compose } from 'redux';
46
- import styled from 'styled-components';
47
45
 
48
46
  import { INJECT_COLUMN_IN_TABLE } from '../../../exposedHooks';
47
+ import { useAdminUsers } from '../../../hooks/useAdminUsers';
49
48
  import { useEnterprise } from '../../../hooks/useEnterprise';
50
- import { selectAdminPermissions } from '../../../pages/App/selectors';
51
49
  import { InjectionZone } from '../../../shared/components';
52
- import AttributeFilter from '../../components/AttributeFilter';
53
- import { getTrad } from '../../utils';
50
+ import { Filter } from '../../components/Filter';
51
+ import { AdminUsersFilter } from '../../components/Filter/CustomInputs/AdminUsersFilter';
52
+ import { useAllowedAttributes } from '../../hooks/useAllowedAttributes';
53
+ import { getTrad, getDisplayName } from '../../utils';
54
54
 
55
55
  import { getData, getDataSucceeded, onChangeListHeaders, onResetListHeaders } from './actions';
56
56
  import { Body } from './components/Body';
57
57
  import BulkActionButtons from './components/BulkActionButtons';
58
58
  import CellContent from './components/CellContent';
59
- import { FieldPicker } from './components/FieldPicker';
59
+ import { ViewSettingsMenu } from './components/ViewSettingsMenu';
60
60
  import makeSelectListView, { selectDisplayedHeaders } from './selectors';
61
61
  import { buildValidGetParams } from './utils';
62
62
 
63
- const ConfigureLayoutBox = styled(Box)`
64
- svg {
65
- path {
66
- fill: ${({ theme }) => theme.colors.neutral900};
67
- }
68
- }
69
- `;
70
-
71
63
  const REVIEW_WORKFLOW_COLUMNS_CE = null;
72
64
  const REVIEW_WORKFLOW_COLUMNS_CELL_CE = () => null;
65
+ const REVIEW_WORKFLOW_FILTER_CE = [];
66
+ const CREATOR_ATTRIBUTES = ['createdBy', 'updatedBy'];
67
+ const USER_FILTER_ATTRIBUTES = [...CREATOR_ATTRIBUTES, 'strapi_assignee'];
73
68
 
74
69
  function ListView({
75
70
  canCreate,
@@ -95,23 +90,106 @@ function ListView({
95
90
  const [isConfirmDeleteRowOpen, setIsConfirmDeleteRowOpen] = React.useState(false);
96
91
  const toggleNotification = useNotification();
97
92
  const { trackUsage } = useTracking();
98
- const { refetchPermissions } = useRBACProvider();
93
+ const { allPermissions, refetchPermissions } = useRBACProvider();
99
94
  const trackUsageRef = React.useRef(trackUsage);
100
95
  const fetchPermissionsRef = React.useRef(refetchPermissions);
101
96
  const { notifyStatus } = useNotifyAT();
102
97
  const { formatAPIError } = useAPIErrorHandler(getTrad);
103
- const permissions = useSelector(selectAdminPermissions);
98
+ const allowedAttributes = useAllowedAttributes(contentType, slug);
99
+ const [{ query }] = useQueryParams();
100
+ const { pathname } = useLocation();
101
+ const { push } = useHistory();
102
+ const { formatMessage, locale } = useIntl();
103
+ const fetchClient = useFetchClient();
104
+ const formatter = useCollator(locale, {
105
+ sensitivity: 'base',
106
+ });
107
+
108
+ const selectedUserIds =
109
+ query?.filters?.$and?.reduce((acc, filter) => {
110
+ const [key, value] = Object.entries(filter)[0];
111
+ const id = value.id?.$eq || value.id?.$ne;
112
+
113
+ // TODO: strapi_assignee should not be in here and rather defined
114
+ // in the ee directory.
115
+ if (USER_FILTER_ATTRIBUTES.includes(key) && !acc.includes(id)) {
116
+ acc.push(id);
117
+ }
118
+
119
+ return acc;
120
+ }, []) ?? [];
121
+
122
+ const { users, isLoading: isLoadingAdminUsers } = useAdminUsers(
123
+ { filter: { id: { in: selectedUserIds } } },
124
+ {
125
+ // fetch the list of admin users only if the filter contains users and the
126
+ // current user has permissions to display users
127
+ enabled:
128
+ selectedUserIds.length > 0 &&
129
+ findMatchingPermissions(allPermissions, [
130
+ {
131
+ action: 'admin::users.read',
132
+ subject: null,
133
+ },
134
+ ]).length > 0,
135
+ }
136
+ );
104
137
 
105
138
  useFocusWhenNavigate();
106
139
 
107
- const [{ query }] = useQueryParams();
108
140
  const params = React.useMemo(() => buildValidGetParams(query), [query]);
109
141
  const pluginsQueryParams = stringify({ plugins: query.plugins }, { encode: false });
110
142
 
111
- const { pathname } = useLocation();
112
- const { push } = useHistory();
113
- const { formatMessage } = useIntl();
114
- const fetchClient = useFetchClient();
143
+ const displayedAttributeFilters = allowedAttributes.map((name) => {
144
+ const attribute = contentType.attributes[name];
145
+ const { type, enum: options } = attribute;
146
+
147
+ const trackedEvent = {
148
+ name: 'didFilterEntries',
149
+ properties: { useRelation: type === 'relation' },
150
+ };
151
+
152
+ const { mainField, label } = metadatas[name].list;
153
+
154
+ const filter = {
155
+ name,
156
+ metadatas: { label: formatMessage({ id: label, defaultMessage: label }) },
157
+ fieldSchema: { type, options, mainField },
158
+ trackedEvent,
159
+ };
160
+
161
+ if (attribute.type === 'relation' && attribute.target === 'admin::user') {
162
+ filter.metadatas = {
163
+ ...filter.metadatas,
164
+ customOperators: [
165
+ {
166
+ intlLabel: {
167
+ id: 'components.FilterOptions.FILTER_TYPES.$eq',
168
+ defaultMessage: 'is',
169
+ },
170
+ value: '$eq',
171
+ },
172
+ {
173
+ intlLabel: {
174
+ id: 'components.FilterOptions.FILTER_TYPES.$ne',
175
+ defaultMessage: 'is not',
176
+ },
177
+ value: '$ne',
178
+ },
179
+ ],
180
+ customInput: AdminUsersFilter,
181
+ options: users.map((user) => ({
182
+ label: getDisplayName(user, formatMessage),
183
+ customValue: user.id.toString(),
184
+ })),
185
+ };
186
+ filter.fieldSchema.mainField = {
187
+ name: 'id',
188
+ };
189
+ }
190
+
191
+ return filter;
192
+ });
115
193
 
116
194
  const hasDraftAndPublish = options?.draftAndPublish ?? false;
117
195
  const hasReviewWorkflows = options?.reviewWorkflows ?? false;
@@ -128,16 +206,82 @@ function ListView({
128
206
  enabled: !!options?.reviewWorkflows,
129
207
  }
130
208
  );
131
- const ReviewWorkflowsStage = useEnterprise(
209
+ const ReviewWorkflowsColumns = useEnterprise(
132
210
  REVIEW_WORKFLOW_COLUMNS_CELL_CE,
133
- async () =>
134
- (await import('../../../../../ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn'))
135
- .ReviewWorkflowsStageEE,
211
+ async () => {
212
+ const { ReviewWorkflowsStageEE, ReviewWorkflowsAssigneeEE } = await import(
213
+ '../../../../../ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn'
214
+ );
215
+
216
+ return { ReviewWorkflowsStageEE, ReviewWorkflowsAssigneeEE };
217
+ },
136
218
  {
137
219
  enabled: hasReviewWorkflows,
138
220
  }
139
221
  );
140
222
 
223
+ const reviewWorkflowFilter = useEnterprise(
224
+ REVIEW_WORKFLOW_FILTER_CE,
225
+ async () =>
226
+ (
227
+ await import(
228
+ '../../../../../ee/admin/content-manager/components/Filter/CustomInputs/ReviewWorkflows/constants'
229
+ )
230
+ ).REVIEW_WORKFLOW_FILTERS,
231
+ {
232
+ combine(ceFilters, eeFilters) {
233
+ return [
234
+ ...ceFilters,
235
+ ...eeFilters
236
+ .filter((eeFilter) => {
237
+ // do not display the filter at all, if the current user does
238
+ // not have permissions to read admin users
239
+ if (eeFilter.name === 'strapi_assignee') {
240
+ return (
241
+ findMatchingPermissions(allPermissions, [
242
+ {
243
+ action: 'admin::users.read',
244
+ subject: null,
245
+ },
246
+ ]).length > 0
247
+ );
248
+ }
249
+
250
+ return true;
251
+ })
252
+ .map((eeFilter) => ({
253
+ ...eeFilter,
254
+ metadatas: {
255
+ ...eeFilter.metadatas,
256
+ // the stage filter needs the current content-type uid to fetch
257
+ // the list of stages that can be assigned to this content-type
258
+ ...(eeFilter.name === 'strapi_stage' ? { uid: contentType.uid } : {}),
259
+
260
+ // translate the filter label
261
+ label: formatMessage(eeFilter.metadatas.label),
262
+
263
+ // `options` allows the filter-tag to render the displayname
264
+ // of a user over a plain id
265
+ options:
266
+ eeFilter.name === 'strapi_assignee' &&
267
+ users.map((user) => ({
268
+ label: getDisplayName(user, formatMessage),
269
+ customValue: user.id.toString(),
270
+ })),
271
+ },
272
+ })),
273
+ ];
274
+ },
275
+
276
+ defaultValue: [],
277
+
278
+ // we have to wait for admin users to be fully loaded, because otherwise
279
+ // combine is called to early and does not contain the latest state of
280
+ // the users array
281
+ enabled: hasReviewWorkflows && !isLoadingAdminUsers,
282
+ }
283
+ );
284
+
141
285
  const { post, del } = fetchClient;
142
286
 
143
287
  const bulkUnpublishMutation = useMutation(
@@ -381,13 +525,15 @@ function ListView({
381
525
 
382
526
  if (reviewWorkflowColumns) {
383
527
  // Make sure the column header label is translated
384
- if (typeof reviewWorkflowColumns.metadatas.label !== 'string') {
385
- reviewWorkflowColumns.metadatas.label = formatMessage(
386
- reviewWorkflowColumns.metadatas.label
387
- );
388
- }
528
+ reviewWorkflowColumns.map((column) => {
529
+ if (typeof column.metadatas.label !== 'string') {
530
+ column.metadatas.label = formatMessage(column.metadatas.label);
531
+ }
389
532
 
390
- formattedHeaders.push(reviewWorkflowColumns);
533
+ return column;
534
+ });
535
+
536
+ formattedHeaders.push(...reviewWorkflowColumns);
391
537
  }
392
538
 
393
539
  return formattedHeaders;
@@ -481,7 +627,7 @@ function ListView({
481
627
  };
482
628
 
483
629
  // Block rendering until the review stage component is fully loaded in EE
484
- if (!ReviewWorkflowsStage) {
630
+ if (!ReviewWorkflowsColumns) {
485
631
  return null;
486
632
  }
487
633
 
@@ -508,25 +654,7 @@ function ListView({
508
654
  endActions={
509
655
  <>
510
656
  <InjectionZone area="contentManager.listView.actions" />
511
- <FieldPicker layout={layout} />
512
- <CheckPermissions
513
- permissions={permissions.contentManager.collectionTypesConfigurations}
514
- >
515
- <ConfigureLayoutBox paddingTop={1} paddingBottom={1}>
516
- <IconButton
517
- onClick={() => {
518
- trackUsage('willEditListLayout');
519
- }}
520
- forwardedAs={ReactRouterLink}
521
- to={{ pathname: `${slug}/configurations/list`, search: pluginsQueryParams }}
522
- icon={<Cog />}
523
- label={formatMessage({
524
- id: 'app.links.configure-view',
525
- defaultMessage: 'Configure the view',
526
- })}
527
- />
528
- </ConfigureLayoutBox>
529
- </CheckPermissions>
657
+ <ViewSettingsMenu slug={slug} layout={layout} />
530
658
  </>
531
659
  }
532
660
  startActions={
@@ -544,8 +672,12 @@ function ListView({
544
672
  trackedEvent="didSearch"
545
673
  />
546
674
  )}
547
- {isFilterable && (
548
- <AttributeFilter contentType={contentType} slug={slug} metadatas={metadatas} />
675
+ {isFilterable && !isLoadingAdminUsers && (
676
+ <Filter
677
+ displayedFilters={[...displayedAttributeFilters, ...reviewWorkflowFilter].sort(
678
+ (a, b) => formatter.compare(a.metadatas.label, b.metadatas.label)
679
+ )}
680
+ />
549
681
  )}
550
682
  </>
551
683
  }
@@ -631,19 +763,56 @@ function ListView({
631
763
  );
632
764
  }
633
765
 
634
- if (hasReviewWorkflows && name === 'strapi_stage') {
766
+ if (hasReviewWorkflows) {
767
+ if (name === 'strapi_stage') {
768
+ return (
769
+ <Td key={key}>
770
+ {rowData.strapi_stage ? (
771
+ <ReviewWorkflowsColumns.ReviewWorkflowsStageEE
772
+ color={
773
+ rowData.strapi_stage.color ?? lightTheme.colors.primary600
774
+ }
775
+ name={rowData.strapi_stage.name}
776
+ />
777
+ ) : (
778
+ <Typography textColor="neutral800">-</Typography>
779
+ )}
780
+ </Td>
781
+ );
782
+ }
783
+ if (name === 'strapi_assignee') {
784
+ return (
785
+ <Td key={key}>
786
+ {rowData.strapi_assignee ? (
787
+ <ReviewWorkflowsColumns.ReviewWorkflowsAssigneeEE
788
+ user={rowData.strapi_assignee}
789
+ />
790
+ ) : (
791
+ <Typography textColor="neutral800">-</Typography>
792
+ )}
793
+ </Td>
794
+ );
795
+ }
796
+ }
797
+
798
+ if (['createdBy', 'updatedBy'].includes(name.split('.')[0])) {
799
+ // Display the users full name
800
+ return (
801
+ <Td key={key}>
802
+ <Typography textColor="neutral800">
803
+ {getDisplayName(rowData[name.split('.')[0]], formatMessage)}
804
+ </Typography>
805
+ </Td>
806
+ );
807
+ }
808
+
809
+ if (['createdBy', 'updatedBy'].includes(name.split('.')[0])) {
810
+ // Display the users full name
635
811
  return (
636
812
  <Td key={key}>
637
- {rowData.strapi_stage ? (
638
- <ReviewWorkflowsStage
639
- color={
640
- rowData.strapi_stage.color ?? lightTheme.colors.primary600
641
- }
642
- name={rowData.strapi_stage.name}
643
- />
644
- ) : (
645
- <Typography textColor="neutral800">-</Typography>
646
- )}
813
+ <Typography textColor="neutral800">
814
+ {getDisplayName(rowData[name.split('.')[0]], formatMessage)}
815
+ </Typography>
647
816
  </Td>
648
817
  );
649
818
  }