@strapi/admin 4.11.3 → 4.11.5

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 (115) hide show
  1. package/admin/src/components/AuthenticatedApp/index.js +2 -2
  2. package/admin/src/content-manager/components/CollectionTypeFormWrapper/index.js +8 -5
  3. package/admin/src/content-manager/components/Inputs/index.js +3 -47
  4. package/admin/src/content-manager/components/RelationInput/RelationInput.js +99 -178
  5. package/admin/src/content-manager/components/RelationInput/components/Option.js +17 -15
  6. package/admin/src/content-manager/components/RelationInput/components/RelationList.js +2 -2
  7. package/admin/src/content-manager/components/SingleTypeFormWrapper/index.js +34 -37
  8. package/admin/src/content-manager/pages/EditSettingsView/components/ModalForm.js +0 -27
  9. package/admin/src/content-manager/pages/ListView/components/TableRows/index.js +93 -14
  10. package/admin/src/content-manager/pages/ListView/index.js +65 -47
  11. package/admin/src/content-manager/pages/ListView/utils/buildValidGetParams.js +30 -0
  12. package/admin/src/content-manager/pages/ListView/utils/index.js +1 -1
  13. package/admin/src/hooks/useAdminUsers/useAdminUsers.js +3 -3
  14. package/admin/src/hooks/useEnterprise/useEnterprise.js +4 -4
  15. package/admin/src/hooks/useSettingsMenu/index.js +35 -21
  16. package/admin/src/pages/App/index.js +0 -3
  17. package/admin/src/pages/AuthPage/components/Register/index.js +5 -1
  18. package/admin/src/pages/ProfilePage/index.js +12 -12
  19. package/admin/src/pages/SettingsPage/components/SettingsNav/index.js +13 -11
  20. package/admin/src/pages/SettingsPage/components/Tokens/Table/index.js +15 -1
  21. package/admin/src/pages/SettingsPage/components/Tokens/TokenBox/index.js +1 -1
  22. package/admin/src/pages/SettingsPage/pages/Roles/ProtectedEditPage/index.js +4 -10
  23. package/admin/src/pages/SettingsPage/pages/Users/EditPage/index.js +2 -2
  24. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/WebhookForm/utils/makeWebhookValidationSchema.js +11 -5
  25. package/admin/src/translations/ca.json +1 -0
  26. package/admin/src/translations/en.json +4 -1
  27. package/admin/src/translations/es.json +5 -0
  28. package/admin/src/translations/fr.json +1 -0
  29. package/admin/src/translations/zh-Hans.json +1 -1
  30. package/build/2799.cf9b491f.chunk.js +1 -0
  31. package/build/539.865446c0.chunk.js +1 -0
  32. package/build/{5542.64b623c9.chunk.js → 5542.c62d0daf.chunk.js} +1 -1
  33. package/build/{5563.86f9aa9c.chunk.js → 5563.27e2de0c.chunk.js} +3 -3
  34. package/build/5932.f8be7e31.chunk.js +1 -0
  35. package/build/7259.0e25ab5d.chunk.js +1 -0
  36. package/build/{6405.27e1bee5.chunk.js → 970.89601f27.chunk.js} +2 -2
  37. package/build/{9932.7e2b71de.chunk.js → 9932.9f3790a5.chunk.js} +81 -81
  38. package/build/9944.29289a16.chunk.js +26 -0
  39. package/build/Admin-authenticatedApp.7f04c595.chunk.js +79 -0
  40. package/build/{Admin_profilePage.2131eb68.chunk.js → Admin_profilePage.0adb571e.chunk.js} +2 -2
  41. package/build/{Admin_settingsPage.4069bb8a.chunk.js → Admin_settingsPage.36152558.chunk.js} +9 -9
  42. package/build/admin-app.3ea6009c.chunk.js +61 -0
  43. package/build/{admin-edit-roles-page.2040034a.chunk.js → admin-edit-roles-page.3fdd6b9d.chunk.js} +11 -11
  44. package/build/admin-edit-users.78552758.chunk.js +10 -0
  45. package/build/admin-users.c23322fc.chunk.js +11 -0
  46. package/build/api-tokens-list-page.a103f526.chunk.js +16 -0
  47. package/build/audit-logs-settings-page.37fe915c.chunk.js +1 -0
  48. package/build/ca-json.1fed5d8b.chunk.js +1 -0
  49. package/build/content-manager.552c7418.chunk.js +1094 -0
  50. package/build/{content-type-builder-list-view.0c3ceb4e.chunk.js → content-type-builder-list-view.82cfadc0.chunk.js} +7 -7
  51. package/build/content-type-builder.238687f9.chunk.js +166 -0
  52. package/build/{email-settings-page.6b38222d.chunk.js → email-settings-page.45695daa.chunk.js} +1 -1
  53. package/build/en-json.fb9f6ddd.chunk.js +1 -0
  54. package/build/es-json.42096084.chunk.js +1 -0
  55. package/build/fr-json.69789980.chunk.js +1 -0
  56. package/build/{i18n-settings-page.ff863f20.chunk.js → i18n-settings-page.29308d0b.chunk.js} +1 -1
  57. package/build/index.html +1 -1
  58. package/build/main.3d752c03.js +2927 -0
  59. package/build/runtime~main.87232977.js +2 -0
  60. package/build/sso-settings-page.0cdb96a6.chunk.js +1 -0
  61. package/build/transfer-tokens-list-page.7237443d.chunk.js +16 -0
  62. package/build/{upload-settings.43cf16cd.chunk.js → upload-settings.cb6c14c3.chunk.js} +1 -1
  63. package/build/{upload.72f8f8fc.chunk.js → upload.7e629643.chunk.js} +2 -2
  64. package/build/users-advanced-settings-page.750b1f76.chunk.js +9 -0
  65. package/build/{users-email-settings-page.33359797.chunk.js → users-email-settings-page.e9bcd865.chunk.js} +1 -1
  66. package/build/{users-providers-settings-page.1e7a4a71.chunk.js → users-providers-settings-page.a94253e9.chunk.js} +1 -1
  67. package/build/{users-roles-settings-page.235378b6.chunk.js → users-roles-settings-page.d286426a.chunk.js} +5 -5
  68. package/build/webhook-edit-page.77ef4f1a.chunk.js +33 -0
  69. package/build/{zh-Hans-json.4cfef87d.chunk.js → zh-Hans-json.fada6f40.chunk.js} +1 -1
  70. package/ee/admin/content-manager/{components/DynamicTable/CellContent/ReviewWorkflowsStage → pages/ListView/ReviewWorkflowsColumn}/ReviewWorkflowsStageEE.js +7 -2
  71. package/ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn/constants.js +24 -0
  72. package/ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn/index.js +1 -0
  73. package/ee/admin/hooks/useLicenseLimitNotification/index.js +17 -6
  74. package/ee/admin/hooks/useLicenseLimits/index.js +1 -32
  75. package/ee/admin/hooks/useLicenseLimits/useLicenseLimits.js +44 -0
  76. package/ee/admin/pages/SettingsPage/pages/ApplicationInfosPage/components/AdminSeatInfo/index.js +6 -4
  77. package/ee/admin/pages/SettingsPage/pages/AuditLogs/ListView/hooks/useAuditLogsData.js +6 -4
  78. package/ee/admin/pages/SettingsPage/pages/AuditLogs/ListView/index.js +4 -9
  79. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/hooks/useReviewWorkflows.js +3 -5
  80. package/ee/admin/pages/SettingsPage/pages/SingleSignOn/index.js +4 -9
  81. package/ee/admin/pages/SettingsPage/pages/Users/ListPage/CreateAction/index.js +9 -2
  82. package/ee/server/services/review-workflows/entity-service-decorator.js +20 -11
  83. package/package.json +14 -15
  84. package/server/content-types/User.js +10 -0
  85. package/server/strategies/api-token.js +9 -5
  86. package/server/strategies/data-transfer.js +9 -5
  87. package/admin/src/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/getTableColumn.js +0 -2
  88. package/admin/src/content-manager/components/RelationInput/components/Relation.js +0 -53
  89. package/admin/src/content-manager/pages/ListView/utils/buildQueryString.js +0 -36
  90. package/admin/src/content-manager/pages/ListView/utils/createPluginsFilter.js +0 -4
  91. package/admin/src/pages/App/utils/index.js +0 -3
  92. package/admin/src/pages/App/utils/unique-identifier.js +0 -12
  93. package/build/1799.84268ad3.chunk.js +0 -33
  94. package/build/5932.6a23b88c.chunk.js +0 -1
  95. package/build/7259.fb69d4bf.chunk.js +0 -1
  96. package/build/Admin-authenticatedApp.69855f1b.chunk.js +0 -79
  97. package/build/admin-app.fea867af.chunk.js +0 -61
  98. package/build/admin-edit-users.53e4290a.chunk.js +0 -10
  99. package/build/admin-users.3b12dca2.chunk.js +0 -11
  100. package/build/api-tokens-list-page.201fb67a.chunk.js +0 -16
  101. package/build/audit-logs-settings-page.b07ad202.chunk.js +0 -1
  102. package/build/ca-json.43e14418.chunk.js +0 -1
  103. package/build/content-manager.66cec770.chunk.js +0 -1094
  104. package/build/content-type-builder.e1b6d13b.chunk.js +0 -166
  105. package/build/en-json.f5fa476a.chunk.js +0 -1
  106. package/build/es-json.715b6fd8.chunk.js +0 -1
  107. package/build/fr-json.73494bf5.chunk.js +0 -1
  108. package/build/main.83edb3fc.js +0 -2926
  109. package/build/runtime~main.20c3cac6.js +0 -2
  110. package/build/sso-settings-page.35b67909.chunk.js +0 -1
  111. package/build/transfer-tokens-list-page.217573c3.chunk.js +0 -16
  112. package/build/users-advanced-settings-page.1911adf5.chunk.js +0 -9
  113. package/build/webhook-edit-page.1ee02c4b.chunk.js +0 -33
  114. package/ee/admin/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/getTableColumn.js +0 -58
  115. package/ee/admin/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/index.js +0 -3
