@strapi/admin 4.9.0 → 4.10.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/admin/src/content-manager/components/DynamicTable/CellContent/PublicationState/PublicationState.js +26 -0
  2. package/admin/src/content-manager/components/DynamicTable/CellContent/PublicationState/index.js +1 -0
  3. package/admin/src/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/ReviewWorkflowsStage.js +15 -0
  4. package/admin/src/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/index.js +1 -0
  5. package/admin/src/content-manager/components/DynamicTable/index.js +43 -49
  6. package/admin/src/content-manager/components/DynamicZone/components/DynamicComponent.js +2 -0
  7. package/admin/src/content-manager/components/EditViewDataManagerProvider/reducer.js +1 -3
  8. package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/findAllAndReplace.js +3 -10
  9. package/admin/src/content-manager/components/InputUID/endActionStyle.js +13 -4
  10. package/admin/src/content-manager/components/InputUID/index.js +71 -94
  11. package/admin/src/content-manager/pages/EditView/Information/index.js +77 -53
  12. package/admin/src/content-manager/pages/EditView/InformationBox/InformationBoxCE.js +13 -0
  13. package/admin/src/content-manager/pages/EditView/InformationBox/index.js +3 -0
  14. package/admin/src/content-manager/pages/EditView/index.js +3 -4
  15. package/admin/src/content-manager/pages/ListView/index.js +6 -9
  16. package/admin/src/hooks/useRegenerate/index.js +7 -12
  17. package/admin/src/index.js +1 -0
  18. package/admin/src/pages/AuthPage/components/Register/index.js +38 -46
  19. package/admin/src/pages/SettingsPage/components/Tokens/FormHead/index.js +0 -4
  20. package/admin/src/pages/SettingsPage/components/Tokens/Regenerate/index.js +3 -5
  21. package/admin/src/pages/SettingsPage/components/Tokens/TokenTypeSelect/index.js +5 -7
  22. package/admin/src/pages/SettingsPage/pages/TransferTokens/EditView/components/FormTransferTokenContainer/index.js +0 -41
  23. package/admin/src/pages/SettingsPage/pages/TransferTokens/EditView/index.js +9 -53
  24. package/admin/src/pages/SettingsPage/pages/TransferTokens/EditView/utils/schema.js +0 -1
  25. package/admin/src/pages/SettingsPage/pages/TransferTokens/ListView/index.js +5 -27
  26. package/admin/src/translations/en.json +6 -1
  27. package/build/2263.4c5916f9.chunk.js +98 -0
  28. package/build/4049.64715f20.chunk.js +1 -0
  29. package/build/4649.213b8a3b.chunk.js +30 -0
  30. package/build/6985.66cca29c.chunk.js +1 -0
  31. package/build/7259.aefb51e8.chunk.js +1 -0
  32. package/build/8469.853c822b.chunk.js +1 -0
  33. package/build/9505.dbe702ab.chunk.js +14 -0
  34. package/build/9816.01ee964f.chunk.js +2 -0
  35. package/build/Admin-authenticatedApp.f50ad423.chunk.js +79 -0
  36. package/build/Admin_InternalErrorPage.4ad8b0df.chunk.js +1 -0
  37. package/build/Admin_homePage.1411fb7c.chunk.js +68 -0
  38. package/build/Admin_marketplace.02608d56.chunk.js +22 -0
  39. package/build/Admin_pluginsPage.15e3b0fd.chunk.js +1 -0
  40. package/build/Admin_profilePage.76afeca0.chunk.js +15 -0
  41. package/build/Admin_settingsPage.147755cd.chunk.js +9 -0
  42. package/build/Upload_ConfigureTheView.34dde278.chunk.js +1 -0
  43. package/build/admin-app.55dd7921.chunk.js +112 -0
  44. package/build/admin-edit-roles-page.cf543488.chunk.js +216 -0
  45. package/build/admin-edit-users.31c20712.chunk.js +10 -0
  46. package/build/admin-roles-list.489c501f.chunk.js +2 -0
  47. package/build/admin-users.3e111a7d.chunk.js +11 -0
  48. package/build/api-tokens-create-page.4328b852.chunk.js +1 -0
  49. package/build/api-tokens-edit-page.bce5050f.chunk.js +1 -0
  50. package/build/api-tokens-list-page.93f24348.chunk.js +16 -0
  51. package/build/audit-logs-settings-page.7be97e82.chunk.js +1 -0
  52. package/build/content-manager.4480ae88.chunk.js +1137 -0
  53. package/build/content-type-builder-list-view.cf38fe2f.chunk.js +191 -0
  54. package/build/content-type-builder-translation-en-json.7961593e.chunk.js +1 -0
  55. package/build/content-type-builder.af9abf1e.chunk.js +126 -0
  56. package/build/email-settings-page.4bdbef9a.chunk.js +3 -0
  57. package/build/en-json.697b4bcf.chunk.js +1 -0
  58. package/build/{highlight.js.28a1547e.chunk.js → highlight.js.26ef649f.chunk.js} +2 -2
  59. package/build/i18n-settings-page.2bb5be96.chunk.js +1 -0
  60. package/build/index.html +1 -1
  61. package/build/main.af8c0f31.js +3790 -0
  62. package/build/review-workflows-settings.7a7dc773.chunk.js +57 -0
  63. package/build/runtime~main.5a95bee6.js +2 -0
  64. package/build/sso-settings-page.272b87c8.chunk.js +1 -0
  65. package/build/transfer-tokens-create-page.a1f14bb1.chunk.js +1 -0
  66. package/build/transfer-tokens-edit-page.00ee1c74.chunk.js +1 -0
  67. package/build/transfer-tokens-list-page.ce37354b.chunk.js +16 -0
  68. package/build/upload-settings.0875e973.chunk.js +1 -0
  69. package/build/{upload-translation-th-json.98d35574.chunk.js → upload-translation-th-json.3847dae0.chunk.js} +1 -1
  70. package/build/upload.c7da1611.chunk.js +13 -0
  71. package/build/users-advanced-settings-page.1d3c14c7.chunk.js +1 -0
  72. package/build/users-email-settings-page.e8db68c4.chunk.js +1 -0
  73. package/build/users-providers-settings-page.14cac425.chunk.js +1 -0
  74. package/build/users-roles-settings-page.2ea4de84.chunk.js +30 -0
  75. package/build/webhook-edit-page.329141a5.chunk.js +23 -0
  76. package/build/webhook-list-page.029957a4.chunk.js +1 -0
  77. package/ee/admin/content-manager/pages/EditView/InformationBox/InformationBoxEE.js +92 -0
  78. package/ee/admin/content-manager/pages/EditView/InformationBox/index.js +3 -0
  79. package/ee/admin/hooks/useSettingsMenu/utils/customAdminLinks.js +12 -12
  80. package/ee/admin/hooks/useSettingsMenu/utils/customGlobalLinks.js +21 -13
  81. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/ReviewWorkflows.js +195 -0
  82. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/actions/index.js +42 -0
  83. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/AddStage/AddStage.js +87 -0
  84. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/AddStage/index.js +1 -0
  85. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/Stage/Stage.js +90 -0
  86. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/Stage/index.js +1 -0
  87. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/Stages.js +92 -0
  88. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/index.js +1 -0
  89. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/constants.js +6 -0
  90. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/hooks/useReviewWorkflows.js +35 -0
  91. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/index.js +3 -0
  92. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/reducer/index.js +121 -0
  93. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/utils/getWorkflowValidationSchema.js +25 -0
  94. package/ee/admin/pages/SettingsPage/utils/customRoutes.js +16 -2
  95. package/ee/admin/permissions/customPermissions.js +3 -0
  96. package/ee/server/bootstrap.js +13 -0
  97. package/ee/server/config/admin-actions.js +10 -0
  98. package/ee/server/constants/default-stages.json +14 -0
  99. package/ee/server/constants/default-workflow.json +1 -0
  100. package/ee/server/constants/workflows.js +8 -0
  101. package/ee/server/content-types/index.js +9 -0
  102. package/ee/server/content-types/workflow/index.js +34 -0
  103. package/ee/server/content-types/workflow-stage/index.js +41 -0
  104. package/ee/server/controllers/index.js +2 -0
  105. package/ee/server/controllers/workflows/index.js +36 -0
  106. package/ee/server/controllers/workflows/stages/index.js +95 -0
  107. package/ee/server/index.js +1 -0
  108. package/ee/server/middlewares/review-workflows.js +40 -0
  109. package/ee/server/migrations/review-workflows.js +39 -0
  110. package/ee/server/register.js +9 -3
  111. package/ee/server/routes/index.js +104 -0
  112. package/ee/server/services/audit-logs.js +16 -75
  113. package/ee/server/services/index.js +4 -0
  114. package/ee/server/services/review-workflows/entity-service-decorator.js +42 -0
  115. package/ee/server/services/review-workflows/review-workflows.js +175 -0
  116. package/ee/server/services/review-workflows/stages.js +148 -0
  117. package/ee/server/services/review-workflows/workflows.js +25 -0
  118. package/ee/server/utils/index.js +8 -0
  119. package/ee/server/utils/review-workflows.js +25 -0
  120. package/ee/server/utils/test.js +11 -0
  121. package/ee/server/validation/review-workflows.js +24 -0
  122. package/jest.config.front.js +6 -1
  123. package/package.json +15 -17
  124. package/server/controllers/transfer/runner.js +2 -4
  125. package/server/middlewares/data-transfer.js +1 -4
  126. package/server/routes/transfer.js +4 -13
  127. package/server/services/constants.js +0 -4
  128. package/server/services/transfer/permission.js +1 -1
  129. package/server/services/transfer/token.js +31 -33
  130. package/server/validation/transfer/token.js +2 -10
  131. package/webpack.config.js +1 -1
  132. package/.eslintignore +0 -4
  133. package/.eslintrc.js +0 -14
  134. package/admin/src/components/LocalesProvider/__mocks__/useLocalesProvider.js +0 -7
  135. package/admin/src/hooks/useConfigurations/__mocks__/index.js +0 -7
  136. package/build/1387.84b454d3.chunk.js +0 -1
  137. package/build/1657.45231968.chunk.js +0 -168
  138. package/build/3081.bcf9a12f.chunk.js +0 -108
  139. package/build/462.8fff7f3b.chunk.js +0 -71
  140. package/build/4628.20631dd1.chunk.js +0 -1
  141. package/build/5542.b8240e3f.chunk.js +0 -70
  142. package/build/5563.905daa13.chunk.js +0 -79
  143. package/build/6404.68405699.chunk.js +0 -100
  144. package/build/7259.b7d00cea.chunk.js +0 -1
  145. package/build/8694.6522968d.chunk.js +0 -247
  146. package/build/9347.058ddb22.chunk.js +0 -1
  147. package/build/Admin-authenticatedApp.31bf88ef.chunk.js +0 -79
  148. package/build/Admin_InternalErrorPage.15c6bf07.chunk.js +0 -1
  149. package/build/Admin_homePage.da2181fe.chunk.js +0 -73
  150. package/build/Admin_marketplace.d99044eb.chunk.js +0 -31
  151. package/build/Admin_pluginsPage.f6b52ee9.chunk.js +0 -6
  152. package/build/Admin_profilePage.9112cffc.chunk.js +0 -15
  153. package/build/Admin_settingsPage.cb63220f.chunk.js +0 -79
  154. package/build/Upload_ConfigureTheView.eaaec495.chunk.js +0 -1
  155. package/build/admin-app.8cde5b22.chunk.js +0 -110
  156. package/build/admin-edit-roles-page.4f1858e9.chunk.js +0 -280
  157. package/build/admin-edit-users.7e14d85f.chunk.js +0 -10
  158. package/build/admin-roles-list.97e198f9.chunk.js +0 -31
  159. package/build/admin-users.d02de059.chunk.js +0 -34
  160. package/build/api-tokens-create-page.97595e12.chunk.js +0 -1
  161. package/build/api-tokens-edit-page.cd36e30e.chunk.js +0 -1
  162. package/build/api-tokens-list-page.6757c7b9.chunk.js +0 -16
  163. package/build/audit-logs-settings-page.ca9a3c46.chunk.js +0 -76
  164. package/build/content-manager.de0ee3e5.chunk.js +0 -1132
  165. package/build/content-type-builder-list-view.9c2c020c.chunk.js +0 -214
  166. package/build/content-type-builder-translation-en-json.e577d595.chunk.js +0 -1
  167. package/build/content-type-builder.ec5ac7ab.chunk.js +0 -126
  168. package/build/email-settings-page.1095e1ab.chunk.js +0 -10
  169. package/build/en-json.b052667a.chunk.js +0 -1
  170. package/build/i18n-settings-page.7d80aae0.chunk.js +0 -60
  171. package/build/main.d40f9ca1.js +0 -2280
  172. package/build/runtime~main.7cdc9956.js +0 -2
  173. package/build/sso-settings-page.1dd4886e.chunk.js +0 -1
  174. package/build/transfer-tokens-create-page.ec2ca215.chunk.js +0 -1
  175. package/build/transfer-tokens-edit-page.22bf28e5.chunk.js +0 -1
  176. package/build/transfer-tokens-list-page.cf8c77f2.chunk.js +0 -16
  177. package/build/upload-settings.945fdcfa.chunk.js +0 -13
  178. package/build/upload.a86b1054.chunk.js +0 -33
  179. package/build/users-advanced-settings-page.5b5a9baa.chunk.js +0 -8
  180. package/build/users-email-settings-page.e5506eb4.chunk.js +0 -23
  181. package/build/users-providers-settings-page.e32089c2.chunk.js +0 -28
  182. package/build/users-roles-settings-page.a5c5b0df.chunk.js +0 -30
  183. package/build/webhook-edit-page.213f0075.chunk.js +0 -75
  184. package/build/webhook-list-page.5beb2a5c.chunk.js +0 -71
