@strapi/admin 4.11.4 → 4.12.0-beta.1

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 (213) hide show
  1. package/admin/src/constants.js +83 -83
  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/SingleTypeFormWrapper/index.js +34 -37
  5. package/admin/src/content-manager/pages/EditSettingsView/components/ModalForm.js +0 -27
  6. package/admin/src/content-manager/pages/EditView/Information/index.js +1 -1
  7. package/admin/src/content-manager/pages/EditView/InformationBox/InformationBoxCE.js +1 -2
  8. package/admin/src/content-manager/pages/EditView/InformationBox/index.js +1 -3
  9. package/admin/src/content-manager/pages/EditView/index.js +14 -2
  10. package/admin/src/content-manager/pages/ListView/components/TableRows/index.js +93 -14
  11. package/admin/src/content-manager/pages/ListView/index.js +65 -59
  12. package/admin/src/content-manager/pages/ListView/utils/buildValidGetParams.js +30 -0
  13. package/admin/src/content-manager/pages/ListView/utils/index.js +1 -1
  14. package/admin/src/content-manager/utils/mergeMetasWithSchema.js +5 -1
  15. package/admin/src/hooks/index.js +0 -1
  16. package/admin/src/hooks/useAdminUsers/useAdminUsers.js +3 -3
  17. package/admin/src/hooks/useEnterprise/useEnterprise.js +4 -4
  18. package/admin/src/hooks/useSettingsMenu/index.js +35 -21
  19. package/admin/src/pages/App/index.js +28 -23
  20. package/admin/src/pages/AuthPage/components/Login/index.js +3 -5
  21. package/admin/src/pages/AuthPage/components/Register/index.js +5 -1
  22. package/admin/src/pages/AuthPage/constants.js +3 -2
  23. package/admin/src/pages/AuthPage/index.js +18 -1
  24. package/admin/src/pages/HomePage/index.js +19 -7
  25. package/admin/src/pages/ProfilePage/index.js +12 -12
  26. package/admin/src/pages/SettingsPage/components/SettingsNav/index.js +13 -11
  27. package/admin/src/pages/SettingsPage/components/Tokens/TokenBox/index.js +1 -1
  28. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/index.js +17 -1
  29. package/admin/src/pages/SettingsPage/pages/Users/EditPage/index.js +16 -3
  30. package/admin/src/pages/SettingsPage/pages/Users/ListPage/CreateAction/index.js +2 -4
  31. package/admin/src/pages/SettingsPage/pages/Users/ListPage/ModalForm/index.js +15 -1
  32. package/admin/src/pages/SettingsPage/pages/Users/ListPage/index.js +36 -5
  33. package/admin/src/pages/SettingsPage/pages/Users/components/MagicLink/index.js +3 -5
  34. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/EventTable/index.js +1 -3
  35. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/WebhookForm/index.js +16 -1
  36. package/admin/src/translations/zh-Hans.json +1 -1
  37. package/build/0cd5f8915b265d5b1856.png +0 -0
  38. package/build/1049.758a01f5.chunk.js +1 -0
  39. package/build/{3528.4845cf92.chunk.js → 1386.762d6eb8.chunk.js} +1 -1
  40. package/build/1727.b49f0713.chunk.js +1 -0
  41. package/build/190.66d89241.chunk.js +117 -0
  42. package/build/{5563.86f9aa9c.chunk.js → 2225.15d1df72.chunk.js} +3 -3
  43. package/build/2379.d33a2e16.chunk.js +1 -0
  44. package/build/2395.b0419a54.chunk.js +26 -0
  45. package/build/2801.18ac397d.chunk.js +1 -0
  46. package/build/{7394.423886bd.chunk.js → 3100.21c343fa.chunk.js} +1 -1
  47. package/build/311.cb0884bb.chunk.js +1 -0
  48. package/build/3483.e182b190.chunk.js +1 -0
  49. package/build/3984.ea7b8036.chunk.js +1 -0
  50. package/build/4546.ff9fdf30.chunk.js +1 -0
  51. package/build/502.ccb38223.chunk.js +1 -0
  52. package/build/5483.ed2c7efa.chunk.js +6 -0
  53. package/build/{5542.c62d0daf.chunk.js → 5542.2415a393.chunk.js} +6 -6
  54. package/build/6158.f9d82db9.chunk.js +1 -0
  55. package/build/7030.b98dcedf.chunk.js +1 -0
  56. package/build/7464.c6d0565c.chunk.js +1 -0
  57. package/build/8276.23e0763b.chunk.js +26 -0
  58. package/build/918.54414509.chunk.js +1 -0
  59. package/build/{9932.7e2b71de.chunk.js → 9932.b5a3bb3a.chunk.js} +81 -81
  60. package/build/9944.7af075a5.chunk.js +26 -0
  61. package/build/{Admin-authenticatedApp.cb649fc1.chunk.js → Admin-authenticatedApp.2ffa318a.chunk.js} +5 -5
  62. package/build/Admin_InternalErrorPage.f45f2462.chunk.js +1 -0
  63. package/build/{Admin_homePage.be30ef4e.chunk.js → Admin_homePage.ac9dfb86.chunk.js} +23 -15
  64. package/build/{Admin_marketplace.74a58e20.chunk.js → Admin_marketplace.f0b87fce.chunk.js} +1 -1
  65. package/build/{Admin_pluginsPage.ce464189.chunk.js → Admin_pluginsPage.8728ff6e.chunk.js} +1 -1
  66. package/build/{Admin_profilePage.2131eb68.chunk.js → Admin_profilePage.a968035f.chunk.js} +2 -2
  67. package/build/Admin_settingsPage.3ad19487.chunk.js +79 -0
  68. package/build/Upload_ConfigureTheView.345ac1e0.chunk.js +1 -0
  69. package/build/admin-app.088bcd33.chunk.js +36 -0
  70. package/build/{admin-edit-roles-page.3fdd6b9d.chunk.js → admin-edit-roles-page.a49b9f4f.chunk.js} +4 -4
  71. package/build/admin-edit-users.67704088.chunk.js +10 -0
  72. package/build/{admin-roles-list.e17b00d7.chunk.js → admin-roles-list.0c129e98.chunk.js} +1 -1
  73. package/build/admin-users.3279ffb0.chunk.js +11 -0
  74. package/build/api-tokens-create-page.46c2ea84.chunk.js +1 -0
  75. package/build/{api-tokens-edit-page.9a1dd2fa.chunk.js → api-tokens-edit-page.58139df9.chunk.js} +1 -1
  76. package/build/{api-tokens-list-page.a103f526.chunk.js → api-tokens-list-page.505bf7e0.chunk.js} +2 -2
  77. package/build/audit-logs-settings-page.4b422831.chunk.js +1 -0
  78. package/build/content-manager.9b569036.chunk.js +1094 -0
  79. package/build/{content-type-builder-list-view.a200a358.chunk.js → content-type-builder-list-view.bf9be456.chunk.js} +9 -9
  80. package/build/content-type-builder-translation-en-json.38e20391.chunk.js +1 -0
  81. package/build/{content-type-builder-translation-zh-json.ad24dbeb.chunk.js → content-type-builder-translation-zh-json.958d90e1.chunk.js} +1 -1
  82. package/build/content-type-builder.3963fb2d.chunk.js +166 -0
  83. package/build/{email-settings-page.45695daa.chunk.js → email-settings-page.d494d1eb.chunk.js} +2 -2
  84. package/build/{i18n-settings-page.29308d0b.chunk.js → i18n-settings-page.47f78016.chunk.js} +1 -1
  85. package/build/index.html +1 -1
  86. package/build/main.98c989b0.js +2908 -0
  87. package/build/review-workflows-settings-create-view.60bc516c.chunk.js +1 -0
  88. package/build/review-workflows-settings-edit-view.898ea409.chunk.js +1 -0
  89. package/build/review-workflows-settings-list-view.240cacdf.chunk.js +56 -0
  90. package/build/runtime~main.44bf2a37.js +2 -0
  91. package/build/sso-settings-page.ed6f3f15.chunk.js +1 -0
  92. package/build/transfer-tokens-create-page.1597e6ab.chunk.js +1 -0
  93. package/build/transfer-tokens-edit-page.8741529f.chunk.js +1 -0
  94. package/build/{transfer-tokens-list-page.7237443d.chunk.js → transfer-tokens-list-page.22147d2c.chunk.js} +2 -2
  95. package/build/upload-settings.cac210a0.chunk.js +14 -0
  96. package/build/upload.8d01c525.chunk.js +26 -0
  97. package/build/{users-advanced-settings-page.750b1f76.chunk.js → users-advanced-settings-page.18379a56.chunk.js} +1 -1
  98. package/build/users-email-settings-page.a87978e5.chunk.js +9 -0
  99. package/build/users-providers-settings-page.8876c1ee.chunk.js +14 -0
  100. package/build/{users-roles-settings-page.1f505119.chunk.js → users-roles-settings-page.0431f48c.chunk.js} +2 -2
  101. package/build/webhook-edit-page.a91f27a1.chunk.js +33 -0
  102. package/build/{webhook-list-page.940a40f1.chunk.js → webhook-list-page.65e1b5bb.chunk.js} +1 -1
  103. package/build/{zh-Hans-json.4cfef87d.chunk.js → zh-Hans-json.fada6f40.chunk.js} +1 -1
  104. package/ee/admin/constants.js +14 -14
  105. package/ee/admin/content-manager/pages/EditView/InformationBox/InformationBoxEE.js +88 -31
  106. package/ee/admin/content-manager/pages/EditView/InformationBox/index.js +1 -3
  107. package/ee/admin/content-manager/{components/DynamicTable/CellContent/ReviewWorkflowsStage → pages/ListView/ReviewWorkflowsColumn}/ReviewWorkflowsStageEE.js +7 -2
  108. package/ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn/constants.js +24 -0
  109. package/ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn/index.js +1 -0
  110. package/ee/admin/hooks/useLicenseLimitNotification/index.js +17 -6
  111. package/ee/admin/hooks/useLicenseLimits/index.js +1 -32
  112. package/ee/admin/hooks/useLicenseLimits/useLicenseLimits.js +44 -0
  113. package/ee/admin/pages/AuthPage/components/Login/index.js +3 -5
  114. package/ee/admin/pages/HomePage/index.js +11 -0
  115. package/ee/admin/pages/SettingsPage/constants.js +25 -1
  116. package/ee/admin/pages/SettingsPage/pages/ApplicationInfosPage/components/AdminSeatInfo/index.js +7 -7
  117. package/ee/admin/pages/SettingsPage/pages/AuditLogs/ListView/hooks/useAuditLogsData.js +6 -4
  118. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/actions/index.js +19 -4
  119. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Layout/Layout.js +65 -0
  120. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Layout/index.js +1 -0
  121. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/LimitsModal/LimitsModal.js +111 -0
  122. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/LimitsModal/assets/balloon.png +0 -0
  123. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/LimitsModal/index.js +3 -0
  124. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/ProtectedPage/ProtectedPage.js +21 -0
  125. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/ProtectedPage/index.js +1 -0
  126. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/Stage/Stage.js +5 -5
  127. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/WorkflowAttributes/WorkflowAttributes.js +110 -0
  128. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/WorkflowAttributes/index.js +1 -0
  129. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/constants.js +3 -1
  130. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/hooks/useReviewWorkflows.js +13 -19
  131. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/CreateView/CreateView.js +246 -0
  132. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/CreateView/index.js +13 -0
  133. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/EditView/EditView.js +287 -0
  134. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/EditView/index.js +13 -0
  135. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/ListView/ListView.js +382 -0
  136. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/ListView/index.js +13 -0
  137. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/reducer/index.js +53 -23
  138. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/utils/getWorkflowValidationSchema.js +43 -28
  139. package/ee/admin/pages/SettingsPage/pages/Users/ListPage/CreateAction/index.js +11 -6
  140. package/ee/admin/pages/SettingsPage/pages/Users/ListPage/index.js +13 -0
  141. package/ee/admin/pages/SettingsPage/pages/Users/components/MagicLink/index.js +3 -5
  142. package/ee/admin/pages/SettingsPage/pages/Webhooks/EditView/components/EventTable/index.js +1 -3
  143. package/ee/server/config/admin-actions.js +24 -0
  144. package/ee/server/constants/default-stages.json +8 -4
  145. package/ee/server/constants/default-workflow.json +3 -1
  146. package/ee/server/constants/workflows.js +10 -1
  147. package/ee/server/content-types/workflow/index.js +10 -0
  148. package/ee/server/content-types/workflow-stage/index.js +3 -1
  149. package/ee/server/controllers/admin.js +1 -0
  150. package/ee/server/controllers/workflows/index.js +135 -8
  151. package/ee/server/controllers/workflows/stages/index.js +38 -38
  152. package/ee/server/migrations/review-workflows-content-types.js +29 -0
  153. package/ee/server/migrations/review-workflows-deleted-ct-in-workflows.js +39 -0
  154. package/ee/server/migrations/review-workflows-stage-attribute.js +49 -0
  155. package/ee/server/migrations/review-workflows-stages-color.js +2 -2
  156. package/ee/server/migrations/review-workflows-workflow-name.js +21 -0
  157. package/ee/server/register.js +12 -2
  158. package/ee/server/routes/review-workflows.js +44 -10
  159. package/ee/server/services/index.js +1 -0
  160. package/ee/server/services/review-workflows/entity-service-decorator.js +8 -13
  161. package/ee/server/services/review-workflows/review-workflows.js +45 -53
  162. package/ee/server/services/review-workflows/stages.js +84 -46
  163. package/ee/server/services/review-workflows/validation.js +60 -0
  164. package/ee/server/services/review-workflows/workflows/content-types.js +80 -0
  165. package/ee/server/services/review-workflows/workflows/index.js +207 -0
  166. package/ee/server/utils/review-workflows.js +30 -25
  167. package/ee/server/validation/review-workflows.js +49 -10
  168. package/index.js +0 -14
  169. package/package.json +12 -21
  170. package/webpack.alias.js +0 -3
  171. package/webpack.config.js +1 -75
  172. package/admin/src/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/getTableColumn.js +0 -2
  173. package/admin/src/content-manager/pages/ListView/utils/buildQueryString.js +0 -36
  174. package/admin/src/content-manager/pages/ListView/utils/createPluginsFilter.js +0 -4
  175. package/admin/src/hooks/useLicenseLimits/index.js +0 -3
  176. package/admin/src/pages/App/utils/index.js +0 -3
  177. package/admin/src/pages/App/utils/unique-identifier.js +0 -12
  178. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/components/AdminSeatInfo/index.js +0 -5
  179. package/build/1386.3b2aa6a7.chunk.js +0 -3
  180. package/build/1799.44d2e264.chunk.js +0 -33
  181. package/build/1970.39a2d75e.chunk.js +0 -1
  182. package/build/3269.1ea0f5a6.chunk.js +0 -1
  183. package/build/5932.6a23b88c.chunk.js +0 -1
  184. package/build/7018.98feed67.chunk.js +0 -1
  185. package/build/7259.fb69d4bf.chunk.js +0 -1
  186. package/build/Admin_InternalErrorPage.8911cb49.chunk.js +0 -1
  187. package/build/Admin_settingsPage.4069bb8a.chunk.js +0 -79
  188. package/build/Upload_ConfigureTheView.7a1cb9c9.chunk.js +0 -1
  189. package/build/admin-app.fea867af.chunk.js +0 -61
  190. package/build/admin-edit-users.200551e3.chunk.js +0 -10
  191. package/build/admin-users.3b12dca2.chunk.js +0 -11
  192. package/build/api-tokens-create-page.3dd4e921.chunk.js +0 -1
  193. package/build/audit-logs-settings-page.f538490f.chunk.js +0 -1
  194. package/build/content-manager.c40f5ff9.chunk.js +0 -1088
  195. package/build/content-type-builder-translation-en-json.f592325b.chunk.js +0 -1
  196. package/build/content-type-builder.bd1bbff1.chunk.js +0 -166
  197. package/build/main.ee36abd9.js +0 -2927
  198. package/build/review-workflows-settings.93808ae0.chunk.js +0 -110
  199. package/build/runtime~main.efd966f6.js +0 -2
  200. package/build/sso-settings-page.0cdb96a6.chunk.js +0 -1
  201. package/build/transfer-tokens-create-page.de14cad4.chunk.js +0 -1
  202. package/build/transfer-tokens-edit-page.4f5e39af.chunk.js +0 -1
  203. package/build/upload-settings.cb6c14c3.chunk.js +0 -14
  204. package/build/upload.7e629643.chunk.js +0 -26
  205. package/build/users-email-settings-page.e9bcd865.chunk.js +0 -9
  206. package/build/users-providers-settings-page.a94253e9.chunk.js +0 -14
  207. package/build/webhook-edit-page.77ef4f1a.chunk.js +0 -33
  208. package/ee/admin/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/getTableColumn.js +0 -58
  209. package/ee/admin/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/index.js +0 -3
  210. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/ProtectedPage.js +0 -20
  211. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/ReviewWorkflows.js +0 -204
  212. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/index.js +0 -3
  213. package/ee/server/services/review-workflows/workflows.js +0 -25
