@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,111 @@
1
+ import * as React from 'react';
2
+
3
+ import { Box, Flex, IconButton, ModalLayout, ModalBody, Typography } from '@strapi/design-system';
4
+ import { LinkButton } from '@strapi/design-system/v2';
5
+ import { Cross } from '@strapi/icons';
6
+ import PropTypes from 'prop-types';
7
+ import { useIntl } from 'react-intl';
8
+ import styled from 'styled-components';
9
+
10
+ import balloonImageSrc from './assets/balloon.png';
11
+
12
+ const TITLE_ID = 'limits-title';
13
+
14
+ const CTA_LEARN_MORE_HREF = 'https://strapi.io/pricing-cloud';
15
+ const CTA_SALES_HREF = 'https://strapi.io/contact-sales';
16
+
17
+ export function Title({ children }) {
18
+ return (
19
+ <Typography variant="alpha" id={TITLE_ID}>
20
+ {children}
21
+ </Typography>
22
+ );
23
+ }
24
+
25
+ Title.propTypes = {
26
+ children: PropTypes.node.isRequired,
27
+ };
28
+
29
+ export function Body({ children }) {
30
+ return <Typography variant="omega">{children}</Typography>;
31
+ }
32
+
33
+ Body.propTypes = {
34
+ children: PropTypes.node.isRequired,
35
+ };
36
+
37
+ function CallToActions() {
38
+ const { formatMessage } = useIntl();
39
+
40
+ return (
41
+ <Flex gap={2} paddingTop={4}>
42
+ <LinkButton variant="default" isExternal href={CTA_LEARN_MORE_HREF}>
43
+ {formatMessage({
44
+ id: 'Settings.review-workflows.limit.cta.learn',
45
+ defaultMessage: 'Learn more',
46
+ })}
47
+ </LinkButton>
48
+
49
+ <LinkButton variant="tertiary" isExternal href={CTA_SALES_HREF}>
50
+ {formatMessage({
51
+ id: 'Settings.review-workflows.limit.cta.sales',
52
+ defaultMessage: 'Contact Sales',
53
+ })}
54
+ </LinkButton>
55
+ </Flex>
56
+ );
57
+ }
58
+
59
+ const BalloonImage = styled.img`
60
+ // Margin top|right reverse the padding of ModalBody
61
+ margin-right: ${({ theme }) => `-${theme.spaces[7]}`};
62
+ margin-top: ${({ theme }) => `-${theme.spaces[7]}`};
63
+ width: 360px;
64
+ `;
65
+
66
+ export function LimitsModal({ children, isOpen, onClose }) {
67
+ const { formatMessage } = useIntl();
68
+
69
+ if (!isOpen) {
70
+ return null;
71
+ }
72
+
73
+ return (
74
+ <ModalLayout labelledBy={TITLE_ID}>
75
+ <ModalBody>
76
+ <Flex gap={2} paddingLeft={7} position="relative">
77
+ <Flex alignItems="start" direction="column" gap={2} width="60%">
78
+ {children}
79
+
80
+ <CallToActions />
81
+ </Flex>
82
+
83
+ <Flex justifyContent="end" height="100%" width="40%">
84
+ <BalloonImage src={balloonImageSrc} aria-hidden alt="" loading="lazy" />
85
+
86
+ <Box display="flex" position="absolute" right={0} top={0}>
87
+ <IconButton
88
+ icon={<Cross />}
89
+ aria-label={formatMessage({
90
+ id: 'global.close',
91
+ defaultMessage: 'Close',
92
+ })}
93
+ onClick={onClose}
94
+ />
95
+ </Box>
96
+ </Flex>
97
+ </Flex>
98
+ </ModalBody>
99
+ </ModalLayout>
100
+ );
101
+ }
102
+
103
+ LimitsModal.defaultProps = {
104
+ isOpen: false,
105
+ };
106
+
107
+ LimitsModal.propTypes = {
108
+ children: PropTypes.node.isRequired,
109
+ isOpen: PropTypes.bool,
110
+ onClose: PropTypes.func.isRequired,
111
+ };
@@ -0,0 +1,3 @@
1
+ import { Title, Body, LimitsModal as Root } from './LimitsModal';
2
+
3
+ export { Title, Body, Root };
@@ -0,0 +1,21 @@
1
+ import React from 'react';
2
+
3
+ import { CheckPagePermissions } from '@strapi/helper-plugin';
4
+ import PropTypes from 'prop-types';
5
+ import { useSelector } from 'react-redux';
6
+
7
+ import { selectAdminPermissions } from '../../../../../../../../admin/src/pages/App/selectors';
8
+
9
+ export function ProtectedPage({ children }) {
10
+ const permissions = useSelector(selectAdminPermissions);
11
+
12
+ return (
13
+ <CheckPagePermissions permissions={permissions.settings.users.main}>
14
+ {children}
15
+ </CheckPagePermissions>
16
+ );
17
+ }
18
+
19
+ ProtectedPage.propTypes = {
20
+ children: PropTypes.node.isRequired,
21
+ };
@@ -0,0 +1 @@
1
+ export * from './ProtectedPage';
@@ -141,8 +141,8 @@ export function Stage({
141
141
  const { trackUsage } = useTracking();
142
142
  const dispatch = useDispatch();
143
143
  const [isOpen, setIsOpen] = React.useState(isOpenDefault);
144
- const [nameField, nameMeta] = useField(`stages.${index}.name`);
145
- const [colorField, colorMeta] = useField(`stages.${index}.color`);
144
+ const [nameField, nameMeta, nameHelper] = useField(`stages.${index}.name`);
145
+ const [colorField, colorMeta, colorHelper] = useField(`stages.${index}.color`);
146
146
  const [{ handlerId, isDragging, handleKeyDown }, stageRef, dropRef, dragRef, dragPreviewRef] =
147
147
  useDragAndDrop(canReorder, {
148
148
  index,
@@ -174,7 +174,7 @@ export function Stage({
174
174
  dragPreviewRef(getEmptyImage(), { captureDraggingState: false });
175
175
  }, [dragPreviewRef, index]);
176
176
 
177
- const { themeColorName } = colorField.value ? getStageColorByHex(colorField.value) : {};
177
+ const { themeColorName } = getStageColorByHex(colorField.value) ?? {};
178
178
 
179
179
  return (
180
180
  <Box ref={composedRef}>
@@ -246,7 +246,7 @@ export function Stage({
246
246
  })}
247
247
  error={nameMeta.error ?? false}
248
248
  onChange={(event) => {
249
- nameField.onChange(event);
249
+ nameHelper.setValue(event.target.value);
250
250
  dispatch(updateStage(id, { name: event.target.value }));
251
251
  }}
252
252
  required
@@ -263,7 +263,7 @@ export function Stage({
263
263
  defaultMessage: 'Color',
264
264
  })}
265
265
  onChange={(value) => {
266
- colorField.onChange({ target: { value } });
266
+ colorHelper.setValue(value);
267
267
  dispatch(updateStage(id, { color: value }));
268
268
  }}
269
269
  value={colorField.value.toUpperCase()}
@@ -0,0 +1,110 @@
1
+ import * as React from 'react';
2
+
3
+ import { Grid, GridItem, MultiSelectNested, TextInput } from '@strapi/design-system';
4
+ import { useCollator } from '@strapi/helper-plugin';
5
+ import { useField } from 'formik';
6
+ import PropTypes from 'prop-types';
7
+ import { useIntl } from 'react-intl';
8
+ import { useDispatch } from 'react-redux';
9
+
10
+ import { updateWorkflow } from '../../actions';
11
+
12
+ export function WorkflowAttributes({ contentTypes: { collectionTypes, singleTypes } }) {
13
+ const { formatMessage, locale } = useIntl();
14
+ const dispatch = useDispatch();
15
+ const [nameField, nameMeta, nameHelper] = useField('name');
16
+ const [contentTypesField, contentTypesMeta, contentTypesHelper] = useField('contentTypes');
17
+ const formatter = useCollator(locale, {
18
+ sensitivity: 'base',
19
+ });
20
+
21
+ return (
22
+ <Grid background="neutral0" hasRadius gap={4} padding={6} shadow="tableShadow">
23
+ <GridItem col={6}>
24
+ <TextInput
25
+ {...nameField}
26
+ id={nameField.name}
27
+ label={formatMessage({
28
+ id: 'Settings.review-workflows.workflow.name.label',
29
+ defaultMessage: 'Workflow Name',
30
+ })}
31
+ error={nameMeta.error ?? false}
32
+ onChange={(event) => {
33
+ dispatch(updateWorkflow({ name: event.target.value }));
34
+ nameHelper.setValue(event.target.value);
35
+ }}
36
+ required
37
+ />
38
+ </GridItem>
39
+
40
+ <GridItem col={6}>
41
+ <MultiSelectNested
42
+ {...contentTypesField}
43
+ customizeContent={(value) =>
44
+ formatMessage(
45
+ {
46
+ id: 'Settings.review-workflows.workflow.contentTypes.displayValue',
47
+ defaultMessage:
48
+ '{count} {count, plural, one {content type} other {content types}} selected',
49
+ },
50
+ { count: value.length }
51
+ )
52
+ }
53
+ error={contentTypesMeta.error ?? false}
54
+ id={contentTypesField.name}
55
+ label={formatMessage({
56
+ id: 'Settings.review-workflows.workflow.contentTypes.label',
57
+ defaultMessage: 'Associated to',
58
+ })}
59
+ onChange={(values) => {
60
+ dispatch(updateWorkflow({ contentTypes: values }));
61
+ contentTypesHelper.setValue(values);
62
+ }}
63
+ options={[
64
+ {
65
+ label: formatMessage({
66
+ id: 'Settings.review-workflows.workflow.contentTypes.collectionTypes.label',
67
+ defaultMessage: 'Collection Types',
68
+ }),
69
+ children: collectionTypes
70
+ .sort((a, b) => formatter.compare(a.info.displayName, b.info.displayName))
71
+ .map((contentType) => ({
72
+ label: contentType.info.displayName,
73
+ value: contentType.uid,
74
+ })),
75
+ },
76
+
77
+ {
78
+ label: formatMessage({
79
+ id: 'Settings.review-workflows.workflow.contentTypes.singleTypes.label',
80
+ defaultMessage: 'Single Types',
81
+ }),
82
+ children: singleTypes.map((contentType) => ({
83
+ label: contentType.info.displayName,
84
+ value: contentType.uid,
85
+ })),
86
+ },
87
+ ]}
88
+ placeholder={formatMessage({
89
+ id: 'Settings.review-workflows.workflow.contentTypes.placeholder',
90
+ defaultMessage: 'Select',
91
+ })}
92
+ />
93
+ </GridItem>
94
+ </Grid>
95
+ );
96
+ }
97
+
98
+ const ContentTypeType = PropTypes.shape({
99
+ uid: PropTypes.string.isRequired,
100
+ info: PropTypes.shape({
101
+ displayName: PropTypes.string.isRequired,
102
+ }).isRequired,
103
+ });
104
+
105
+ WorkflowAttributes.propTypes = {
106
+ contentTypes: PropTypes.shape({
107
+ collectionTypes: PropTypes.arrayOf(ContentTypeType).isRequired,
108
+ singleTypes: PropTypes.arrayOf(ContentTypeType).isRequired,
109
+ }).isRequired,
110
+ };
@@ -0,0 +1 @@
1
+ export * from './WorkflowAttributes';
@@ -2,11 +2,13 @@ import { lightTheme } from '@strapi/design-system';
2
2
 
3
3
  export const REDUX_NAMESPACE = 'settings_review-workflows';
4
4
 
5
- export const ACTION_SET_WORKFLOWS = `Settings/Review_Workflows/SET_WORKFLOWS`;
5
+ export const ACTION_RESET_WORKFLOW = `Settings/Review_Workflows/RESET_WORKFLOW`;
6
+ export const ACTION_SET_WORKFLOW = `Settings/Review_Workflows/SET_WORKFLOW`;
6
7
  export const ACTION_DELETE_STAGE = `Settings/Review_Workflows/WORKFLOW_DELETE_STAGE`;
7
8
  export const ACTION_ADD_STAGE = `Settings/Review_Workflows/WORKFLOW_ADD_STAGE`;
8
9
  export const ACTION_UPDATE_STAGE = `Settings/Review_Workflows/WORKFLOW_UPDATE_STAGE`;
9
10
  export const ACTION_UPDATE_STAGE_POSITION = `Settings/Review_Workflows/WORKFLOW_UPDATE_STAGE_POSITION`;
11
+ export const ACTION_UPDATE_WORKFLOW = `Settings/Review_Workflows/WORKFLOW_UPDATE`;
10
12
 
11
13
  export const STAGE_COLORS = {
12
14
  primary600: 'Blue',
@@ -1,43 +1,37 @@
1
1
  import { useFetchClient } from '@strapi/helper-plugin';
2
- import { stringify } from 'qs';
3
2
  import { useQuery } from 'react-query';
4
3
 
5
4
  export function useReviewWorkflows(params = {}) {
5
+ const { get } = useFetchClient();
6
+
6
7
  const { id = '', ...queryParams } = params;
7
8
  const defaultQueryParams = {
8
9
  populate: 'stages',
9
10
  };
10
11
 
11
- const { get } = useFetchClient();
12
- const queryString = stringify({ ...defaultQueryParams, ...queryParams }, { encode: false });
13
-
14
12
  const { data, isLoading, status, refetch } = useQuery(
15
13
  ['review-workflows', 'workflows', id],
16
14
  async () => {
17
- try {
18
- const {
19
- data: { data },
20
- } = await get(
21
- `/admin/review-workflows/workflows/${id}${queryString ? `?${queryString}` : ''}`
22
- );
15
+ const res = await get(`/admin/review-workflows/workflows/${id}`, {
16
+ params: { ...defaultQueryParams, ...queryParams },
17
+ });
23
18
 
24
- return data;
25
- } catch (err) {
26
- // silence
27
- return null;
28
- }
19
+ return res.data;
29
20
  }
30
21
  );
31
22
 
32
23
  let workflows = [];
33
24
 
34
- if (id && data) {
35
- workflows = [data];
36
- } else if (Array.isArray(data)) {
37
- workflows = data;
25
+ if (id && data?.data) {
26
+ workflows = [data.data];
27
+ } else if (Array.isArray(data?.data)) {
28
+ workflows = data.data;
38
29
  }
39
30
 
40
31
  return {
32
+ // meta contains e.g. the total of all workflows. we can not use
33
+ // the pagination object here, because the list is not paginated.
34
+ meta: data?.meta ?? {},
41
35
  workflows,
42
36
  isLoading,
43
37
  status,
@@ -0,0 +1,246 @@
1
+ import * as React from 'react';
2
+
3
+ import { Button, Flex, Loader } from '@strapi/design-system';
4
+ import { useAPIErrorHandler, useFetchClient, useNotification } from '@strapi/helper-plugin';
5
+ import { Check } from '@strapi/icons';
6
+ import { useFormik, Form, FormikProvider } from 'formik';
7
+ import { useIntl } from 'react-intl';
8
+ import { useMutation } from 'react-query';
9
+ import { useDispatch, useSelector } from 'react-redux';
10
+ import { useHistory } from 'react-router-dom';
11
+
12
+ import { useContentTypes } from '../../../../../../../../admin/src/hooks/useContentTypes';
13
+ import { useInjectReducer } from '../../../../../../../../admin/src/hooks/useInjectReducer';
14
+ import { useLicenseLimits } from '../../../../../../hooks';
15
+ import { resetWorkflow } from '../../actions';
16
+ import * as Layout from '../../components/Layout';
17
+ import * as LimitsModal from '../../components/LimitsModal';
18
+ import { Stages } from '../../components/Stages';
19
+ import { WorkflowAttributes } from '../../components/WorkflowAttributes';
20
+ import { REDUX_NAMESPACE } from '../../constants';
21
+ import { useReviewWorkflows } from '../../hooks/useReviewWorkflows';
22
+ import { reducer, initialState } from '../../reducer';
23
+ import { getWorkflowValidationSchema } from '../../utils/getWorkflowValidationSchema';
24
+
25
+ export function ReviewWorkflowsCreateView() {
26
+ const { formatMessage } = useIntl();
27
+ const { post } = useFetchClient();
28
+ const { push } = useHistory();
29
+ const { formatAPIError } = useAPIErrorHandler();
30
+ const dispatch = useDispatch();
31
+ const toggleNotification = useNotification();
32
+ const { collectionTypes, singleTypes, isLoading: isLoadingModels } = useContentTypes();
33
+ const {
34
+ clientState: {
35
+ currentWorkflow: { data: currentWorkflow, isDirty: currentWorkflowIsDirty },
36
+ },
37
+ } = useSelector((state) => state?.[REDUX_NAMESPACE] ?? initialState);
38
+ const [showLimitModal, setShowLimitModal] = React.useState(false);
39
+ const { isLoading: isLicenseLoading, getFeature } = useLicenseLimits();
40
+ const { meta, isLoading: isWorkflowLoading } = useReviewWorkflows();
41
+
42
+ const { mutateAsync, isLoading } = useMutation(
43
+ async ({ workflow }) => {
44
+ const {
45
+ data: { data },
46
+ } = await post(`/admin/review-workflows/workflows`, {
47
+ data: workflow,
48
+ });
49
+
50
+ return data;
51
+ },
52
+ {
53
+ onSuccess() {
54
+ toggleNotification({
55
+ type: 'success',
56
+ message: {
57
+ id: 'Settings.review-workflows.create.page.notification.success',
58
+ defaultMessage: 'Workflow successfully created',
59
+ },
60
+ });
61
+ },
62
+ }
63
+ );
64
+
65
+ const submitForm = async () => {
66
+ try {
67
+ const workflow = await mutateAsync({ workflow: currentWorkflow });
68
+
69
+ push(`/settings/review-workflows/${workflow.id}`);
70
+
71
+ return workflow;
72
+ } catch (error) {
73
+ toggleNotification({
74
+ type: 'warning',
75
+ message: formatAPIError(error),
76
+ });
77
+
78
+ return null;
79
+ }
80
+ };
81
+
82
+ const limits = getFeature('review-workflows');
83
+
84
+ const formik = useFormik({
85
+ enableReinitialize: true,
86
+ initialValues: currentWorkflow,
87
+ async onSubmit() {
88
+ /**
89
+ * If the current license has a limit, check if the total count of workflows
90
+ * exceeds that limit and display the limits modal instead of sending the
91
+ * update, because it would throw an API error.
92
+ */
93
+
94
+ if (limits?.workflows && meta?.workflowCount >= parseInt(limits.workflows, 10)) {
95
+ setShowLimitModal('workflow');
96
+
97
+ /**
98
+ * If the current license has a limit, check if the total count of stages
99
+ * exceeds that limit and display the limits modal instead of sending the
100
+ * update, because it would throw an API error.
101
+ */
102
+ } else if (
103
+ limits?.stagesPerWorkflow &&
104
+ currentWorkflow.stages.length >= parseInt(limits.stagesPerWorkflow, 10)
105
+ ) {
106
+ setShowLimitModal('stage');
107
+ } else {
108
+ submitForm();
109
+ }
110
+ },
111
+ validationSchema: getWorkflowValidationSchema({ formatMessage }),
112
+ });
113
+
114
+ useInjectReducer(REDUX_NAMESPACE, reducer);
115
+
116
+ React.useEffect(() => {
117
+ dispatch(resetWorkflow());
118
+ }, [dispatch]);
119
+
120
+ /**
121
+ * If the current license has a limit:
122
+ * check if the total count of workflows or stages exceeds that limit and display
123
+ * the limits modal on page load. It can be closed by the user, but the
124
+ * API will throw an error in case they try to create a new workflow or update the
125
+ * stages.
126
+ *
127
+ * If the current license does not have a limit (e.g. offline license):
128
+ * do nothing (for now). In case they are trying to create the 201st workflow/ stage
129
+ * the API will throw an error.
130
+ *
131
+ */
132
+
133
+ React.useEffect(() => {
134
+ if (!isWorkflowLoading && !isLicenseLoading) {
135
+ if (limits.workflows && meta?.workflowsTotal >= parseInt(limits.workflows, 10)) {
136
+ setShowLimitModal('workflow');
137
+ } else if (
138
+ limits.stagesPerWorkflow &&
139
+ currentWorkflow.stages.length >= parseInt(limits.stagesPerWorkflow, 10)
140
+ ) {
141
+ setShowLimitModal('stage');
142
+ }
143
+ }
144
+ }, [
145
+ isLicenseLoading,
146
+ isWorkflowLoading,
147
+ limits.stagesPerWorkflow,
148
+ limits?.workflows,
149
+ meta?.workflowsTotal,
150
+ currentWorkflow.stages.length,
151
+ ]);
152
+
153
+ return (
154
+ <>
155
+ <Layout.DragLayerRendered />
156
+
157
+ <FormikProvider value={formik}>
158
+ <Form onSubmit={formik.handleSubmit}>
159
+ <Layout.Header
160
+ navigationAction={<Layout.Back href="/settings/review-workflows" />}
161
+ primaryAction={
162
+ <Button
163
+ startIcon={<Check />}
164
+ type="submit"
165
+ size="M"
166
+ disabled={!currentWorkflowIsDirty}
167
+ isLoading={isLoading}
168
+ >
169
+ {formatMessage({
170
+ id: 'global.save',
171
+ defaultMessage: 'Save',
172
+ })}
173
+ </Button>
174
+ }
175
+ title={formatMessage({
176
+ id: 'Settings.review-workflows.create.page.title',
177
+ defaultMessage: 'Create Review Workflow',
178
+ })}
179
+ subtitle={formatMessage(
180
+ {
181
+ id: 'Settings.review-workflows.page.subtitle',
182
+ defaultMessage: '{count, plural, one {# stage} other {# stages}}',
183
+ },
184
+ { count: currentWorkflow?.stages?.length ?? 0 }
185
+ )}
186
+ />
187
+ <Layout.Root>
188
+ <Flex alignItems="stretch" direction="column" gap={7}>
189
+ {isLoadingModels ? (
190
+ <Loader>
191
+ {formatMessage({
192
+ id: 'Settings.review-workflows.page.isLoading',
193
+ defaultMessage: 'Workflow is loading',
194
+ })}
195
+ </Loader>
196
+ ) : (
197
+ <Flex alignItems="stretch" direction="column" gap={7}>
198
+ <WorkflowAttributes contentTypes={{ collectionTypes, singleTypes }} />
199
+ <Stages stages={formik.values?.stages} />
200
+ </Flex>
201
+ )}
202
+ </Flex>
203
+ </Layout.Root>
204
+ </Form>
205
+ </FormikProvider>
206
+
207
+ <LimitsModal.Root
208
+ isOpen={showLimitModal === 'workflow'}
209
+ onClose={() => setShowLimitModal(false)}
210
+ >
211
+ <LimitsModal.Title>
212
+ {formatMessage({
213
+ id: 'Settings.review-workflows.create.page.workflows.limit.title',
214
+ defaultMessage: 'You’ve reached the limit of workflows in your plan',
215
+ })}
216
+ </LimitsModal.Title>
217
+
218
+ <LimitsModal.Body>
219
+ {formatMessage({
220
+ id: 'Settings.review-workflows.create.page.workflows.limit.body',
221
+ defaultMessage: 'Delete a workflow or contact Sales to enable more workflows.',
222
+ })}
223
+ </LimitsModal.Body>
224
+ </LimitsModal.Root>
225
+
226
+ <LimitsModal.Root
227
+ isOpen={showLimitModal === 'stage'}
228
+ onClose={() => setShowLimitModal(false)}
229
+ >
230
+ <LimitsModal.Title>
231
+ {formatMessage({
232
+ id: 'Settings.review-workflows.create.page.stages.limit.title',
233
+ defaultMessage: 'You have reached the limit of stages for this workflow in your plan',
234
+ })}
235
+ </LimitsModal.Title>
236
+
237
+ <LimitsModal.Body>
238
+ {formatMessage({
239
+ id: 'Settings.review-workflows.create.page.stages.limit.body',
240
+ defaultMessage: 'Try deleting some stages or contact Sales to enable more stages.',
241
+ })}
242
+ </LimitsModal.Body>
243
+ </LimitsModal.Root>
244
+ </>
245
+ );
246
+ }
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+
3
+ import { ProtectedPage } from '../../components/ProtectedPage';
4
+
5
+ import { ReviewWorkflowsCreateView } from './CreateView';
6
+
7
+ export default function () {
8
+ return (
9
+ <ProtectedPage>
10
+ <ReviewWorkflowsCreateView />
11
+ </ProtectedPage>
12
+ );
13
+ }