@strapi/admin 4.12.0-beta.3 → 4.12.0-beta.4

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 (66) hide show
  1. package/admin/src/content-manager/components/CollectionTypeFormWrapper/index.js +13 -35
  2. package/admin/src/content-manager/pages/ListView/components/TableRows/index.js +304 -0
  3. package/admin/src/pages/AuthPage/components/Register/index.js +4 -0
  4. package/build/2379.0ca87a89.chunk.js +1 -0
  5. package/build/2395.df7a044a.chunk.js +26 -0
  6. package/build/2801.b1140c9b.chunk.js +1 -0
  7. package/build/{3100.21c343fa.chunk.js → 3100.2ba4df95.chunk.js} +1 -1
  8. package/build/{3483.ddd2d6df.chunk.js → 3483.e2ee2547.chunk.js} +1 -1
  9. package/build/3984.dda474f7.chunk.js +1 -0
  10. package/build/502.8ae8ef60.chunk.js +1 -0
  11. package/build/5483.6dd2e776.chunk.js +6 -0
  12. package/build/7065.99ca8ab1.chunk.js +112 -0
  13. package/build/7464.8a6c1e6c.chunk.js +1 -0
  14. package/build/8276.6c7b8e6e.chunk.js +26 -0
  15. package/build/{Admin-authenticatedApp.36b3826c.chunk.js → Admin-authenticatedApp.24998de8.chunk.js} +1 -1
  16. package/build/{admin-app.1c3f7fd6.chunk.js → admin-app.c2e4e128.chunk.js} +8 -8
  17. package/build/content-manager.8772445b.chunk.js +1103 -0
  18. package/build/index.html +1 -1
  19. package/build/{main.a12c4c0f.js → main.ef5fb1a8.js} +45 -45
  20. package/build/review-workflows-settings-create-view.d4b5dbb8.chunk.js +1 -0
  21. package/build/review-workflows-settings-edit-view.77299c63.chunk.js +1 -0
  22. package/build/review-workflows-settings-list-view.3ee9190d.chunk.js +56 -0
  23. package/build/{runtime~main.d197f488.js → runtime~main.c99f4c36.js} +2 -2
  24. package/build/sso-settings-page.3a1ed8c9.chunk.js +1 -0
  25. package/ee/admin/constants.js +3 -0
  26. package/ee/admin/content-manager/pages/EditView/InformationBox/InformationBoxEE.js +12 -4
  27. package/ee/admin/hooks/index.js +1 -1
  28. package/ee/admin/hooks/useLicenseLimitNotification/index.js +1 -1
  29. package/ee/admin/hooks/useLicenseLimits/__mocks__/index.js +8 -0
  30. package/ee/admin/hooks/useLicenseLimits/index.js +1 -3
  31. package/ee/admin/hooks/useLicenseLimits/useLicenseLimits.js +3 -13
  32. package/ee/admin/pages/SettingsPage/pages/ApplicationInfosPage/components/AdminSeatInfo/index.js +19 -4
  33. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/Stage/Stage.js +7 -0
  34. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/Stages.js +20 -16
  35. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/WorkflowAttributes/WorkflowAttributes.js +38 -23
  36. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/constants.js +3 -0
  37. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/CreateView/CreateView.js +47 -27
  38. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/CreateView/index.js +8 -3
  39. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/EditView/EditView.js +68 -41
  40. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/EditView/index.js +8 -3
  41. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/ListView/ListView.js +72 -55
  42. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/ListView/index.js +8 -3
  43. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/reducer/index.js +1 -7
  44. package/ee/admin/pages/SettingsPage/pages/SingleSignOn/utils/schema.js +8 -5
  45. package/ee/server/services/review-workflows/review-workflows.js +1 -1
  46. package/ee/server/services/review-workflows/validation.js +6 -4
  47. package/ee/server/services/review-workflows/workflows/content-types.js +28 -19
  48. package/ee/server/services/review-workflows/workflows/index.js +14 -2
  49. package/ee/server/validation/authentication.js +14 -8
  50. package/package.json +8 -8
  51. package/build/2379.d33a2e16.chunk.js +0 -1
  52. package/build/2395.b0419a54.chunk.js +0 -26
  53. package/build/2801.18f38baf.chunk.js +0 -1
  54. package/build/3984.ea7b8036.chunk.js +0 -1
  55. package/build/502.ccb38223.chunk.js +0 -1
  56. package/build/5483.ed2c7efa.chunk.js +0 -6
  57. package/build/7464.c6d0565c.chunk.js +0 -1
  58. package/build/8276.23e0763b.chunk.js +0 -26
  59. package/build/8298.fd253c9f.chunk.js +0 -117
  60. package/build/content-manager.b8d593d4.chunk.js +0 -1103
  61. package/build/review-workflows-settings-create-view.dfd87e1f.chunk.js +0 -1
  62. package/build/review-workflows-settings-edit-view.53c00afe.chunk.js +0 -1
  63. package/build/review-workflows-settings-list-view.a34be805.chunk.js +0 -56
  64. package/build/sso-settings-page.ed6f3f15.chunk.js +0 -1
  65. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/ProtectedPage/ProtectedPage.js +0 -21
  66. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/ProtectedPage/index.js +0 -1