@@ -0,0 +1,92 @@
1
+ import React from 'react';
2
+ import {
3
+ ReactSelect,
4
+ useCMEditViewDataManager,
5
+ useAPIErrorHandler,
6
+ useFetchClient,
7
+ } from '@strapi/helper-plugin';
8
+ import { Field, FieldLabel, FieldError, Flex } from '@strapi/design-system';
9
+ import { useIntl } from 'react-intl';
10
+ import { useMutation } from 'react-query';
11
+
12
+ import { useReviewWorkflows } from '../../../../pages/SettingsPage/pages/ReviewWorkflows/hooks/useReviewWorkflows';
13
+ import Information from '../../../../../../admin/src/content-manager/pages/EditView/Information';
14
+
15
+ const ATTRIBUTE_NAME = 'strapi_reviewWorkflows_stage';
16
+
17
+ export function InformationBoxEE() {
18
+ const {
19
+ initialData,
20
+ isCreatingEntry,
21
+ layout: { uid },
22
+ } = useCMEditViewDataManager();
23
+ const { put } = useFetchClient();
24
+ const activeWorkflowStage = initialData?.[ATTRIBUTE_NAME] ?? null;
25
+ const { formatMessage } = useIntl();
26
+ const { formatAPIError } = useAPIErrorHandler();
27
+
28
+ const { workflows: { data: [workflow] = [] } = {} } = useReviewWorkflows();
29
+
30
+ const { error, isLoading, mutateAsync } = useMutation(async ({ entityId, stageId, uid }) => {
31
+ const {
32
+ data: { data },
33
+ } = await put(`/admin/content-manager/collection-types/${uid}/${entityId}/stage`, {
34
+ data: { id: stageId },
35
+ });
36
+
37
+ return data;
38
+ });
39
+
40
+ // stages are empty while the workflow is loading
41
+ const options = (workflow?.stages ?? []).map(({ id, name }) => ({ value: id, label: name }));
42
+ const formattedError = error ? formatAPIError(error) : null;
43
+
44
+ const handleStageChange = async ({ value: stageId }) => {
45
+ try {
46
+ await mutateAsync({
47
+ entityId: initialData.id,
48
+ stageId,
49
+ uid,
50
+ });
51
+ } catch (error) {
52
+ // react-query@v3: the error doesn't have to be handled here
53
+ // see: https://github.com/TanStack/query/issues/121
54
+ }
55
+ };
56
+
57
+ return (
58
+ <Information.Root>
59
+ <Information.Title />
60
+
61
+ {activeWorkflowStage && (
62
+ <Field error={formattedError} name={ATTRIBUTE_NAME} id={ATTRIBUTE_NAME}>
63
+ <Flex direction="column" gap={2} alignItems="stretch">
64
+ <FieldLabel>
65
+ {formatMessage({
66
+ id: 'content-manager.reviewWorkflows.stage.label',
67
+ defaultMessage: 'Review stage',
68
+ })}
69
+ </FieldLabel>
70
+
71
+ <ReactSelect
72
+ error={formattedError}
73
+ inputId={ATTRIBUTE_NAME}
74
+ isDisabled={isCreatingEntry}
75
+ options={options}
76
+ name={ATTRIBUTE_NAME}
77
+ defaultValue={{ value: activeWorkflowStage?.id, label: activeWorkflowStage?.name }}
78
+ isLoading={isLoading}
79
+ isSearchable={false}
80
+ isClearable={false}
81
+ onChange={handleStageChange}
82
+ />
83
+
84
+ <FieldError />
85
+ </Flex>
86
+ </Field>
87
+ )}
88
+
89
+ <Information.Body />
90
+ </Information.Root>
91
+ );
92
+ }
@@ -0,0 +1,3 @@
1
+ import { InformationBoxEE } from './InformationBoxEE';
2
+
3
+ export default InformationBoxEE;
@@ -1,17 +1,17 @@
1
1
  import adminPermissions from '../../../../../admin/src/permissions';