@@ -0,0 +1,287 @@
1
+ import * as React from 'react';
2
+
3
+ import { Button, Flex, Loader } from '@strapi/design-system';
4
+ import {
5
+ ConfirmDialog,
6
+ useAPIErrorHandler,
7
+ useFetchClient,
8
+ useNotification,
9
+ } from '@strapi/helper-plugin';
10
+ import { Check } from '@strapi/icons';
11
+ import { useFormik, Form, FormikProvider } from 'formik';
12
+ import { useIntl } from 'react-intl';
13
+ import { useMutation } from 'react-query';
14
+ import { useSelector, useDispatch } from 'react-redux';
15
+ import { useParams } from 'react-router-dom';
16
+
17
+ import { useContentTypes } from '../../../../../../../../admin/src/hooks/useContentTypes';
18
+ import { useInjectReducer } from '../../../../../../../../admin/src/hooks/useInjectReducer';
19
+ import { useLicenseLimits } from '../../../../../../hooks';
20
+ import { setWorkflow } from '../../actions';
21
+ import * as Layout from '../../components/Layout';
22
+ import * as LimitsModal from '../../components/LimitsModal';
23
+ import { Stages } from '../../components/Stages';
24
+ import { WorkflowAttributes } from '../../components/WorkflowAttributes';
25
+ import { REDUX_NAMESPACE } from '../../constants';
26
+ import { useReviewWorkflows } from '../../hooks/useReviewWorkflows';
27
+ import { reducer, initialState } from '../../reducer';
28
+ import { getWorkflowValidationSchema } from '../../utils/getWorkflowValidationSchema';
29
+
30
+ export function ReviewWorkflowsEditView() {
31
+ const { workflowId } = useParams();
32
+ const { formatMessage } = useIntl();
33
+ const dispatch = useDispatch();
34
+ const { put } = useFetchClient();
35
+ const { formatAPIError } = useAPIErrorHandler();
36
+ const toggleNotification = useNotification();
37
+ const {
38
+ isLoading: isWorkflowLoading,
39
+ meta,
40
+ workflows: [workflow],
41
+ status: workflowStatus,
42
+ refetch,
43
+ } = useReviewWorkflows({ id: workflowId });
44
+ const { collectionTypes, singleTypes, isLoading: isLoadingModels } = useContentTypes();
45
+ const {
46
+ status,
47
+ clientState: {
48
+ currentWorkflow: {
49
+ data: currentWorkflow,
50
+ isDirty: currentWorkflowIsDirty,
51
+ hasDeletedServerStages: currentWorkflowHasDeletedServerStages,
52
+ },
53
+ },
54
+ } = useSelector((state) => state?.[REDUX_NAMESPACE] ?? initialState);
55
+ const [isConfirmDeleteDialogOpen, setIsConfirmDeleteDialogOpen] = React.useState(false);
56
+ const { getFeature, isLoading: isLicenseLoading } = useLicenseLimits();
57
+ const [showLimitModal, setShowLimitModal] = React.useState(false);
58
+
59
+ const { mutateAsync, isLoading } = useMutation(
60
+ async ({ workflow }) => {
61
+ const {
62
+ data: { data },
63
+ } = await put(`/admin/review-workflows/workflows/${workflow.id}`, {
64
+ data: workflow,
65
+ });
66
+
67
+ return data;
68
+ },
69
+ {
70
+ onSuccess() {
71
+ toggleNotification({
72
+ type: 'success',
73
+ message: { id: 'notification.success.saved', defaultMessage: 'Saved' },
74
+ });
75
+ },
76
+ }
77
+ );
78
+
79
+ const updateWorkflow = async (workflow) => {
80
+ try {
81
+ const res = await mutateAsync({ workflow });
82
+
83
+ return res;
84
+ } catch (error) {
85
+ toggleNotification({
86
+ type: 'warning',
87
+ message: formatAPIError(error),
88
+ });
89
+
90
+ return null;
91
+ }
92
+ };
93
+
94
+ const submitForm = async () => {
95
+ await updateWorkflow(currentWorkflow);
96
+ await refetch();
97
+
98
+ setIsConfirmDeleteDialogOpen(false);
99
+ };
100
+
101
+ const handleConfirmDeleteDialog = async () => {
102
+ await submitForm();
103
+ };
104
+
105
+ const toggleConfirmDeleteDialog = () => {
106
+ setIsConfirmDeleteDialogOpen((prev) => !prev);
107
+ };
108
+
109
+ const formik = useFormik({
110
+ enableReinitialize: true,
111
+ initialValues: currentWorkflow,
112
+ async onSubmit() {
113
+ if (currentWorkflowHasDeletedServerStages) {
114
+ setIsConfirmDeleteDialogOpen(true);
115
+ } else if (limits?.workflows && meta?.workflowCount > parseInt(limits.workflows, 10)) {
116
+ /**
117
+ * If the current license has a limit, check if the total count of workflows
118
+ * exceeds that limit and display the limits modal instead of sending the
119
+ * update, because it would throw an API error.
120
+ */
121
+ setShowLimitModal('workflow');
122
+
123
+ /**
124
+ * If the current license has a limit, check if the total count of stages
125
+ * exceeds that limit and display the limits modal instead of sending the
126
+ * update, because it would throw an API error.
127
+ */
128
+ } else if (
129
+ limits?.stagesPerWorkflow &&
130
+ currentWorkflow.stages.length > parseInt(limits.stagesPerWorkflow, 10)
131
+ ) {
132
+ setShowLimitModal('stage');
133
+ } else {
134
+ submitForm();
135
+ }
136
+ },
137
+ validationSchema: getWorkflowValidationSchema({ formatMessage }),
138
+ });
139
+
140
+ useInjectReducer(REDUX_NAMESPACE, reducer);
141
+
142
+ const limits = getFeature('review-workflows');
143
+
144
+ React.useEffect(() => {
145
+ dispatch(setWorkflow({ status: workflowStatus, data: workflow }));
146
+ }, [workflowStatus, workflow, dispatch]);
147
+
148
+ /**
149
+ * If the current license has a limit:
150
+ * check if the total count of workflows or stages exceeds that limit and display
151
+ * the limits modal on page load. It can be closed by the user, but the
152
+ * API will throw an error in case they try to create a new workflow or update the
153
+ * stages.
154
+ *
155
+ * If the current license does not have a limit (e.g. offline license):
156
+ * do nothing (for now). In case they are trying to create the 201st workflow/ stage
157
+ * the API will throw an error.
158
+ *
159
+ */
160
+
161
+ React.useEffect(() => {
162
+ if (!isWorkflowLoading && !isLicenseLoading) {
163
+ if (limits?.workflows && meta?.workflowCount > parseInt(limits.workflows, 10)) {
164
+ setShowLimitModal('workflow');
165
+ } else if (
166
+ limits?.stagesPerWorkflow &&
167
+ currentWorkflow.stages.length > parseInt(limits.stagesPerWorkflow, 10)
168
+ ) {
169
+ setShowLimitModal('stage');
170
+ }
171
+ }
172
+ }, [
173
+ currentWorkflow.stages.length,
174
+ isLicenseLoading,
175
+ isWorkflowLoading,
176
+ limits.stagesPerWorkflow,
177
+ limits.workflows,
178
+ meta?.workflowCount,
179
+ meta.workflowsTotal,
180
+ ]);
181
+
182
+ // TODO: redirect back to list-view if workflow is not found?
183
+
184
+ return (
185
+ <>
186
+ <Layout.DragLayerRendered />
187
+
188
+ <FormikProvider value={formik}>
189
+ <Form onSubmit={formik.handleSubmit}>
190
+ <Layout.Header
191
+ navigationAction={<Layout.Back href="/settings/review-workflows" />}
192
+ primaryAction={
193
+ <Button
194
+ startIcon={<Check />}
195
+ type="submit"
196
+ size="M"
197
+ disabled={!currentWorkflowIsDirty}
198
+ // if the confirm dialog is open the loading state is on
199
+ // the confirm button already
200
+ loading={!isConfirmDeleteDialogOpen && isLoading}
201
+ >
202
+ {formatMessage({
203
+ id: 'global.save',
204
+ defaultMessage: 'Save',
205
+ })}
206
+ </Button>
207
+ }
208
+ subtitle={formatMessage(
209
+ {
210
+ id: 'Settings.review-workflows.page.subtitle',
211
+ defaultMessage: '{count, plural, one {# stage} other {# stages}}',
212
+ },
213
+ { count: currentWorkflow?.stages?.length ?? 0 }
214
+ )}
215
+ title={currentWorkflow.name}
216
+ />
217
+
218
+ <Layout.Root>
219
+ {isLoadingModels || status === 'loading' ? (
220
+ <Loader>
221
+ {formatMessage({
222
+ id: 'Settings.review-workflows.page.isLoading',
223
+ defaultMessage: 'Workflow is loading',
224
+ })}
225
+ </Loader>
226
+ ) : (
227
+ <Flex alignItems="stretch" direction="column" gap={7}>
228
+ <WorkflowAttributes contentTypes={{ collectionTypes, singleTypes }} />
229
+ <Stages stages={formik.values?.stages} />
230
+ </Flex>
231
+ )}
232
+ </Layout.Root>
233
+ </Form>
234
+ </FormikProvider>
235
+
236
+ <ConfirmDialog
237
+ bodyText={{
238
+ id: 'Settings.review-workflows.page.delete.confirm.body',
239
+ defaultMessage:
240
+ 'All entries assigned to deleted stages will be moved to the previous stage. Are you sure you want to save?',
241
+ }}
242
+ isConfirmButtonLoading={isLoading}
243
+ isOpen={isConfirmDeleteDialogOpen}
244
+ onToggleDialog={toggleConfirmDeleteDialog}
245
+ onConfirm={handleConfirmDeleteDialog}
246
+ />
247
+
248
+ <LimitsModal.Root
249
+ isOpen={showLimitModal === 'workflow'}
250
+ onClose={() => setShowLimitModal(false)}
251
+ >
252
+ <LimitsModal.Title>
253
+ {formatMessage({
254
+ id: 'Settings.review-workflows.edit.page.workflows.limit.title',
255
+ defaultMessage: 'You’ve reached the limit of workflows in your plan',
256
+ })}
257
+ </LimitsModal.Title>
258
+
259
+ <LimitsModal.Body>
260
+ {formatMessage({
261
+ id: 'Settings.review-workflows.edit.page.workflows.limit.body',
262
+ defaultMessage: 'Delete a workflow or contact Sales to enable more workflows.',
263
+ })}
264
+ </LimitsModal.Body>
265
+ </LimitsModal.Root>
266
+
267
+ <LimitsModal.Root
268
+ isOpen={showLimitModal === 'stage'}
269
+ onClose={() => setShowLimitModal(false)}
270
+ >
271
+ <LimitsModal.Title>
272
+ {formatMessage({
273
+ id: 'Settings.review-workflows.edit.page.stages.limit.title',
274
+ defaultMessage: 'You have reached the limit of stages for this workflow in your plan',
275
+ })}
276
+ </LimitsModal.Title>
277
+
278
+ <LimitsModal.Body>
279
+ {formatMessage({
280
+ id: 'Settings.review-workflows.edit.page.stages.limit.body',
281
+ defaultMessage: 'Try deleting some stages or contact Sales to enable more stages.',
282
+ })}
283
+ </LimitsModal.Body>
284
+ </LimitsModal.Root>
285
+ </>
286
+ );
287
+ }
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+
3
+ import { ProtectedPage } from '../../components/ProtectedPage';
4
+
5
+ import { ReviewWorkflowsEditView } from './EditView';
6
+
7
+ export default function () {
8
+ return (
9
+ <ProtectedPage>
10
+ <ReviewWorkflowsEditView />
11
+ </ProtectedPage>
12
+ );
13
+ }
@@ -0,0 +1,382 @@
1
+ import React from 'react';
2
+
3
+ import {
4
+ Flex,
5
+ IconButton,
6
+ Loader,
7
+ Table,
8
+ Thead,
9
+ Tbody,
10
+ Tr,
11
+ Td,
12
+ TFooter,
13
+ Th,
14
+ Typography,
15
+ VisuallyHidden,
16
+ } from '@strapi/design-system';
17
+ import {
18
+ ConfirmDialog,
19
+ Link,
20
+ LinkButton,
21
+ onRowClick,
22
+ pxToRem,
23
+ useAPIErrorHandler,
24
+ useFetchClient,
25
+ useNotification,
26
+ useTracking,
27
+ } from '@strapi/helper-plugin';
28
+ import { Pencil, Plus, Trash } from '@strapi/icons';
29
+ import { useIntl } from 'react-intl';
30
+ import { useMutation } from 'react-query';
31
+ import { useHistory } from 'react-router-dom';
32
+ import styled from 'styled-components';
33
+
34
+ import { useContentTypes } from '../../../../../../../../admin/src/hooks/useContentTypes';
35
+ import { useLicenseLimits } from '../../../../../../hooks';
36
+ import * as Layout from '../../components/Layout';
37
+ import * as LimitsModal from '../../components/LimitsModal';
38
+ import { useReviewWorkflows } from '../../hooks/useReviewWorkflows';
39
+
40
+ const ActionLink = styled(Link)`
41
+ align-items: center;
42
+ height: ${pxToRem(32)};
43
+ display: flex;
44
+ justify-content: center;
45
+ padding: ${({ theme }) => `${theme.spaces[2]}}`};
46
+ width: ${pxToRem(32)};
47
+
48
+ svg {
49
+ height: ${pxToRem(12)};
50
+ width: ${pxToRem(12)};
51
+
52
+ path {
53
+ fill: ${({ theme }) => theme.colors.neutral500};
54
+ }
55
+ }
56
+
57
+ &:hover,
58
+ &:focus {
59
+ svg {
60
+ path {
61
+ fill: ${({ theme }) => theme.colors.neutral800};
62
+ }
63
+ }
64
+ }
65
+ `;
66
+
67
+ export function ReviewWorkflowsListView() {
68
+ const { formatMessage } = useIntl();
69
+ const { push } = useHistory();
70
+ const { collectionTypes, singleTypes, isLoading: isLoadingModels } = useContentTypes();
71
+ const { meta, workflows, isLoading, refetch } = useReviewWorkflows();
72
+ const [workflowToDelete, setWorkflowToDelete] = React.useState(null);
73
+ const [showLimitModal, setShowLimitModal] = React.useState(false);
74
+ const { del } = useFetchClient();
75
+ const { formatAPIError } = useAPIErrorHandler();
76
+ const toggleNotification = useNotification();
77
+ const { getFeature, isLoading: isLicenseLoading } = useLicenseLimits();
78
+ const { trackUsage } = useTracking();
79
+
80
+ const limits = getFeature('review-workflows');
81
+
82
+ const { mutateAsync, isLoading: isLoadingMutation } = useMutation(
83
+ async ({ workflowId, stages }) => {
84
+ const {
85
+ data: { data },
86
+ } = await del(`/admin/review-workflows/workflows/${workflowId}`, {
87
+ data: stages,
88
+ });
89
+
90
+ return data;
91
+ },
92
+ {
93
+ onSuccess() {
94
+ toggleNotification({
95
+ type: 'success',
96
+ message: { id: 'notification.success.deleted', defaultMessage: 'Deleted' },
97
+ });
98
+ },
99
+ }
100
+ );
101
+
102
+ const getContentTypeDisplayName = (uid) => {
103
+ const contentType = [...collectionTypes, ...singleTypes].find(
104
+ (contentType) => contentType.uid === uid
105
+ );
106
+
107
+ return contentType.info.displayName;
108
+ };
109
+
110
+ const handleDeleteWorkflow = (workflowId) => {
111
+ setWorkflowToDelete(workflowId);
112
+ };
113
+
114
+ const toggleConfirmDeleteDialog = () => {
115
+ setWorkflowToDelete(null);
116
+ };
117
+
118
+ const handleConfirmDeleteDialog = async () => {
119
+ try {
120
+ const res = await mutateAsync({ workflowId: workflowToDelete });
121
+
122
+ await refetch();
123
+ setWorkflowToDelete(null);
124
+
125
+ return res;
126
+ } catch (error) {
127
+ toggleNotification({
128
+ type: 'warning',
129
+ message: formatAPIError(error),
130
+ });
131
+
132
+ return null;
133
+ }
134
+ };
135
+
136
+ /**
137
+ * If the current license has a limit:
138
+ * check if the total count of workflows or stages exceeds that limit and display
139
+ * the limits modal on page load. It can be closed by the user, but the
140
+ * API will throw an error in case they try to create a new workflow or update the
141
+ * stages.
142
+ *
143
+ * If the current license does not have a limit (e.g. offline license):
144
+ * do nothing (for now). In case they are trying to create the 201st workflow/ stage
145
+ * the API will throw an error.
146
+ *
147
+ */
148
+
149
+ React.useEffect(() => {
150
+ if (!isLoading && !isLicenseLoading) {
151
+ if (limits?.workflows && meta?.workflowCount >= parseInt(limits.workflows, 10)) {
152
+ setShowLimitModal(true);
153
+ }
154
+ }
155
+ }, [
156
+ isLicenseLoading,
157
+ isLoading,
158
+ limits.stagesPerWorkflow,
159
+ limits.workflows,
160
+ meta?.workflowCount,
161
+ meta.workflowsTotal,
162
+ ]);
163
+
164
+ return (
165
+ <>
166
+ <Layout.Header
167
+ primaryAction={
168
+ <LinkButton
169
+ startIcon={<Plus />}
170
+ size="S"
171
+ to="/settings/review-workflows/create"
172
+ onClick={(event) => {
173
+ /**
174
+ * If the current license has a workflow limit:
175
+ * check if the total count of workflows exceeds that limit. If so,
176
+ * prevent the navigation and show the limits overlay.
177
+ *
178
+ * If the current license does not have a limit (e.g. offline license):
179
+ * allow the user to navigate to the create-view. In case they exceed the
180
+ * current hard-limit of 200 they will see an error thrown by the API.
181
+ */
182
+
183
+ if (limits?.workflows && meta?.workflowCount >= parseInt(limits.workflows, 10)) {
184
+ event.preventDefault();
185
+ setShowLimitModal(true);
186
+ } else {
187
+ trackUsage('willCreateWorkflow');
188
+ }
189
+ }}
190
+ >
191
+ {formatMessage({
192
+ id: 'Settings.review-workflows.list.page.create',
193
+ defaultMessage: 'Create new workflow',
194
+ })}
195
+ </LinkButton>
196
+ }
197
+ subtitle={formatMessage({
198
+ id: 'Settings.review-workflows.list.page.subtitle',
199
+ defaultMessage:
200
+ 'Manage content review stages and collaborate during content creation from draft to publication',
201
+ })}
202
+ title={formatMessage({
203
+ id: 'Settings.review-workflows.list.page.title',
204
+ defaultMessage: 'Review Workflows',
205
+ })}
206
+ />
207
+
208
+ <Layout.Root>
209
+ {isLoading || isLoadingModels ? (
210
+ <Loader>
211
+ {formatMessage({
212
+ id: 'Settings.review-workflows.page.list.isLoading',
213
+ defaultMessage: 'Workflows are loading',
214
+ })}
215
+ </Loader>
216
+ ) : (
217
+ <Table
218
+ colCount={3}
219
+ footer={
220
+ // 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
+ */
232
+
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>
246
+ }
247
+ rowCount={1}
248
+ >
249
+ <Thead>
250
+ <Tr>
251
+ <Th>
252
+ <Typography variant="sigma">
253
+ {formatMessage({
254
+ id: 'Settings.review-workflows.list.page.list.column.name.title',
255
+ defaultMessage: 'Name',
256
+ })}
257
+ </Typography>
258
+ </Th>
259
+ <Th>
260
+ <Typography variant="sigma">
261
+ {formatMessage({
262
+ id: 'Settings.review-workflows.list.page.list.column.stages.title',
263
+ defaultMessage: 'Stages',
264
+ })}
265
+ </Typography>
266
+ </Th>
267
+ <Th>
268
+ <Typography variant="sigma">
269
+ {formatMessage({
270
+ id: 'Settings.review-workflows.list.page.list.column.contentTypes.title',
271
+ defaultMessage: 'Content Types',
272
+ })}
273
+ </Typography>
274
+ </Th>
275
+ <Th>
276
+ <VisuallyHidden>
277
+ {formatMessage({
278
+ id: 'Settings.review-workflows.list.page.list.column.actions.title',
279
+ defaultMessage: 'Actions',
280
+ })}
281
+ </VisuallyHidden>
282
+ </Th>
283
+ </Tr>
284
+ </Thead>
285
+
286
+ <Tbody>
287
+ {workflows.map((workflow) => (
288
+ <Tr
289
+ {...onRowClick({
290
+ fn(event) {
291
+ // Abort row onClick event when the user click on the delete button
292
+ if (event.target.nodeName === 'BUTTON') {
293
+ return;
294
+ }
295
+
296
+ push(`/settings/review-workflows/${workflow.id}`);
297
+ },
298
+ })}
299
+ key={`workflow-${workflow.id}`}
300
+ >
301
+ <Td width={pxToRem(250)}>
302
+ <Typography textColor="neutral800" fontWeight="bold" ellipsis>
303
+ {workflow.name}
304
+ </Typography>
305
+ </Td>
306
+ <Td>
307
+ <Typography textColor="neutral800">{workflow.stages.length}</Typography>
308
+ </Td>
309
+ <Td>
310
+ <Typography textColor="neutral800">
311
+ {(workflow?.contentTypes ?? []).map(getContentTypeDisplayName).join(', ')}
312
+ </Typography>
313
+ </Td>
314
+ <Td>
315
+ <Flex alignItems="center" justifyContent="end">
316
+ <ActionLink
317
+ to={`/settings/review-workflows/${workflow.id}`}
318
+ aria-label={formatMessage(
319
+ {
320
+ id: 'Settings.review-workflows.list.page.list.column.actions.edit.label',
321
+ defaultMessage: 'Edit {name}',
322
+ },
323
+ { name: workflow.name }
324
+ )}
325
+ >
326
+ <Pencil />
327
+ </ActionLink>
328
+
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
+ />
344
+ </Flex>
345
+ </Td>
346
+ </Tr>
347
+ ))}
348
+ </Tbody>
349
+ </Table>
350
+ )}
351
+
352
+ <ConfirmDialog
353
+ bodyText={{
354
+ id: 'Settings.review-workflows.list.page.delete.confirm.body',
355
+ defaultMessage:
356
+ 'If you remove this worfklow, all stage-related information will be removed for this content-type. Are you sure you want to remove it?',
357
+ }}
358
+ isConfirmButtonLoading={isLoadingMutation}
359
+ isOpen={!!workflowToDelete}
360
+ onToggleDialog={toggleConfirmDeleteDialog}
361
+ onConfirm={handleConfirmDeleteDialog}
362
+ />
363
+
364
+ <LimitsModal.Root isOpen={showLimitModal} onClose={() => setShowLimitModal(false)}>
365
+ <LimitsModal.Title>
366
+ {formatMessage({
367
+ id: 'Settings.review-workflows.list.page.workflows.limit.title',
368
+ defaultMessage: 'You’ve reached the limit of workflows in your plan',
369
+ })}
370
+ </LimitsModal.Title>
371
+
372
+ <LimitsModal.Body>
373
+ {formatMessage({
374
+ id: 'Settings.review-workflows.list.page.workflows.limit.body',
375
+ defaultMessage: 'Delete a workflow or contact Sales to enable more workflows.',
376
+ })}
377
+ </LimitsModal.Body>
378
+ </LimitsModal.Root>
379
+ </Layout.Root>
380
+ </>
381
+ );
382
+ }
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+
3
+ import { ProtectedPage } from '../../components/ProtectedPage';
4
+
5
+ import { ReviewWorkflowsListView } from './ListView';
6
+
7
+ export default function () {
8
+ return (
9
+ <ProtectedPage>
10
+ <ReviewWorkflowsListView />
11
+ </ProtectedPage>
12
+ );
13
+ }