@@ -1,7 +1,12 @@
1
1
  import * as React from 'react';
2
2
 
3
3
  import { Button, Flex, Loader } from '@strapi/design-system';
4
- import { useAPIErrorHandler, useFetchClient, useNotification } from '@strapi/helper-plugin';
4
+ import {
5
+ useAPIErrorHandler,
6
+ useFetchClient,
7
+ useNotification,
8
+ useRBAC,
9
+ } from '@strapi/helper-plugin';
5
10
  import { Check } from '@strapi/icons';
6
11
  import { useFormik, Form, FormikProvider } from 'formik';
7
12
  import set from 'lodash/set';
@@ -12,13 +17,18 @@ import { useHistory } from 'react-router-dom';
12
17
 
13
18
  import { useContentTypes } from '../../../../../../../../admin/src/hooks/useContentTypes';
14
19
  import { useInjectReducer } from '../../../../../../../../admin/src/hooks/useInjectReducer';
20
+ import { selectAdminPermissions } from '../../../../../../../../admin/src/pages/App/selectors';
15
21
  import { useLicenseLimits } from '../../../../../../hooks';
16
- import { resetWorkflow } from '../../actions';
22
+ import { addStage, resetWorkflow } from '../../actions';
17
23
  import * as Layout from '../../components/Layout';
18
24
  import * as LimitsModal from '../../components/LimitsModal';
19
25
  import { Stages } from '../../components/Stages';
20
26
  import { WorkflowAttributes } from '../../components/WorkflowAttributes';
21
- import { REDUX_NAMESPACE } from '../../constants';
27
+ import {
28
+ CHARGEBEE_STAGES_PER_WORKFLOW_ENTITLEMENT_NAME,
29
+ CHARGEBEE_WORKFLOW_ENTITLEMENT_NAME,
30
+ REDUX_NAMESPACE,
31
+ } from '../../constants';
22
32
  import { useReviewWorkflows } from '../../hooks/useReviewWorkflows';
23
33
  import { reducer, initialState } from '../../reducer';
24
34
  import { validateWorkflow } from '../../utils/validateWorkflow';