2
2
 
3
- const auditLogsRoutes = strapi.features.isEnabled(strapi.features.AUDIT_LOGS)
4
- ? [
5
- {
6
- intlLabel: { id: 'global.auditLogs', defaultMessage: 'Audit Logs' },
7
- to: '/settings/audit-logs?pageSize=50&page=1&sort=date:DESC',
8
- id: 'auditLogs',
9
- isDisplayed: false,
10
- permissions: adminPermissions.settings.auditLogs.main,
11
- },
12
- ]
13
- : [];
3
+ const items = [];
14
4
 
15
- const customAdminLinks = [...auditLogsRoutes];
5
+ if (window.strapi.features.isEnabled(window.strapi.features.AUDIT_LOGS)) {
6
+ items.push({
7
+ intlLabel: { id: 'global.auditLogs', defaultMessage: 'Audit Logs' },
8
+ to: '/settings/audit-logs?pageSize=50&page=1&sort=date:DESC',
9
+ id: 'auditLogs',
10
+ isDisplayed: false,
11
+ permissions: adminPermissions.settings.auditLogs.main,
12
+ });
13
+ }
14
+
15
+ const customAdminLinks = items;
16
16
 
17
17
  export default customAdminLinks;
@@ -1,17 +1,25 @@
1
1
  import adminPermissions from '../../../../../admin/src/permissions';