@@ -1,4 +1,4 @@
1
- import { memo, useCallback, useEffect, useRef, useState } from 'react';
1
+ import { memo, useCallback, useEffect, useRef, useState, useMemo } from 'react';
2
2
 
3
3
  import {
4
4
  formatContentTypeData,
@@ -16,7 +16,7 @@ import { useQueryClient } from 'react-query';
16
16
  import { useDispatch, useSelector } from 'react-redux';
17
17
  import { useHistory } from 'react-router-dom';
18
18
 
19
- import buildQueryString from '../../pages/ListView/utils/buildQueryString';
19
+ import { buildValidGetParams } from '../../pages/ListView/utils';
20
20
  import {
21
21
  getData,
22
22
  getDataSucceeded,
@@ -40,7 +40,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
40
40
  const trackUsageRef = useRef(trackUsage);
41
41
  const [isCreatingEntry, setIsCreatingEntry] = useState(true);
42
42
  const [{ query, rawQuery }] = useQueryParams();
43
- const searchToSend = buildQueryString(query);
43
+ const params = useMemo(() => buildValidGetParams(query), [query]);
44
44
  const toggleNotification = useNotification();
45
45
  const dispatch = useDispatch();
46
46
  const { formatAPIError } = useAPIErrorHandler(getTrad);
@@ -110,8 +110,9 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
110
110
  setIsCreatingEntry(true);
111
111
 
112
112
  try {
113
- const { data } = await fetchClient.get(getRequestUrl(`${slug}${searchToSend}`), {
113
+ const { data } = await fetchClient.get(getRequestUrl(slug), {
114
114
  cancelToken: source.token,
115
+ params,
115
116
  });
116
117
 
117
118
  dispatch(getDataSucceeded(cleanReceivedData(data)));
@@ -143,16 +144,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
143
144
  fetchData(source);
144
145
 
145
146
  return () => source.cancel('Operation canceled by the user.');
146
- }, [
147
- fetchClient,
148
- cleanReceivedData,
149
- push,
150
- slug,
151
- dispatch,
152
- searchToSend,
153
- rawQuery,
154
- toggleNotification,
155
- ]);
147
+ }, [fetchClient, cleanReceivedData, push, slug, dispatch, params, rawQuery, toggleNotification]);
156
148
 
157
149
  const displayErrors = useCallback(
158
150
  (err) => {
@@ -166,7 +158,9 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
166
158
  try {
167
159
  trackUsageRef.current('willDeleteEntry', trackerProperty);
168
160
 
169
- const { data } = await del(getRequestUrl(`${slug}${searchToSend}`));
161
+ const { data } = await del(getRequestUrl(slug), {
162
+ params: query,
163
+ });
170
164
 
171
165
  toggleNotification({
172
166
  type: 'success',
@@ -187,17 +181,17 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
187
181
  return Promise.reject(err);
188
182
  }
189
183
  },
190
- [del, slug, displayErrors, toggleNotification, searchToSend, dispatch, rawQuery]
184
+ [del, slug, displayErrors, toggleNotification, query, dispatch, rawQuery]
191
185
  );
192
186
 
193
187
  const onPost = useCallback(
194
188
  async (body, trackerProperty) => {
195
- const endPoint = getRequestUrl(`${slug}${rawQuery}`);
189
+ const endPoint = getRequestUrl(slug);
196
190
 
197
191
  try {
198
192
  dispatch(setStatus('submit-pending'));
199
193
 
200
- const { data } = await put(endPoint, body);
194
+ const { data } = await put(endPoint, body, { params: query });
201
195
 
202
196
  trackUsageRef.current('didCreateEntry', trackerProperty);
203
197
  toggleNotification({
@@ -232,7 +226,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
232
226
  displayErrors,
233
227
  slug,
234
228
  dispatch,
235
- rawQuery,
229
+ query,
236
230
  toggleNotification,
237
231
  setCurrentStep,
238
232
  queryClient,
@@ -263,11 +257,17 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
263
257
  const onPublish = useCallback(async () => {
264
258
  try {
265
259
  trackUsageRef.current('willPublishEntry');
266
- const endPoint = getRequestUrl(`${slug}/actions/publish${searchToSend}`);
260
+ const endPoint = getRequestUrl(`${slug}/actions/publish`);
267
261
 
268
262
  dispatch(setStatus('publish-pending'));
269
263
 
270
- const { data } = await post(endPoint);
264
+ const { data } = await post(
265
+ endPoint,
266
+ {},
267
+ {
268
+ params: query,
269
+ }
270
+ );
271
271
 
272
272
  trackUsageRef.current('didPublishEntry');
273
273
  toggleNotification({
@@ -287,18 +287,18 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
287
287
 
288
288
  return Promise.reject(err);
289
289
  }
290
- }, [post, cleanReceivedData, displayErrors, slug, searchToSend, dispatch, toggleNotification]);
290
+ }, [post, cleanReceivedData, displayErrors, slug, query, dispatch, toggleNotification]);
291
291
 
292
292
  const onPut = useCallback(
293
293
  async (body, trackerProperty) => {
294
- const endPoint = getRequestUrl(`${slug}${rawQuery}`);
294
+ const endPoint = getRequestUrl(slug);
295
295
 
296
296
  try {
297
297
  trackUsageRef.current('willEditEntry', trackerProperty);
298
298
 
299
299
  dispatch(setStatus('submit-pending'));
300
300
 
301
- const { data } = await put(endPoint, body);
301
+ const { data } = await put(endPoint, body, { params: query });
302
302
 
303
303
  toggleNotification({
304
304
  type: 'success',
@@ -325,28 +325,25 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
325
325
  return Promise.reject(err);
326
326
  }
327
327
  },
328
- [
329
- put,
330
- cleanReceivedData,
331
- displayErrors,
332
- slug,
333
- dispatch,
334
- rawQuery,
335
- toggleNotification,
336
- queryClient,
337
- ]
328
+ [put, cleanReceivedData, displayErrors, slug, dispatch, query, toggleNotification, queryClient]
338
329
  );
339
330
 
340
331
  // The publish and unpublish method could be refactored but let's leave the duplication for now
341
332
  const onUnpublish = useCallback(async () => {
342
- const endPoint = getRequestUrl(`${slug}/actions/unpublish${searchToSend}`);
333
+ const endPoint = getRequestUrl(`${slug}/actions/unpublish`);
343
334
 
344
335
  dispatch(setStatus('unpublish-pending'));
345
336
 
346
337
  try {
347
338
  trackUsageRef.current('willUnpublishEntry');
348
339
 
349
- const { data } = await post(endPoint);
340
+ const { data } = await post(
341
+ endPoint,
342
+ {},
343
+ {
344
+ params: query,
345
+ }
346
+ );
350
347
 
351
348
  trackUsageRef.current('didUnpublishEntry');
352
349
  toggleNotification({
@@ -361,7 +358,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
361
358
  dispatch(setStatus('resolved'));
362
359
  displayErrors(err);
363
360
  }
364
- }, [post, cleanReceivedData, toggleNotification, displayErrors, slug, dispatch, searchToSend]);
361
+ }, [post, cleanReceivedData, toggleNotification, displayErrors, slug, dispatch, query]);
365
362
 
366
363
  return children({
367
364
  componentsDataStructure,
@@ -20,10 +20,6 @@ const FIELD_SIZES = [
20
20
  [12, '100%'],
21
21
  ];
22
22
 
23
- const TIME_FIELD_OPTIONS = [1, 5, 10, 15, 30, 60];
24
-
25
- const TIME_FIELD_TYPES = ['datetime', 'time'];
26
-
27
23
  const ModalForm = ({ onMetaChange, onSizeChange }) => {
28
24
  const { formatMessage } = useIntl();
29
25
  const { modifiedData, selectedField, attributes, fieldForm } = useLayoutDnd();
@@ -131,33 +127,10 @@ const ModalForm = ({ onMetaChange, onSizeChange }) => {
131
127
  </GridItem>
132
128
  );
133
129
 
134
- const hasTimePicker = TIME_FIELD_TYPES.includes(attributes[selectedField].type);
135
-
136
- const timeStepField = (
137
- <GridItem col={6} key="step">
138
- <Select
139
- value={get(fieldForm, ['metadata', 'step'], 1)}
140
- name="step"
141
- onChange={(value) => onMetaChange({ target: { name: 'step', value } })}
142
- label={formatMessage({
143
- id: getTrad('containers.SettingPage.editSettings.step.label'),
144
- defaultMessage: 'Time interval (minutes)',
145
- })}
146
- >
147
- {TIME_FIELD_OPTIONS.map((value) => (
148
- <Option key={value} value={value}>
149
- {value}
150
- </Option>
151
- ))}
152
- </Select>
153
- </GridItem>
154
- );
155
-
156
130
  return (
157
131
  <>
158
132
  {metaFields}
159
133
  {isResizable && sizeField}
160
- {hasTimePicker && timeStepField}
161
134
  </>
162
135
  );
163
136
  };
@@ -1,22 +1,40 @@
1
1
  import React from 'react';
2
2
 
3
- import { BaseCheckbox, IconButton, Tbody, Td, Tr, Flex } from '@strapi/design-system';
4
- import { useTracking, useFetchClient, useAPIErrorHandler } from '@strapi/helper-plugin';
3
+ import {
4
+ BaseCheckbox,
5
+ IconButton,
6
+ Tbody,
7
+ Td,
8
+ Tr,
9
+ Flex,
10
+ Status,
11
+ Typography,
12
+ } from '@strapi/design-system';
13
+ import {
14
+ useTracking,
15
+ useFetchClient,
16
+ useAPIErrorHandler,
17
+ useQueryParams,
18
+ } from '@strapi/helper-plugin';
5
19
  import { Trash, Duplicate, Pencil } from '@strapi/icons';
6
20
  import { AxiosError } from 'axios';
7
21
  import PropTypes from 'prop-types';
8
22
  import { useIntl } from 'react-intl';
9
23
  import { Link, useHistory } from 'react-router-dom';
10
24
 
25
+ import { useEnterprise } from '../../../../../hooks/useEnterprise';
11
26
  import { getFullName } from '../../../../../utils';
12
27
  import { usePluginsQueryParams } from '../../../../hooks';
13
28
  import { getTrad } from '../../../../utils';
14
29
  import CellContent from '../CellContent';
15
30
 
31
+ const REVIEW_WORKFLOW_COLUMNS_CE = () => null;
32
+
16
33
  export const TableRows = ({
17
34
  canCreate,
18
35
  canDelete,
19
36
  contentType,
37
+ features: { hasDraftAndPublish, hasReviewWorkflows },
20
38
  headers,
21
39
  entriesToDelete,
22
40
  onClickDelete,
@@ -32,7 +50,20 @@ export const TableRows = ({
32
50
 
33
51
  const { trackUsage } = useTracking();
34
52
  const pluginsQueryParams = usePluginsQueryParams();
53
+ const [{ query }] = useQueryParams();
35
54
  const { formatAPIError } = useAPIErrorHandler(getTrad);
55
+ const ReviewWorkflowsStage = useEnterprise(
56
+ REVIEW_WORKFLOW_COLUMNS_CE,
57
+ async () =>
58
+ (
59
+ await import(
60
+ '../../../../../../../ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn'
61
+ )
62
+ ).ReviewWorkflowsStageEE,
63
+ {
64
+ enabled: hasReviewWorkflows,
65
+ }
66
+ );
36
67
 
37
68
  /**
38
69
  *
@@ -53,7 +84,9 @@ export const TableRows = ({
53
84
  const handleCloneClick = (id) => async () => {
54
85
  try {
55
86
  const { data } = await post(
56
- `/content-manager/collection-types/${contentType.uid}/auto-clone/${id}?${pluginsQueryParams}`
87
+ `/content-manager/collection-types/${contentType.uid}/auto-clone/${id}`,
88
+ {},
89
+ { params: { plugins: query?.plugins } }
57
90
  );
58
91
 
59
92
  if ('id' in data) {
@@ -74,6 +107,11 @@ export const TableRows = ({
74
107
  }
75
108
  };
76
109
 
110
+ // block rendering until the review stage component is fully loaded in EE
111
+ if (!ReviewWorkflowsStage) {
112
+ return null;
113
+ }
114
+
77
115
  /**
78
116
  * Table Cells with actions e.g edit, delete, duplicate have `stopPropagation`
79
117
  * to prevent the row from being selected.
@@ -113,20 +151,57 @@ export const TableRows = ({
113
151
  />
114
152
  </Td>
115
153
  )}
154
+
116
155
  {headers.map(({ key, cellFormatter, name, ...rest }) => {
156
+ if (hasDraftAndPublish && name === 'publishedAt') {
157
+ return (
158
+ <Td key={key}>
159
+ <Status
160
+ width="min-content"
161
+ showBullet={false}
162
+ variant={data.publishedAt ? 'success' : 'secondary'}
163
+ size="S"
164
+ >
165
+ <Typography
166
+ fontWeight="bold"
167
+ textColor={`${data.publishedAt ? 'success' : 'secondary'}700`}
168
+ >
169
+ {formatMessage({
170
+ id: getTrad(
171
+ `containers.List.${data.publishedAt ? 'published' : 'draft'}`
172
+ ),
173
+ defaultMessage: data.publishedAt ? 'Published' : 'Draft',
174
+ })}
175
+ </Typography>
176
+ </Status>
177
+ </Td>
178
+ );
179
+ }
180
+
181
+ if (hasReviewWorkflows && name === 'strapi_reviewWorkflows_stage') {
182
+ return (
183
+ <Td key={key}>
184
+ {data.strapi_reviewWorkflows_stage ? (
185
+ <ReviewWorkflowsStage
186
+ color={data.strapi_reviewWorkflows_stage.color}
187
+ name={data.strapi_reviewWorkflows_stage.name}
188
+ />
189
+ ) : (
190
+ <Typography textColor="neutral800">-</Typography>
191
+ )}
192
+ </Td>
193
+ );
194
+ }
195
+
117
196
  return (
118
197
  <Td key={key}>
119
- {typeof cellFormatter === 'function' ? (
120
- cellFormatter(data, { key, name, ...rest })
121
- ) : (
122
- <CellContent
123
- content={data[name.split('.')[0]]}
124
- name={name}
125
- contentType={contentType}
126
- {...rest}
127
- rowId={data.id}
128
- />
129
- )}
198
+ <CellContent
199
+ content={data[name.split('.')[0]]}
200
+ name={name}
201
+ contentType={contentType}
202
+ {...rest}
203
+ rowId={data.id}
204
+ />
130
205
  </Td>
131
206
  );
132
207
  })}
@@ -212,6 +287,10 @@ TableRows.propTypes = {
212
287
  uid: PropTypes.string.isRequired,
213
288
  }).isRequired,
214
289
  entriesToDelete: PropTypes.array,
290
+ features: PropTypes.shape({
291
+ hasDraftAndPublish: PropTypes.bool.isRequired,
292
+ hasReviewWorkflows: PropTypes.bool.isRequired,
293
+ }).isRequired,
215
294
  headers: PropTypes.array.isRequired,
216
295
  onClickDelete: PropTypes.func,
217
296
  onSelectRow: PropTypes.func,
@@ -10,8 +10,6 @@ import {
10
10
  HeaderLayout,
11
11
  useNotifyAT,
12
12
  Flex,
13
- Typography,
14
- Status,
15
13
  } from '@strapi/design-system';
16
14
  import {
17
15
  NoPermissions,
@@ -44,6 +42,7 @@ import { bindActionCreators, compose } from 'redux';
44
42
  import styled from 'styled-components';
45
43
 
46
44
  import { INJECT_COLUMN_IN_TABLE } from '../../../exposedHooks';
45
+ import { useEnterprise } from '../../../hooks/useEnterprise';
47
46
  import { selectAdminPermissions } from '../../../pages/App/selectors';
48
47
  import { InjectionZone } from '../../../shared/components';
49
48
  import AttributeFilter from '../../components/AttributeFilter';
@@ -56,7 +55,7 @@ import { ConfirmDialogDeleteAll } from './components/ConfirmDialogDeleteAll';
56
55
  import { FieldPicker } from './components/FieldPicker';
57
56
  import { TableRows } from './components/TableRows';
58
57
  import makeSelectListView, { selectDisplayedHeaders } from './selectors';
59
- import { buildQueryString } from './utils';
58
+ import { buildValidGetParams } from './utils';
60
59
 
61
60
  const ConfigureLayoutBox = styled(Box)`
62
61
  svg {
@@ -66,6 +65,8 @@ const ConfigureLayoutBox = styled(Box)`
66
65
  }
67
66
  `;
68
67
 
68
+ const REVIEW_WORKFLOW_COLUMNS_CE = null;
69
+
69
70
  function ListView({
70
71
  canCreate,
71
72
  canDelete,
@@ -100,14 +101,30 @@ function ListView({
100
101
  useFocusWhenNavigate();
101
102
 
102
103
  const [{ query }] = useQueryParams();
103
- const params = buildQueryString(query);
104
+ const params = React.useMemo(() => buildValidGetParams(query), [query]);
104
105
  const pluginsQueryParams = stringify({ plugins: query.plugins }, { encode: false });
105
106
 
106
107
  const { pathname } = useLocation();
107
108
  const { push } = useHistory();
108
109
  const { formatMessage } = useIntl();
109
- const hasDraftAndPublish = options?.draftAndPublish || false;
110
110
  const fetchClient = useFetchClient();
111
+
112
+ const hasDraftAndPublish = options?.draftAndPublish ?? false;
113
+ const hasReviewWorkflows = options?.reviewWorkflows ?? false;
114
+
115
+ const reviewWorkflowColumns = useEnterprise(
116
+ REVIEW_WORKFLOW_COLUMNS_CE,
117
+ async () =>
118
+ (
119
+ await import(
120
+ '../../../../../ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn/constants'
121
+ )
122
+ ).REVIEW_WORKFLOW_COLUMNS_EE,
123
+ {
124
+ enabled: !!options?.reviewWorkflows,
125
+ }
126
+ );
127
+
111
128
  const { post, del } = fetchClient;
112
129
 
113
130
  const bulkPublishMutation = useMutation(
@@ -120,7 +137,7 @@ function ListView({
120
137
  message: { id: 'content-manager.success.record.publish', defaultMessage: 'Published' },
121
138
  });
122
139
 
123
- fetchData(`/content-manager/collection-types/${slug}${params}`);
140
+ fetchData(`/content-manager/collection-types/${slug}`, { params });
124
141
  },
125
142
  onError(error) {
126
143
  toggleNotification({
@@ -143,7 +160,7 @@ function ListView({
143
160
  },
144
161
  });
145
162
 
146
- fetchData(`/content-manager/collection-types/${slug}${params}`);
163
+ fetchData(`/content-manager/collection-types/${slug}`, { params });
147
164
  },
148
165
  onError(error) {
149
166
  toggleNotification({
@@ -158,19 +175,17 @@ function ListView({
158
175
  // Using a ref to avoid requests being fired multiple times on slug on change
159
176
  // We need it because the hook as mulitple dependencies so it may run before the permissions have checked
160
177
  const requestUrlRef = React.useRef('');
161
-
162
178
  /**
163
179
  * TODO: re-write all of this, it's a mess.
164
180
  */
165
181
  const fetchData = React.useCallback(
166
- async (endPoint, source) => {
182
+ async (endPoint, options) => {
167
183
  getData();
168
184
 
169
185
  try {
170
- const opts = source ? { cancelToken: source.token } : null;
171
186
  const {
172
187
  data: { results, pagination: paginationResult },
173
- } = await fetchClient.get(endPoint, opts);
188
+ } = await fetchClient.get(endPoint, options);
174
189
 
175
190
  notifyStatus(
176
191
  formatMessage(
@@ -217,12 +232,12 @@ function ListView({
217
232
  const handleConfirmDeleteAllData = React.useCallback(
218
233
  async (ids) => {
219
234
  try {
220
- await post(getRequestUrl(`collection-types/${slug}/actions/bulkDelete`), {
235
+ await post(`/content-manager/collection-types/${slug}/actions/bulkDelete`, {
221
236
  ids,
222
237
  });
223
238
 
224
- const requestUrl = getRequestUrl(`collection-types/${slug}${params}`);
225
- fetchData(requestUrl);
239
+ fetchData(`/content-manager/collection-types/${slug}`, { params });
240
+
226
241
  trackUsageRef.current('didBulkDeleteEntries');
227
242
  } catch (err) {
228
243
  toggleNotification({
@@ -231,16 +246,16 @@ function ListView({
231
246
  });
232
247
  }
233
248
  },
234
- [fetchData, params, slug, toggleNotification, formatAPIError, post]
249
+ [slug, toggleNotification, formatAPIError, post, fetchData, params]
235
250
  );
236
251
 
237
252
  const handleConfirmDeleteData = React.useCallback(
238
253
  async (idToDelete) => {
239
254
  try {
240
- await del(getRequestUrl(`collection-types/${slug}/${idToDelete}`));
255
+ await del(`/content-manager/collection-types/${slug}/${idToDelete}`);
241
256
 
242
- const requestUrl = getRequestUrl(`collection-types/${slug}${params}`);
243
- fetchData(requestUrl);
257
+ const requestUrl = getRequestUrl(`collection-types/${slug}`);
258
+ fetchData(requestUrl, { params });
244
259
 
245
260
  toggleNotification({
246
261
  type: 'success',
@@ -253,7 +268,7 @@ function ListView({
253
268
  });
254
269
  }
255
270
  },
256
- [slug, params, fetchData, toggleNotification, formatAPIError, del]
271
+ [slug, toggleNotification, formatAPIError, del, fetchData, params]
257
272
  );
258
273
 
259
274
  /**
@@ -325,10 +340,10 @@ function ListView({
325
340
  const source = CancelToken.source();
326
341
 
327
342
  const shouldSendRequest = canRead;
328
- const requestUrl = getRequestUrl(`collection-types/${slug}${params}`);
343
+ const requestUrl = getRequestUrl(`collection-types/${slug}`);
329
344
 
330
345
  if (shouldSendRequest && requestUrl.includes(requestUrlRef.current)) {
331
- fetchData(requestUrl, source);
346
+ fetchData(requestUrl, { cancelToken: source.token, params });
332
347
  }
333
348
 
334
349
  return () => {
@@ -387,13 +402,8 @@ function ListView({
387
402
  };
388
403
  });
389
404
 
390
- if (!hasDraftAndPublish) {
391
- return formattedHeaders;
392
- }
393
-
394
- return [
395
- ...formattedHeaders,
396
- {
405
+ if (hasDraftAndPublish) {
406
+ formattedHeaders.push({
397
407
  key: '__published_at_temp_key__',
398
408
  name: 'publishedAt',
399
409
  fieldSchema: {
@@ -407,25 +417,29 @@ function ListView({
407
417
  searchable: false,
408
418
  sortable: true,
409
419
  },
410
- // eslint-disable-next-line react/no-unstable-nested-components
411
- cellFormatter(cellData) {
412
- const isPublished = cellData.publishedAt;
413
- const variant = isPublished ? 'success' : 'secondary';
414
-
415
- return (
416
- <Status width="min-content" showBullet={false} variant={variant} size="S">
417
- <Typography fontWeight="bold" textColor={`${variant}700`}>
418
- {formatMessage({
419
- id: getTrad(`containers.List.${isPublished ? 'published' : 'draft'}`),
420
- defaultMessage: isPublished ? 'Published' : 'Draft',
421
- })}
422
- </Typography>
423
- </Status>
424
- );
425
- },
426
- },
427
- ];
428
- }, [runHookWaterfall, displayedHeaders, layout, hasDraftAndPublish, formatMessage]);
420
+ });
421
+ }
422
+
423
+ if (reviewWorkflowColumns) {
424
+ // Make sure the column header label is translated
425
+ if (typeof reviewWorkflowColumns.metadatas.label !== 'string') {
426
+ reviewWorkflowColumns.metadatas.label = formatMessage(
427
+ reviewWorkflowColumns.metadatas.label
428
+ );
429
+ }
430
+
431
+ formattedHeaders.push(reviewWorkflowColumns);
432
+ }
433
+
434
+ return formattedHeaders;
435
+ }, [
436
+ runHookWaterfall,
437
+ displayedHeaders,
438
+ layout,
439
+ reviewWorkflowColumns,
440
+ hasDraftAndPublish,
441
+ formatMessage,
442
+ ]);
429
443
 
430
444
  const subtitle = canRead
431
445
  ? formatMessage(
@@ -568,6 +582,10 @@ function ListView({
568
582
  canCreate={canCreate}
569
583
  canDelete={canDelete}
570
584
  contentType={contentType}
585
+ features={{
586
+ hasDraftAndPublish,
587
+ hasReviewWorkflows,
588
+ }}
571
589
  headers={tableHeaders}
572
590
  rows={data}
573
591
  withBulkActions
@@ -0,0 +1,30 @@
1
+ const createPluginsFilter = (obj = {}) =>
2
+ Object.values(obj).reduce((acc, current) => Object.assign(acc, current), {});
3
+
4
+ /**
5
+ * @description
6
+ * Creates a valid query params object for get requests
7
+ * ie. plugins[18n][locale]=en becomes locale=en
8
+ * @param {object} [query={}] - The query params
9
+ * @returns {object} - The modified query params
10
+ */
11
+ const buildValidGetParams = (query = {}) => {
12
+ // Extract pluginOptions from the query, they shouldn't be part of the URL
13
+ const {
14
+ plugins: _,
15
+ _q: searchQuery,
16
+ ...validQueryParams
17
+ } = {
18
+ ...query,
19
+ ...createPluginsFilter(query.plugins),
20
+ };
21
+
22
+ if (searchQuery) {
23
+ // Encode the search query here since the paramsSerializer will not
24
+ validQueryParams._q = encodeURIComponent(searchQuery);
25
+ }
26
+
27
+ return validQueryParams;
28
+ };
29
+
30
+ export default buildValidGetParams;
@@ -1 +1 @@
1
- export { default as buildQueryString } from './buildQueryString';
1
+ export { default as buildValidGetParams } from './buildValidGetParams';
@@ -1,10 +1,8 @@
1
1
  import { useFetchClient } from '@strapi/helper-plugin';
2
- import { stringify } from 'qs';
3
2
  import { useQuery } from 'react-query';
4
3
 
5
4
  export function useAdminUsers(params = {}, queryOptions = {}) {
6
5
  const { id = '', ...queryParams } = params;
7
- const queryString = stringify(queryParams, { encode: false });
8
6
 
9
7
  const { get } = useFetchClient();
10
8
 
@@ -13,7 +11,9 @@ export function useAdminUsers(params = {}, queryOptions = {}) {
13
11
  async () => {
14
12
  const {
15
13
  data: { data },
16
- } = await get(`/admin/users/${id}${queryString ? `?${queryString}` : ''}`);
14
+ } = await get(`/admin/users/${id}`, {
15
+ params: queryParams,
16
+ });
17
17
 
18
18
  return data;
19
19
  },