@@ -29,6 +39,7 @@ export function ReviewWorkflowsCreateView() {
29
39
  const { push } = useHistory();
30
40
  const { formatAPIError } = useAPIErrorHandler();
31
41
  const dispatch = useDispatch();
42
+ const permissions = useSelector(selectAdminPermissions);
32
43
  const toggleNotification = useNotification();
33
44
  const { collectionTypes, singleTypes, isLoading: isLoadingModels } = useContentTypes();
34
45
  const {
@@ -36,6 +47,9 @@ export function ReviewWorkflowsCreateView() {
36
47
  currentWorkflow: { data: currentWorkflow, isDirty: currentWorkflowIsDirty },
37
48
  },
38
49
  } = useSelector((state) => state?.[REDUX_NAMESPACE] ?? initialState);
50
+ const {
51
+ allowedActions: { canCreate },
52
+ } = useRBAC(permissions.settings['review-workflows']);
39
53
  const [showLimitModal, setShowLimitModal] = React.useState(false);
40
54
  const { isLoading: isLicenseLoading, getFeature } = useLicenseLimits();
41
55
  const { meta, isLoading: isWorkflowLoading } = useReviewWorkflows();
@@ -72,20 +86,12 @@ export function ReviewWorkflowsCreateView() {
72
86
 
73
87
  return workflow;
74
88
  } catch (error) {
75
- // TODO: the current implementation of `formatAPIError` prints all error messages of all details,
76
- // which get's hairy when we have duplicated-name errors, because the same error message is printed
77
- // several times. What we want instead in these scenarios is to print only the error summary and
78
- // display the individual error messages for each field. This is a workaround, until we change the
79
- // implementation of `formatAPIError`.
89
+ // TODO: this would benefit from a utility to get a formik error
90
+ // representation from an API error
80
91
  if (
81
92
  error.response.data?.error?.name === 'ValidationError' &&
82
93
  error.response.data?.error?.details?.errors?.length > 0
83
94
  ) {
84
- toggleNotification({
85
- type: 'warning',
86
- message: error.response.data.error.message,
87
- });
88
-
89
95
  setInitialErrors(
90
96
  error.response.data?.error?.details?.errors.reduce((acc, error) => {
91
97
  set(acc, error.path, error.message);
@@ -93,13 +99,13 @@ export function ReviewWorkflowsCreateView() {
93
99
  return acc;
94
100
  }, {})
95
101
  );
96
- } else {
97
- toggleNotification({
98
- type: 'warning',
99
- message: formatAPIError(error),
100
- });
101
102
  }
102
103
 
104
+ toggleNotification({
105
+ type: 'warning',
106
+ message: formatAPIError(error),
107
+ });
108
+
103
109
  return null;
104
110
  }
105
111
  };
@@ -117,7 +123,10 @@ export function ReviewWorkflowsCreateView() {
117
123
  * update, because it would throw an API error.
118
124
  */
119
125
 
120
- if (limits?.workflows && meta?.workflowCount >= parseInt(limits.workflows, 10)) {
126
+ if (
127
+ limits?.[CHARGEBEE_WORKFLOW_ENTITLEMENT_NAME] &&
128
+ meta?.workflowCount >= parseInt(limits[CHARGEBEE_WORKFLOW_ENTITLEMENT_NAME], 10)
129
+ ) {
121
130
  setShowLimitModal('workflow');
122
131
 
123
132
  /**
@@ -126,8 +135,9 @@ export function ReviewWorkflowsCreateView() {
126
135
  * update, because it would throw an API error.
127
136
  */
128
137
  } else if (
129
- limits?.stagesPerWorkflow &&
130
- currentWorkflow.stages.length >= parseInt(limits.stagesPerWorkflow, 10)
138
+ limits?.[CHARGEBEE_STAGES_PER_WORKFLOW_ENTITLEMENT_NAME] &&
139
+ currentWorkflow.stages.length >=
140
+ parseInt(limits[CHARGEBEE_STAGES_PER_WORKFLOW_ENTITLEMENT_NAME], 10)
131
141
  ) {
132
142
  setShowLimitModal('stage');
133
143
  } else {
@@ -143,6 +153,13 @@ export function ReviewWorkflowsCreateView() {
143
153
 
144
154
  React.useEffect(() => {
145
155
  dispatch(resetWorkflow());
156
+
157
+ // Create an empty default stage
158
+ dispatch(
159
+ addStage({
160
+ name: '',
161
+ })
162
+ );
146
163
  }, [dispatch]);
147
164
 
148
165
  /**
@@ -160,11 +177,15 @@ export function ReviewWorkflowsCreateView() {
160
177
 
161
178
  React.useEffect(() => {
162
179
  if (!isWorkflowLoading && !isLicenseLoading) {
163
- if (limits.workflows && meta?.workflowsTotal >= parseInt(limits.workflows, 10)) {
180
+ if (
181
+ limits?.[CHARGEBEE_WORKFLOW_ENTITLEMENT_NAME] &&
182
+ meta?.workflowsTotal >= parseInt(limits[CHARGEBEE_WORKFLOW_ENTITLEMENT_NAME], 10)
183
+ ) {
164
184
  setShowLimitModal('workflow');
165
185
  } else if (
166
- limits.stagesPerWorkflow &&
167
- currentWorkflow.stages.length >= parseInt(limits.stagesPerWorkflow, 10)
186
+ limits?.[CHARGEBEE_STAGES_PER_WORKFLOW_ENTITLEMENT_NAME] &&
187
+ currentWorkflow.stages.length >=
188
+ parseInt(limits[CHARGEBEE_STAGES_PER_WORKFLOW_ENTITLEMENT_NAME], 10)
168
189
  ) {
169
190
  setShowLimitModal('stage');
170
191
  }
@@ -172,8 +193,7 @@ export function ReviewWorkflowsCreateView() {
172
193
  }, [
173
194
  isLicenseLoading,
174
195
  isWorkflowLoading,
175
- limits.stagesPerWorkflow,
176
- limits?.workflows,
196
+ limits,
177
197
  meta?.workflowsTotal,
178
198
  currentWorkflow.stages.length,
179
199
  ]);
@@ -191,7 +211,7 @@ export function ReviewWorkflowsCreateView() {
191
211
  startIcon={<Check />}
192
212
  type="submit"
193
213
  size="M"
194
- disabled={!currentWorkflowIsDirty}
214
+ disabled={!currentWorkflowIsDirty || !canCreate}
195
215
  isLoading={isLoading}
196
216
  >
197
217
  {formatMessage({
@@ -1,13 +1,18 @@
1
1
  import React from 'react';
2
2
 
3
- import { ProtectedPage } from '../../components/ProtectedPage';
3
+ import { CheckPagePermissions } from '@strapi/helper-plugin';
4
+ import { useSelector } from 'react-redux';
5
+
6
+ import { selectAdminPermissions } from '../../../../../../../../admin/src/pages/App/selectors';
4
7
 
5
8
  import { ReviewWorkflowsCreateView } from './CreateView';
6
9
 
7
10
  export default function () {
11
+ const permissions = useSelector(selectAdminPermissions);
12
+
8
13
  return (
9
- <ProtectedPage>
14
+ <CheckPagePermissions permissions={permissions.settings['review-workflows'].create}>
10
15
  <ReviewWorkflowsCreateView />
11
- </ProtectedPage>
16
+ </CheckPagePermissions>
12
17
  );
13
18
  }
@@ -6,6 +6,7 @@ import {
6
6
  useAPIErrorHandler,
7
7
  useFetchClient,
8
8
  useNotification,
9
+ useRBAC,
9
10
  } from '@strapi/helper-plugin';
10
11
  import { Check } from '@strapi/icons';
11
12
  import { useFormik, Form, FormikProvider } from 'formik';
@@ -17,19 +18,25 @@ import { useParams } from 'react-router-dom';
17
18
 
18
19
  import { useContentTypes } from '../../../../../../../../admin/src/hooks/useContentTypes';
19
20
  import { useInjectReducer } from '../../../../../../../../admin/src/hooks/useInjectReducer';
21
+ import { selectAdminPermissions } from '../../../../../../../../admin/src/pages/App/selectors';
20
22
  import { useLicenseLimits } from '../../../../../../hooks';
21
- import { setWorkflow } from '../../actions';
23
+ import { resetWorkflow, setWorkflow } from '../../actions';
22
24
  import * as Layout from '../../components/Layout';
23
25
  import * as LimitsModal from '../../components/LimitsModal';
24
26
  import { Stages } from '../../components/Stages';
25
27
  import { WorkflowAttributes } from '../../components/WorkflowAttributes';
26
- import { REDUX_NAMESPACE } from '../../constants';
28
+ import {
29
+ CHARGEBEE_WORKFLOW_ENTITLEMENT_NAME,
30
+ CHARGEBEE_STAGES_PER_WORKFLOW_ENTITLEMENT_NAME,
31
+ REDUX_NAMESPACE,
32
+ } from '../../constants';
27
33
  import { useReviewWorkflows } from '../../hooks/useReviewWorkflows';
28
34
  import { reducer, initialState } from '../../reducer';
29
35
  import { validateWorkflow } from '../../utils/validateWorkflow';
30
36
 
31
37
  export function ReviewWorkflowsEditView() {
32
38
  const { workflowId } = useParams();
39
+ const permissions = useSelector(selectAdminPermissions);
33
40
  const { formatMessage } = useIntl();
34
41
  const dispatch = useDispatch();
35
42
  const { put } = useFetchClient();
@@ -53,6 +60,9 @@ export function ReviewWorkflowsEditView() {
53
60
  },
54
61
  },
55
62
  } = useSelector((state) => state?.[REDUX_NAMESPACE] ?? initialState);
63
+ const {
64
+ allowedActions: { canDelete, canUpdate },
65
+ } = useRBAC(permissions.settings['review-workflows']);
56
66
  const [isConfirmDeleteDialogOpen, setIsConfirmDeleteDialogOpen] = React.useState(false);
57
67
  const { getFeature, isLoading: isLicenseLoading } = useLicenseLimits();
58
68
  const [showLimitModal, setShowLimitModal] = React.useState(false);
@@ -87,20 +97,12 @@ export function ReviewWorkflowsEditView() {
87
97
 
88
98
  return res;
89
99
  } catch (error) {
90
- // TODO: the current implementation of `formatAPIError` prints all error messages of all details,
91
- // which get's hairy when we have duplicated-name errors, because the same error message is printed
92
- // several times. What we want instead in these scenarios is to print only the error summary and
93
- // display the individual error messages for each field. This is a workaround, until we change the
94
- // implementation of `formatAPIError`.
100
+ // TODO: this would benefit from a utility to get a formik error
101
+ // representation from an API error
95
102
  if (
96
103
  error.response.data?.error?.name === 'ValidationError' &&
97
104
  error.response.data?.error?.details?.errors?.length > 0
98
105
  ) {
99
- toggleNotification({
100
- type: 'warning',
101
- message: error.response.data.error.message,
102
- });
103
-
104
106
  setInitialErrors(
105
107
  error.response.data?.error?.details?.errors.reduce((acc, error) => {
106
108
  set(acc, error.path, error.message);
@@ -108,13 +110,13 @@ export function ReviewWorkflowsEditView() {
108
110
  return acc;
109
111
  }, {})
110
112
  );
111
- } else {
112
- toggleNotification({
113
- type: 'warning',
114
- message: formatAPIError(error),
115
- });
116
113
  }
117
114
 
115
+ toggleNotification({
116
+ type: 'warning',
117
+ message: formatAPIError(error),
118
+ });
119
+
118
120
  return null;
119
121
  }
120
122
  };
@@ -141,7 +143,10 @@ export function ReviewWorkflowsEditView() {
141
143
  async onSubmit() {
142
144
  if (currentWorkflowHasDeletedServerStages) {
143
145
  setIsConfirmDeleteDialogOpen(true);
144
- } else if (limits?.workflows && meta?.workflowCount > parseInt(limits.workflows, 10)) {
146
+ } else if (
147
+ limits?.[CHARGEBEE_WORKFLOW_ENTITLEMENT_NAME] &&
148
+ meta?.workflowCount > parseInt(limits[CHARGEBEE_WORKFLOW_ENTITLEMENT_NAME], 10)
149
+ ) {
145
150
  /**
146
151
  * If the current license has a limit, check if the total count of workflows
147
152
  * exceeds that limit and display the limits modal instead of sending the
@@ -155,8 +160,9 @@ export function ReviewWorkflowsEditView() {
155
160
  * update, because it would throw an API error.
156
161
  */
157
162
  } else if (
158
- limits?.stagesPerWorkflow &&
159
- currentWorkflow.stages.length > parseInt(limits.stagesPerWorkflow, 10)
163
+ limits?.[CHARGEBEE_STAGES_PER_WORKFLOW_ENTITLEMENT_NAME] &&
164
+ currentWorkflow.stages.length >
165
+ parseInt(limits[CHARGEBEE_STAGES_PER_WORKFLOW_ENTITLEMENT_NAME], 10)
160
166
  ) {
161
167
  setShowLimitModal('stage');
162
168
  } else {
@@ -174,6 +180,12 @@ export function ReviewWorkflowsEditView() {
174
180
 
175
181
  React.useEffect(() => {
176
182
  dispatch(setWorkflow({ status: workflowStatus, data: workflow }));
183
+
184
+ // reset the state to the initial state to avoid flashes if a user
185
+ // navigates from an edit-view to a create-view
186
+ return () => {
187
+ dispatch(resetWorkflow());
188
+ };
177
189
  }, [workflowStatus, workflow, dispatch]);
178
190
 
179
191
  /**
@@ -191,11 +203,15 @@ export function ReviewWorkflowsEditView() {
191
203
 
192
204
  React.useEffect(() => {
193
205
  if (!isWorkflowLoading && !isLicenseLoading) {
194
- if (limits?.workflows && meta?.workflowCount > parseInt(limits.workflows, 10)) {
206
+ if (
207
+ limits?.[CHARGEBEE_WORKFLOW_ENTITLEMENT_NAME] &&
208
+ meta?.workflowCount > parseInt(limits[CHARGEBEE_WORKFLOW_ENTITLEMENT_NAME], 10)
209
+ ) {
195
210
  setShowLimitModal('workflow');
196
211
  } else if (
197
- limits?.stagesPerWorkflow &&
198
- currentWorkflow.stages.length > parseInt(limits.stagesPerWorkflow, 10)
212
+ limits?.[CHARGEBEE_STAGES_PER_WORKFLOW_ENTITLEMENT_NAME] &&
213
+ currentWorkflow.stages.length >
214
+ parseInt(limits[CHARGEBEE_STAGES_PER_WORKFLOW_ENTITLEMENT_NAME], 10)
199
215
  ) {
200
216
  setShowLimitModal('stage');
201
217
  }
@@ -204,8 +220,7 @@ export function ReviewWorkflowsEditView() {
204
220
  currentWorkflow.stages.length,
205
221
  isLicenseLoading,
206
222
  isWorkflowLoading,
207
- limits.stagesPerWorkflow,
208
- limits.workflows,
223
+ limits,
209
224
  meta?.workflowCount,
210
225
  meta.workflowsTotal,
211
226
  ]);
@@ -225,7 +240,7 @@ export function ReviewWorkflowsEditView() {
225
240
  startIcon={<Check />}
226
241
  type="submit"
227
242
  size="M"
228
- disabled={!currentWorkflowIsDirty}
243
+ disabled={!currentWorkflowIsDirty || !canUpdate}
229
244
  // if the confirm dialog is open the loading state is on
230
245
  // the confirm button already
231
246
  loading={!isConfirmDeleteDialogOpen && isLoading}
@@ -236,28 +251,40 @@ export function ReviewWorkflowsEditView() {
236
251
  })}
237
252
  </Button>
238
253
  }
239
- subtitle={formatMessage(
240
- {
241
- id: 'Settings.review-workflows.page.subtitle',
242
- defaultMessage: '{count, plural, one {# stage} other {# stages}}',
243
- },
244
- { count: currentWorkflow?.stages?.length ?? 0 }
245
- )}
254
+ subtitle={
255
+ currentWorkflow.stages.length > 0 &&
256
+ formatMessage(
257
+ {
258
+ id: 'Settings.review-workflows.page.subtitle',
259
+ defaultMessage: '{count, plural, one {# stage} other {# stages}}',
260
+ },
261
+ { count: currentWorkflow.stages.length }
262
+ )
263
+ }
246
264
  title={currentWorkflow.name}
247
265
  />
248
266
 
249
267
  <Layout.Root>
250
268
  {isLoadingModels || status === 'loading' ? (
251
- <Loader>
252
- {formatMessage({
253
- id: 'Settings.review-workflows.page.isLoading',
254
- defaultMessage: 'Workflow is loading',
255
- })}
256
- </Loader>
269
+ <Flex justifyContent="center">
270
+ <Loader>
271
+ {formatMessage({
272
+ id: 'Settings.review-workflows.page.isLoading',
273
+ defaultMessage: 'Workflow is loading',
274
+ })}
275
+ </Loader>
276
+ </Flex>
257
277
  ) : (
258
278
  <Flex alignItems="stretch" direction="column" gap={7}>
259
- <WorkflowAttributes contentTypes={{ collectionTypes, singleTypes }} />
260
- <Stages stages={formik.values?.stages} />
279
+ <WorkflowAttributes
280
+ canUpdate={canUpdate}
281
+ contentTypes={{ collectionTypes, singleTypes }}
282
+ />
283
+ <Stages
284
+ canDelete={canDelete}
285
+ canUpdate={canUpdate}
286
+ stages={formik.values?.stages}
287
+ />
261
288
  </Flex>
262
289
  )}
263
290
  </Layout.Root>
@@ -1,13 +1,18 @@
1
1
  import React from 'react';
2
2
 
3
- import { ProtectedPage } from '../../components/ProtectedPage';
3
+ import { CheckPagePermissions } from '@strapi/helper-plugin';
4
+ import { useSelector } from 'react-redux';
5
+
6
+ import { selectAdminPermissions } from '../../../../../../../../admin/src/pages/App/selectors';
4
7
 
5
8
  import { ReviewWorkflowsEditView } from './EditView';
6
9
 
7
10
  export default function () {
11
+ const permissions = useSelector(selectAdminPermissions);
12
+
8
13
  return (
9
- <ProtectedPage>
14
+ <CheckPagePermissions permissions={permissions.settings['review-workflows'].main}>
10
15
  <ReviewWorkflowsEditView />
11
- </ProtectedPage>
16
+ </CheckPagePermissions>
12
17
  );
13
18
  }
@@ -23,18 +23,22 @@ import {
23
23
  useAPIErrorHandler,
24
24
  useFetchClient,
25
25
  useNotification,
26
+ useRBAC,
26
27
  useTracking,
27
28
  } from '@strapi/helper-plugin';
28
29
  import { Pencil, Plus, Trash } from '@strapi/icons';
29
30
  import { useIntl } from 'react-intl';
30
31
  import { useMutation } from 'react-query';
32
+ import { useSelector } from 'react-redux';
31
33
  import { useHistory } from 'react-router-dom';
32
34
  import styled from 'styled-components';
33
35
 
34
36
  import { useContentTypes } from '../../../../../../../../admin/src/hooks/useContentTypes';
37
+ import { selectAdminPermissions } from '../../../../../../../../admin/src/pages/App/selectors';
35
38
  import { useLicenseLimits } from '../../../../../../hooks';
36
39
  import * as Layout from '../../components/Layout';
37
40
  import * as LimitsModal from '../../components/LimitsModal';
41
+ import { CHARGEBEE_WORKFLOW_ENTITLEMENT_NAME } from '../../constants';
38
42
  import { useReviewWorkflows } from '../../hooks/useReviewWorkflows';
39
43
 
40
44
  const ActionLink = styled(Link)`
@@ -76,6 +80,10 @@ export function ReviewWorkflowsListView() {
76
80
  const toggleNotification = useNotification();
77
81
  const { getFeature, isLoading: isLicenseLoading } = useLicenseLimits();
78
82
  const { trackUsage } = useTracking();
83
+ const permissions = useSelector(selectAdminPermissions);
84
+ const {
85
+ allowedActions: { canCreate, canDelete },
86
+ } = useRBAC(permissions.settings['review-workflows']);
79
87
 
80
88
  const limits = getFeature('review-workflows');
81
89
 
@@ -148,24 +156,21 @@ export function ReviewWorkflowsListView() {
148
156
 
149
157
  React.useEffect(() => {
150
158
  if (!isLoading && !isLicenseLoading) {
151
- if (limits?.workflows && meta?.workflowCount >= parseInt(limits.workflows, 10)) {
159
+ if (
160
+ limits?.[CHARGEBEE_WORKFLOW_ENTITLEMENT_NAME] &&
161
+ meta?.workflowCount > parseInt(limits[CHARGEBEE_WORKFLOW_ENTITLEMENT_NAME], 10)
162
+ ) {
152
163
  setShowLimitModal(true);
153
164
  }
154
165
  }
155
- }, [
156
- isLicenseLoading,
157
- isLoading,
158
- limits.stagesPerWorkflow,
159
- limits.workflows,
160
- meta?.workflowCount,
161
- meta.workflowsTotal,
162
- ]);
166
+ }, [isLicenseLoading, isLoading, limits, meta?.workflowCount, meta.workflowsTotal]);
163
167
 
164
168
  return (
165
169
  <>
166
170
  <Layout.Header
167
171
  primaryAction={
168
172
  <LinkButton
173
+ disabled={!canCreate}
169
174
  startIcon={<Plus />}
170
175
  size="S"
171
176
  to="/settings/review-workflows/create"
@@ -180,7 +185,10 @@ export function ReviewWorkflowsListView() {
180
185
  * current hard-limit of 200 they will see an error thrown by the API.
181
186
  */
182
187
 
183
- if (limits?.workflows && meta?.workflowCount >= parseInt(limits.workflows, 10)) {
188
+ if (
189
+ limits?.[CHARGEBEE_WORKFLOW_ENTITLEMENT_NAME] &&
190
+ meta?.workflowCount >= parseInt(limits[CHARGEBEE_WORKFLOW_ENTITLEMENT_NAME], 10)
191
+ ) {
184
192
  event.preventDefault();
185
193
  setShowLimitModal(true);
186
194
  } else {
@@ -207,42 +215,50 @@ export function ReviewWorkflowsListView() {
207
215
 
208
216
  <Layout.Root>
209
217
  {isLoading || isLoadingModels ? (
210
- <Loader>
211
- {formatMessage({
212
- id: 'Settings.review-workflows.page.list.isLoading',
213
- defaultMessage: 'Workflows are loading',
214
- })}
215
- </Loader>
218
+ <Flex justifyContent="center">
219
+ <Loader>
220
+ {formatMessage({
221
+ id: 'Settings.review-workflows.page.list.isLoading',
222
+ defaultMessage: 'Workflows are loading',
223
+ })}
224
+ </Loader>
225
+ </Flex>
216
226
  ) : (
217
227
  <Table
218
228
  colCount={3}
219
229
  footer={
220
230
  // TODO: we should be able to use a link here instead of an (inaccessible onClick) handler
221
- <TFooter
222
- icon={<Plus />}
223
- onClick={() => {
224
- /**
225
- * If the current license has a workflow limit:
226
- * check if the total count of workflows exceeds that limit
227
- *
228
- * If the current license does not have a limit (e.g. offline license):
229
- * allow the user to navigate to the create-view. In case they exceed the
230
- * current hard-limit of 200 they will see an error thrown by the API.
231
- */
231
+ canCreate && (
232
+ <TFooter
233
+ icon={<Plus />}
234
+ onClick={() => {
235
+ /**
236
+ * If the current license has a workflow limit:
237
+ * check if the total count of workflows exceeds that limit
238
+ *
239
+ * If the current license does not have a limit (e.g. offline license):
240
+ * allow the user to navigate to the create-view. In case they exceed the
241
+ * current hard-limit of 200 they will see an error thrown by the API.
242
+ */
232
243
 
233
- if (limits?.workflows && meta?.workflowCount >= parseInt(limits.workflows, 10)) {
234
- setShowLimitModal(true);
235
- } else {
236
- push('/settings/review-workflows/create');
237
- trackUsage('willCreateWorkflow');
238
- }
239
- }}
240
- >
241
- {formatMessage({
242
- id: 'Settings.review-workflows.list.page.create',
243
- defaultMessage: 'Create new workflow',
244
- })}
245
- </TFooter>
244
+ if (
245
+ limits?.[CHARGEBEE_WORKFLOW_ENTITLEMENT_NAME] &&
246
+ meta?.workflowCount >=
247
+ parseInt(limits[CHARGEBEE_WORKFLOW_ENTITLEMENT_NAME], 10)
248
+ ) {
249
+ setShowLimitModal(true);
250
+ } else {
251
+ push('/settings/review-workflows/create');
252
+ trackUsage('willCreateWorkflow');
253
+ }
254
+ }}
255
+ >
256
+ {formatMessage({
257
+ id: 'Settings.review-workflows.list.page.create',
258
+ defaultMessage: 'Create new workflow',
259
+ })}
260
+ </TFooter>
261
+ )
246
262
  }
247
263
  rowCount={1}
248
264
  >
@@ -326,21 +342,22 @@ export function ReviewWorkflowsListView() {
326
342
  <Pencil />
327
343
  </ActionLink>
328
344
 
329
- <IconButton
330
- aria-label={formatMessage(
331
- {
332
- id: 'Settings.review-workflows.list.page.list.column.actions.delete.label',
333
- defaultMessage: 'Delete {name}',
334
- },
335
- { name: 'Default workflow' }
336
- )}
337
- disabled={workflows.length === 1}
338
- icon={<Trash />}
339
- noBorder
340
- onClick={() => {
341
- handleDeleteWorkflow(workflow.id);
342
- }}
343
- />
345
+ {workflows.length > 1 && canDelete && (
346
+ <IconButton
347
+ aria-label={formatMessage(
348
+ {
349
+ id: 'Settings.review-workflows.list.page.list.column.actions.delete.label',
350
+ defaultMessage: 'Delete {name}',
351
+ },
352
+ { name: 'Default workflow' }
353
+ )}
354
+ icon={<Trash />}
355
+ noBorder
356
+ onClick={() => {
357
+ handleDeleteWorkflow(workflow.id);
358
+ }}
359
+ />
360
+ )}
344
361
  </Flex>
345
362
  </Td>
346
363
  </Tr>
@@ -1,13 +1,18 @@
1
1
  import React from 'react';
2
2
 
3
- import { ProtectedPage } from '../../components/ProtectedPage';
3
+ import { CheckPagePermissions } from '@strapi/helper-plugin';
4
+ import { useSelector } from 'react-redux';
5
+
6
+ import { selectAdminPermissions } from '../../../../../../../../admin/src/pages/App/selectors';
4
7
 
5
8
  import { ReviewWorkflowsListView } from './ListView';
6
9
 
7
10
  export default function () {
11
+ const permissions = useSelector(selectAdminPermissions);
12
+
8
13
  return (
9
- <ProtectedPage>
14
+ <CheckPagePermissions permissions={permissions.settings['review-workflows'].main}>
10
15
  <ReviewWorkflowsListView />
11
- </ProtectedPage>
16
+ </CheckPagePermissions>
12
17
  );
13
18
  }
@@ -22,13 +22,7 @@ export const initialState = {
22
22
  data: {
23
23
  name: '',
24
24
  contentTypes: [],
25
- stages: [
26
- {
27
- color: STAGE_COLOR_DEFAULT,
28
- name: '',
29
- __temp_key__: 1,
30
- },
31
- ],
25
+ stages: [],
32
26
  },
33
27
  isDirty: false,
34
28
  hasDeletedServerStages: false,