2
2
 
3
- const ssoGlobalRoutes = strapi.features.isEnabled(strapi.features.SSO)
4
- ? [
5
- {
6
- intlLabel: { id: 'Settings.sso.title', defaultMessage: 'Single Sign-On' },
7
- to: '/settings/single-sign-on',
8
- id: 'sso',
9
- isDisplayed: false,
10
- permissions: adminPermissions.settings.sso.main,
11
- },
12
- ]
13
- : [];
3
+ const items = [];
14
4
 
15
- const customGlobalLinks = [...ssoGlobalRoutes];
5
+ if (window.strapi.features.isEnabled(window.strapi.features.SSO)) {
6
+ items.push({
7
+ intlLabel: { id: 'Settings.sso.title', defaultMessage: 'Single Sign-On' },
8
+ to: '/settings/single-sign-on',
9
+ id: 'sso',
10
+ isDisplayed: false,
11
+ permissions: adminPermissions.settings.sso.main,
12
+ });
13
+ }
16
14
 
17
- export default customGlobalLinks;
15
+ if (window.strapi.features.isEnabled(window.strapi.features.REVIEW_WORKFLOWS)) {
16
+ items.push({
17
+ intlLabel: { id: 'Settings.review-workflows.page.title', defaultMessage: 'Review Workflows' },
18
+ to: '/settings/review-workflows',
19
+ id: 'review-workflows',
20
+ isDisplayed: false,
21
+ permissions: adminPermissions.settings['review-workflows'].main,
22
+ });
23
+ }
24
+
25
+ export default items;
@@ -0,0 +1,195 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import { FormikProvider, useFormik, Form } from 'formik';
3
+ import { useIntl } from 'react-intl';
4
+ import { useSelector, useDispatch } from 'react-redux';
5
+ import { useMutation } from 'react-query';
6
+
7
+ import {
8
+ CheckPagePermissions,
9
+ ConfirmDialog,
10
+ SettingsPageTitle,
11
+ useAPIErrorHandler,
12
+ useFetchClient,
13
+ useNotification,
14
+ useTracking,
15
+ } from '@strapi/helper-plugin';
16
+ import { Button, ContentLayout, HeaderLayout, Layout, Loader, Main } from '@strapi/design-system';
17
+ import { Check } from '@strapi/icons';
18
+
19
+ import { Stages } from './components/Stages';
20
+ import { reducer, initialState } from './reducer';
21
+ import { REDUX_NAMESPACE } from './constants';
22
+ import { useInjectReducer } from '../../../../../../admin/src/hooks/useInjectReducer';
23
+ import { useReviewWorkflows } from './hooks/useReviewWorkflows';
24
+ import { setWorkflows } from './actions';
25
+ import { getWorkflowValidationSchema } from './utils/getWorkflowValidationSchema';
26
+ import adminPermissions from '../../../../../../admin/src/permissions';
27
+
28
+ export function ReviewWorkflowsPage() {
29
+ const { trackUsage } = useTracking();
30
+ const { formatMessage } = useIntl();
31
+ const dispatch = useDispatch();
32
+ const { put } = useFetchClient();
33
+ const { formatAPIError } = useAPIErrorHandler();
34
+ const toggleNotification = useNotification();
35
+ const { workflows: workflowsData, refetchWorkflow } = useReviewWorkflows();
36
+ const {
37
+ status,
38
+ clientState: {
39
+ currentWorkflow: {
40
+ data: currentWorkflow,
41
+ isDirty: currentWorkflowIsDirty,
42
+ hasDeletedServerStages: currentWorkflowHasDeletedServerStages,
43
+ },
44
+ },
45
+ } = useSelector((state) => state?.[REDUX_NAMESPACE] ?? initialState);
46
+ const [isConfirmDeleteDialogOpen, setIsConfirmDeleteDialogOpen] = useState(false);
47
+
48
+ const { mutateAsync, isLoading } = useMutation(
49
+ async ({ workflowId, stages }) => {
50
+ try {
51
+ const {
52
+ data: { data },
53
+ } = await put(`/admin/review-workflows/workflows/${workflowId}/stages`, {
54
+ data: stages,
55
+ });
56
+
57
+ return data;
58
+ } catch (error) {
59
+ toggleNotification({
60
+ type: 'warning',
61
+ message: formatAPIError(error),
62
+ });
63
+ }
64
+
65
+ return null;
66
+ },
67
+ {
68
+ onError(error) {
69
+ toggleNotification({
70
+ type: 'warning',
71
+ message: formatAPIError(error),
72
+ });
73
+ },
74
+
75
+ onSuccess() {
76
+ toggleNotification({
77
+ type: 'success',
78
+ message: { id: 'notification.success.saved', defaultMessage: 'Saved' },
79
+ });
80
+ },
81
+ }
82
+ );
83
+
84
+ const updateWorkflowStages = (workflowId, stages) => {
85
+ return mutateAsync({ workflowId, stages });
86
+ };
87
+
88
+ const submitForm = async () => {
89
+ setIsConfirmDeleteDialogOpen(false);
90
+
91
+ await updateWorkflowStages(currentWorkflow.id, currentWorkflow.stages);
92
+ refetchWorkflow();
93
+ };
94
+
95
+ const handleConfirmDeleteDialog = async () => {
96
+ await submitForm();
97
+ };
98
+
99
+ const toggleConfirmDeleteDialog = () => {
100
+ setIsConfirmDeleteDialogOpen((prev) => !prev);
101
+ };
102
+
103
+ const formik = useFormik({
104
+ enableReinitialize: true,
105
+ initialValues: currentWorkflow,
106
+ async onSubmit() {
107
+ if (currentWorkflowHasDeletedServerStages) {
108
+ setIsConfirmDeleteDialogOpen(true);
109
+ } else {
110
+ submitForm();
111
+ }
112
+ },
113
+ validationSchema: getWorkflowValidationSchema({ formatMessage }),
114
+ validateOnChange: false,
115
+ });
116
+
117
+ useInjectReducer(REDUX_NAMESPACE, reducer);
118
+
119
+ useEffect(() => {
120
+ dispatch(setWorkflows({ status: workflowsData.status, data: workflowsData.data }));
121
+ }, [workflowsData.status, workflowsData.data, dispatch]);
122
+
123
+ useEffect(() => {
124
+ trackUsage('didViewWorkflow');
125
+ }, [trackUsage]);
126
+
127
+ return (
128
+ <CheckPagePermissions permissions={adminPermissions.settings['review-workflows'].main}>
129
+ <Layout>
130
+ <SettingsPageTitle
131
+ name={formatMessage({
132
+ id: 'Settings.review-workflows.page.title',
133
+ defaultMessage: 'Review Workflows',
134
+ })}
135
+ />
136
+ <Main tabIndex={-1}>
137
+ <FormikProvider value={formik}>
138
+ <Form onSubmit={formik.handleSubmit}>
139
+ <HeaderLayout
140
+ primaryAction={
141
+ <Button
142
+ startIcon={<Check />}
143
+ type="submit"
144
+ size="M"
145
+ disabled={!currentWorkflowIsDirty}
146
+ >
147
+ {formatMessage({
148
+ id: 'global.save',
149
+ defaultMessage: 'Save',
150
+ })}
151
+ </Button>
152
+ }
153
+ title={formatMessage({
154
+ id: 'Settings.review-workflows.page.title',
155
+ defaultMessage: 'Review Workflows',
156
+ })}
157
+ subtitle={formatMessage(
158
+ {
159
+ id: 'Settings.review-workflows.page.subtitle',
160
+ defaultMessage: '{count, plural, one {# stage} other {# stages}}',
161
+ },
162
+ { count: currentWorkflow?.stages?.length ?? 0 }
163
+ )}
164
+ />
165
+ <ContentLayout>
166
+ {status === 'loading' && (
167
+ <Loader>
168
+ {formatMessage({
169
+ id: 'Settings.review-workflows.page.isLoading',
170
+ defaultMessage: 'Workflow is loading',
171
+ })}
172
+ </Loader>
173
+ )}
174
+
175
+ <Stages stages={formik.values?.stages} />
176
+ </ContentLayout>
177
+ </Form>
178
+ </FormikProvider>
179
+
180
+ <ConfirmDialog
181
+ bodyText={{
182
+ id: 'Settings.review-workflows.page.delete.confirm.body',
183
+ defaultMessage:
184
+ 'All entries assigned to deleted stages will be moved to the first stage. Are you sure you want to save this?',
185
+ }}
186
+ isConfirmButtonLoading={isLoading}
187
+ isOpen={isConfirmDeleteDialogOpen}
188
+ onToggleDialog={toggleConfirmDeleteDialog}
189
+ onConfirm={handleConfirmDeleteDialog}
190
+ />
191
+ </Main>
192
+ </Layout>
193
+ </CheckPagePermissions>
194
+ );
195
+ }
@@ -0,0 +1,42 @@
1
+ import {
2
+ ACTION_SET_WORKFLOWS,
3
+ ACTION_DELETE_STAGE,
4
+ ACTION_ADD_STAGE,
5
+ ACTION_UPDATE_STAGE,
6
+ } from '../constants';
7
+
8
+ export function setWorkflows({ status, data }) {
9
+ return {
10
+ type: ACTION_SET_WORKFLOWS,
11
+ payload: {
12
+ status,
13
+ workflows: data,
14
+ },
15
+ };
16
+ }
17
+
18
+ export function deleteStage(stageId) {
19
+ return {
20
+ type: ACTION_DELETE_STAGE,
21
+ payload: {
22
+ stageId,
23
+ },
24
+ };
25
+ }
26
+
27
+ export function addStage(stage = {}) {
28
+ return {
29
+ type: ACTION_ADD_STAGE,
30
+ payload: stage,
31
+ };
32
+ }
33
+
34
+ export function updateStage(stageId, payload) {
35
+ return {
36
+ type: ACTION_UPDATE_STAGE,
37
+ payload: {
38
+ stageId,
39
+ ...payload,
40
+ },
41
+ };
42
+ }
@@ -0,0 +1,87 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import styled from 'styled-components';
4
+
5
+ import { Box, Flex, Typography } from '@strapi/design-system';
6
+ import { PlusCircle } from '@strapi/icons';
7
+
8
+ const StyledAddIcon = styled(PlusCircle)`
9
+ > circle {
10
+ fill: ${({ theme }) => theme.colors.neutral150};
11
+ }
12
+ > path {
13
+ fill: ${({ theme }) => theme.colors.neutral600};
14
+ }
15
+ `;
16
+
17
+ const StyledButton = styled(Box)`
18
+ border-radius: 26px;
19
+
20
+ svg {
21
+ height: ${({ theme }) => theme.spaces[6]};
22
+ width: ${({ theme }) => theme.spaces[6]};
23
+
24
+ > path {
25
+ fill: ${({ theme }) => theme.colors.neutral600};
26
+ }
27
+ }
28
+
29
+ &:hover {
30
+ color: ${({ theme }) => theme.colors.primary600} !important;
31
+ ${Typography} {
32
+ color: ${({ theme }) => theme.colors.primary600} !important;
33
+ }
34
+
35
+ ${StyledAddIcon} {
36
+ > circle {
37
+ fill: ${({ theme }) => theme.colors.primary600};
38
+ }
39
+ > path {
40
+ fill: ${({ theme }) => theme.colors.neutral100};
41
+ }
42
+ }
43
+ }
44
+
45
+ &:active {
46
+ ${Typography} {
47
+ color: ${({ theme }) => theme.colors.primary600};
48
+ }
49
+
50
+ ${StyledAddIcon} {
51
+ > circle {
52
+ fill: ${({ theme }) => theme.colors.primary600};
53
+ }
54
+ > path {
55
+ fill: ${({ theme }) => theme.colors.neutral100};
56
+ }
57
+ }
58
+ }
59
+ `;
60
+
61
+ export function AddStage({ children, ...props }) {
62
+ return (
63
+ <StyledButton
64
+ as="button"
65
+ background="neutral0"
66
+ border="neutral150"
67
+ paddingBottom={3}
68
+ paddingLeft={4}
69
+ paddingRight={4}
70
+ paddingTop={3}
71
+ shadow="filterShadow"
72
+ {...props}
73
+ >
74
+ <Flex gap={2}>
75
+ <StyledAddIcon aria-hidden />
76
+
77
+ <Typography variant="pi" fontWeight="bold" textColor="neutral500">
78
+ {children}
79
+ </Typography>
80
+ </Flex>
81
+ </StyledButton>
82
+ );
83
+ }
84
+
85
+ AddStage.propTypes = {
86
+ children: PropTypes.node.isRequired,
87
+ };
@@ -0,0 +1,90 @@
1
+ import React, { useState } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { useField } from 'formik';
4
+ import { useIntl } from 'react-intl';
5
+ import { useDispatch } from 'react-redux';
6
+ import {
7
+ Accordion,
8
+ AccordionToggle,
9
+ AccordionContent,
10
+ Grid,
11
+ GridItem,
12
+ IconButton,
13
+ TextInput,
14
+ } from '@strapi/design-system';
15
+ import { useTracking } from '@strapi/helper-plugin';
16
+ import { Trash } from '@strapi/icons';
17
+
18
+ import { deleteStage, updateStage } from '../../../actions';
19
+
20
+ function Stage({ id, name, index, canDelete, isOpen: isOpenDefault = false }) {
21
+ const { formatMessage } = useIntl();
22
+ const { trackUsage } = useTracking();
23
+ const [isOpen, setIsOpen] = useState(isOpenDefault);
24
+ const fieldIdentifier = `stages.${index}.name`;
25
+ const [field, meta] = useField(fieldIdentifier);
26
+ const dispatch = useDispatch();
27
+
28
+ return (
29
+ <Accordion
30
+ size="S"
31
+ variant="primary"
32
+ onToggle={() => {
33
+ setIsOpen(!isOpen);
34
+
35
+ if (!isOpen) {
36
+ trackUsage('willEditStage');
37
+ }
38
+ }}
39
+ expanded={isOpen}
40
+ shadow="tableShadow"
41
+ >
42
+ <AccordionToggle
43
+ title={name}
44
+ togglePosition="left"
45
+ action={
46
+ canDelete ? (
47
+ <IconButton
48
+ backgroundColor="transparent"
49
+ noBorder
50
+ onClick={() => dispatch(deleteStage(id))}
51
+ label={formatMessage({
52
+ id: 'Settings.review-workflows.stage.delete',
53
+ defaultMessage: 'Delete stage',
54
+ })}
55
+ icon={<Trash />}
56
+ />
57
+ ) : null
58
+ }
59
+ />
60
+ <AccordionContent padding={6} background="neutral0">
61
+ <Grid gap={4}>
62
+ <GridItem col={6}>
63
+ <TextInput
64
+ {...field}
65
+ id={fieldIdentifier}
66
+ value={name}
67
+ label={formatMessage({
68
+ id: 'Settings.review-workflows.stage.name.label',
69
+ defaultMessage: 'Stage name',
70
+ })}
71
+ error={meta.error ?? false}
72
+ onChange={(event) => {
73
+ field.onChange(event);
74
+ dispatch(updateStage(id, { name: event.target.value }));
75
+ }}
76
+ />
77
+ </GridItem>
78
+ </Grid>
79
+ </AccordionContent>
80
+ </Accordion>
81
+ );
82
+ }
83
+
84
+ export { Stage };
85
+
86
+ Stage.propTypes = PropTypes.shape({
87
+ id: PropTypes.number.isRequired,
88
+ name: PropTypes.string.isRequired,
89
+ canDelete: PropTypes.bool.isRequired,
90
+ }).isRequired;
@@ -0,0 +1,92 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import styled from 'styled-components';
4
+ import { useIntl } from 'react-intl';
5
+ import { useDispatch } from 'react-redux';
6
+ import { Box, Flex } from '@strapi/design-system';
7
+ import { useTracking } from '@strapi/helper-plugin';
8
+
9
+ import { addStage } from '../../actions';
10
+ import { AddStage } from '../AddStage';
11
+ import { Stage } from './Stage';
12
+
13
+ const StagesContainer = styled(Box)`
14
+ position: relative;
15
+ `;
16
+
17
+ const Background = styled(Box)`
18
+ left: 50%;
19
+ position: absolute;
20
+ top: 0;
21
+ transform: translateX(-50%);
22
+ `;
23
+
24
+ function Stages({ stages }) {
25
+ const { formatMessage } = useIntl();
26
+ const dispatch = useDispatch();
27
+ const { trackUsage } = useTracking();
28
+
29
+ return (
30
+ <Flex direction="column" gap={6} width="100%">
31
+ <StagesContainer spacing={4} width="100%">
32
+ <Background background="neutral200" height="100%" width={2} zIndex={1} />
33
+
34
+ <Flex
35
+ direction="column"
36
+ alignItems="stretch"
37
+ gap={6}
38
+ zIndex={2}
39
+ position="relative"
40
+ as="ol"
41
+ >
42
+ {stages.map((stage, index) => {
43
+ const id = stage?.id ?? stage.__temp_key__;
44
+
45
+ return (
46
+ <Box key={`stage-${id}`} as="li">
47
+ <Stage
48
+ {...stage}
49
+ id={id}
50
+ index={index}
51
+ canDelete={stages.length > 1}
52
+ isOpen={!stage.id}
53
+ />
54
+ </Box>
55
+ );
56
+ })}
57
+ </Flex>
58
+ </StagesContainer>
59
+
60
+ <Flex direction="column" gap={6}>
61
+ <AddStage
62
+ type="button"
63
+ onClick={() => {
64
+ dispatch(addStage({ name: '' }));
65
+ trackUsage('willCreateStage');
66
+ }}
67
+ >
68
+ {formatMessage({
69
+ id: 'Settings.review-workflows.stage.add',
70
+ defaultMessage: 'Add new stage',
71
+ })}
72
+ </AddStage>
73
+ </Flex>
74
+ </Flex>
75
+ );
76
+ }
77
+
78
+ export { Stages };
79
+
80
+ Stages.defaultProps = {
81
+ stages: [],
82
+ };
83
+
84
+ Stages.propTypes = {
85
+ stages: PropTypes.arrayOf(
86
+ PropTypes.shape({
87
+ id: PropTypes.number,
88
+ __temp_key__: PropTypes.number,
89
+ name: PropTypes.string.isRequired,
90
+ })
91
+ ),
92
+ };
@@ -0,0 +1,6 @@
1
+ export const REDUX_NAMESPACE = 'settings_review-workflows';
2
+
3
+ export const ACTION_SET_WORKFLOWS = `Settings/Review_Workflows/SET_WORKFLOWS`;
4
+ export const ACTION_DELETE_STAGE = `Settings/Review_Workflows/WORKFLOW_DELETE_STAGE`;
5
+ export const ACTION_ADD_STAGE = `Settings/Review_Workflows/WORKFLOW_ADD_STAGE`;
6
+ export const ACTION_UPDATE_STAGE = `Settings/Review_Workflows/WORKFLOW_UPDATE_STAGE`;