@strapi/admin 4.6.1 → 4.9.0-alpha.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 (196) hide show
  1. package/admin/src/assets/images/onboarding-preview.png +0 -0
  2. package/admin/src/content-manager/components/CollectionTypeFormWrapper/index.js +4 -14
  3. package/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/index.js +1 -1
  4. package/admin/src/content-manager/components/DynamicZone/components/DynamicComponent.js +2 -0
  5. package/admin/src/content-manager/components/EditViewDataManagerProvider/reducer.js +39 -9
  6. package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/cleanData.js +9 -15
  7. package/admin/src/content-manager/components/SingleTypeFormWrapper/index.js +4 -12
  8. package/admin/src/content-manager/pages/EditView/DeleteLink/index.js +6 -8
  9. package/admin/src/content-manager/pages/ListView/index.js +6 -11
  10. package/admin/src/index.js +1 -0
  11. package/admin/src/pages/Admin/Onboarding/constants.js +46 -0
  12. package/admin/src/pages/Admin/Onboarding/index.js +161 -89
  13. package/admin/src/pages/Admin/index.js +5 -2
  14. package/admin/src/pages/SettingsPage/components/SettingsNav/index.js +15 -2
  15. package/admin/src/pages/SettingsPage/pages/Users/components/MagicLink/MagicLinkWrapper.js +3 -20
  16. package/admin/src/permissions/defaultPermissions.js +2 -15
  17. package/admin/src/translations/ar.json +4 -4
  18. package/admin/src/translations/ca.json +4 -4
  19. package/admin/src/translations/cs.json +4 -4
  20. package/admin/src/translations/de.json +4 -4
  21. package/admin/src/translations/dk.json +4 -4
  22. package/admin/src/translations/en.json +14 -5
  23. package/admin/src/translations/es.json +4 -4
  24. package/admin/src/translations/eu.json +4 -4
  25. package/admin/src/translations/fr.json +4 -4
  26. package/admin/src/translations/gu.json +4 -4
  27. package/admin/src/translations/he.json +4 -4
  28. package/admin/src/translations/hi.json +4 -4
  29. package/admin/src/translations/hu.json +4 -4
  30. package/admin/src/translations/id.json +4 -4
  31. package/admin/src/translations/it.json +4 -4
  32. package/admin/src/translations/ja.json +4 -4
  33. package/admin/src/translations/ko.json +4 -4
  34. package/admin/src/translations/ml.json +4 -4
  35. package/admin/src/translations/ms.json +4 -4
  36. package/admin/src/translations/nl.json +4 -4
  37. package/admin/src/translations/no.json +4 -4
  38. package/admin/src/translations/pl.json +4 -4
  39. package/admin/src/translations/pt-BR.json +4 -4
  40. package/admin/src/translations/pt.json +4 -4
  41. package/admin/src/translations/ru.json +785 -789
  42. package/admin/src/translations/sa.json +4 -4
  43. package/admin/src/translations/sk.json +4 -4
  44. package/admin/src/translations/sv.json +4 -4
  45. package/admin/src/translations/th.json +4 -4
  46. package/admin/src/translations/tr.json +4 -4
  47. package/admin/src/translations/uk.json +4 -4
  48. package/admin/src/translations/vi.json +4 -4
  49. package/admin/src/translations/zh-Hans.json +4 -4
  50. package/admin/src/translations/zh.json +4 -4
  51. package/build/1683.d59d0f23.chunk.js +268 -0
  52. package/build/19eb2dfcf2603eb55733.png +0 -0
  53. package/build/{4855.bd092921.chunk.js → 2223.1bfea951.chunk.js} +88 -88
  54. package/build/2743.646a1015.chunk.js +45 -0
  55. package/build/3075.3ee481f1.chunk.js +108 -0
  56. package/build/3632.2e378cf8.chunk.js +138 -0
  57. package/build/{4318.f96a9d4d.chunk.js → 4318.cd55ce02.chunk.js} +1 -1
  58. package/build/9707.b36ed71e.chunk.js +96 -0
  59. package/build/Admin-authenticatedApp.a73577e1.chunk.js +79 -0
  60. package/build/{Admin_InternalErrorPage.157152a8.chunk.js → Admin_InternalErrorPage.178ddb90.chunk.js} +1 -1
  61. package/build/{Admin_homePage.b1730882.chunk.js → Admin_homePage.c2f5f27d.chunk.js} +2 -2
  62. package/build/{Admin_marketplace.ea0316c2.chunk.js → Admin_marketplace.1df49c42.chunk.js} +1 -1
  63. package/build/{Admin_pluginsPage.5c24f963.chunk.js → Admin_pluginsPage.8d824408.chunk.js} +2 -2
  64. package/build/{Admin_profilePage.59af1978.chunk.js → Admin_profilePage.cb667bc5.chunk.js} +2 -2
  65. package/build/Admin_settingsPage.f90615fb.chunk.js +178 -0
  66. package/build/{Upload_ConfigureTheView.3f2b6e6a.chunk.js → Upload_ConfigureTheView.d306009d.chunk.js} +1 -1
  67. package/build/admin-app.06f07029.chunk.js +112 -0
  68. package/build/admin-edit-roles-page.35199b9d.chunk.js +1 -0
  69. package/build/admin-edit-users.9e48b00d.chunk.js +10 -0
  70. package/build/admin-users.cf7b4151.chunk.js +11 -0
  71. package/build/{api-tokens-create-page.d248362d.chunk.js → api-tokens-create-page.a31c7fba.chunk.js} +1 -1
  72. package/build/{api-tokens-edit-page.8516fa20.chunk.js → api-tokens-edit-page.64fef287.chunk.js} +1 -1
  73. package/build/{api-tokens-list-page.44a79fda.chunk.js → api-tokens-list-page.e600ad3e.chunk.js} +2 -2
  74. package/build/ar-json.39e54aba.chunk.js +1 -0
  75. package/build/{audit-logs-settings-page.c3dce30d.chunk.js → audit-logs-settings-page.d4da4579.chunk.js} +1 -1
  76. package/build/{ca-json.f6a0f472.chunk.js → ca-json.4d999055.chunk.js} +1 -1
  77. package/build/content-manager.255c3a59.chunk.js +1139 -0
  78. package/build/{content-type-builder-list-view.79e84b36.chunk.js → content-type-builder-list-view.8d7a3d68.chunk.js} +5 -5
  79. package/build/content-type-builder.3c8558a5.chunk.js +126 -0
  80. package/build/cs-json.4b44411c.chunk.js +1 -0
  81. package/build/{de-json.30e1f35b.chunk.js → de-json.866f8a28.chunk.js} +1 -1
  82. package/build/{dk-json.e6d9ffa4.chunk.js → dk-json.10f7b1d1.chunk.js} +1 -1
  83. package/build/email-settings-page.b19f2eb2.chunk.js +10 -0
  84. package/build/en-json.1997583c.chunk.js +1 -0
  85. package/build/es-json.ea15c957.chunk.js +1 -0
  86. package/build/{eu-json.fceecd8b.chunk.js → eu-json.3bc24d60.chunk.js} +1 -1
  87. package/build/{fr-json.78545ef8.chunk.js → fr-json.e88fbdfd.chunk.js} +1 -1
  88. package/build/{gu-json.676518f2.chunk.js → gu-json.94f0d242.chunk.js} +1 -1
  89. package/build/{he-json.ad22e8cc.chunk.js → he-json.f0de8cdb.chunk.js} +1 -1
  90. package/build/{hi-json.19b51c09.chunk.js → hi-json.df3a7be2.chunk.js} +1 -1
  91. package/build/{hu-json.f947088f.chunk.js → hu-json.680e6eef.chunk.js} +1 -1
  92. package/build/{i18n-settings-page.b8d8753e.chunk.js → i18n-settings-page.a6b49eac.chunk.js} +1 -1
  93. package/build/{id-json.504daa84.chunk.js → id-json.e0d83d41.chunk.js} +1 -1
  94. package/build/index.html +1 -1
  95. package/build/{it-json.2fd90f4d.chunk.js → it-json.8be59205.chunk.js} +1 -1
  96. package/build/{ja-json.c9f12d0b.chunk.js → ja-json.97ee41ba.chunk.js} +1 -1
  97. package/build/{ko-json.ef463065.chunk.js → ko-json.4cbbf4f2.chunk.js} +1 -1
  98. package/build/main.7f308c20.js +4322 -0
  99. package/build/{ml-json.490f666c.chunk.js → ml-json.e3747091.chunk.js} +1 -1
  100. package/build/ms-json.0eddffd9.chunk.js +1 -0
  101. package/build/{nl-json.c416295a.chunk.js → nl-json.371a15ee.chunk.js} +1 -1
  102. package/build/{no-json.1a2258ba.chunk.js → no-json.9b3cd181.chunk.js} +1 -1
  103. package/build/{pl-json.8cf0c871.chunk.js → pl-json.e535cbce.chunk.js} +1 -1
  104. package/build/{pt-BR-json.51fab8d0.chunk.js → pt-BR-json.e5fafa46.chunk.js} +1 -1
  105. package/build/pt-json.ee554a41.chunk.js +1 -0
  106. package/build/review-workflows-settings.7b4be1b0.chunk.js +63 -0
  107. package/build/{ru-json.aa5cd123.chunk.js → ru-json.866f0ff1.chunk.js} +1 -1
  108. package/build/runtime~main.bf374148.js +2 -0
  109. package/build/{sa-json.f3fa5407.chunk.js → sa-json.7efeb257.chunk.js} +1 -1
  110. package/build/{sk-json.9ec60d9f.chunk.js → sk-json.7bbeb0af.chunk.js} +1 -1
  111. package/build/{sso-settings-page.b85ad080.chunk.js → sso-settings-page.ad2143dd.chunk.js} +1 -1
  112. package/build/{sv-json.c6b0c237.chunk.js → sv-json.dc40951f.chunk.js} +1 -1
  113. package/build/{th-json.6e68155c.chunk.js → th-json.f664b96d.chunk.js} +1 -1
  114. package/build/{tr-json.9f41dc08.chunk.js → tr-json.b79eae31.chunk.js} +1 -1
  115. package/build/uk-json.b7e38370.chunk.js +1 -0
  116. package/build/upload-settings.eb1a7908.chunk.js +84 -0
  117. package/build/upload.700e2c84.chunk.js +33 -0
  118. package/build/{users-advanced-settings-page.fce9908e.chunk.js → users-advanced-settings-page.aae212f2.chunk.js} +1 -1
  119. package/build/{users-email-settings-page.343d0ad2.chunk.js → users-email-settings-page.8a9b0da1.chunk.js} +1 -1
  120. package/build/{users-providers-settings-page.e5a9a3f1.chunk.js → users-providers-settings-page.e6be909d.chunk.js} +10 -10
  121. package/build/{users-roles-settings-page.66312f31.chunk.js → users-roles-settings-page.97d06a80.chunk.js} +3 -3
  122. package/build/vi-json.ee4c5537.chunk.js +1 -0
  123. package/build/webhook-edit-page.9eb0f789.chunk.js +75 -0
  124. package/build/webhook-list-page.66082323.chunk.js +42 -0
  125. package/build/{zh-Hans-json.9c0eac99.chunk.js → zh-Hans-json.30a18940.chunk.js} +1 -1
  126. package/build/{zh-json.f88f563d.chunk.js → zh-json.49d84433.chunk.js} +1 -1
  127. package/ee/admin/hooks/useSettingsMenu/utils/customAdminLinks.js +12 -12
  128. package/ee/admin/hooks/useSettingsMenu/utils/customGlobalLinks.js +21 -13
  129. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/ReviewWorkflows.js +137 -0
  130. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/actions/index.js +42 -0
  131. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/AddStage/AddStage.js +87 -0
  132. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/AddStage/index.js +1 -0
  133. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/Stage/Stage.js +90 -0
  134. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/Stage/index.js +1 -0
  135. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/Stages.js +77 -0
  136. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/index.js +1 -0
  137. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/constants.js +6 -0
  138. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/hooks/useReviewWorkflows.js +63 -0
  139. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/index.js +3 -0
  140. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/reducer/index.js +102 -0
  141. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/utils/getWorkflowValidationSchema.js +25 -0
  142. package/ee/admin/pages/SettingsPage/utils/customRoutes.js +16 -2
  143. package/ee/admin/permissions/customPermissions.js +7 -0
  144. package/ee/server/bootstrap.js +7 -1
  145. package/ee/server/config/admin-actions.js +10 -0
  146. package/ee/server/constants/default-stages.json +14 -0
  147. package/ee/server/constants/default-workflow.json +1 -0
  148. package/ee/server/constants/workflows.js +7 -0
  149. package/ee/server/content-types/index.js +9 -0
  150. package/ee/server/content-types/workflow/index.js +34 -0
  151. package/ee/server/content-types/workflow-stage/index.js +36 -0
  152. package/ee/server/controllers/index.js +2 -0
  153. package/ee/server/controllers/workflows/index.js +36 -0
  154. package/ee/server/controllers/workflows/stages/index.js +57 -0
  155. package/ee/server/index.js +1 -0
  156. package/ee/server/routes/index.js +87 -0
  157. package/ee/server/services/index.js +3 -0
  158. package/ee/server/services/review-workflows/review-workflows.js +54 -0
  159. package/ee/server/services/review-workflows/stages.js +133 -0
  160. package/ee/server/services/review-workflows/workflows.js +25 -0
  161. package/ee/server/utils/index.js +8 -0
  162. package/ee/server/utils/test.js +11 -0
  163. package/ee/server/validation/review-workflows.js +17 -0
  164. package/package.json +15 -15
  165. package/server/config/admin-actions.js +0 -16
  166. package/server/controllers/admin.js +0 -55
  167. package/server/routes/admin.js +0 -28
  168. package/webpack.config.js +0 -3
  169. package/build/1683.c8aa7b7c.chunk.js +0 -268
  170. package/build/2743.6d1632f9.chunk.js +0 -45
  171. package/build/3075.dc3894fe.chunk.js +0 -108
  172. package/build/3632.0317b618.chunk.js +0 -138
  173. package/build/9707.7290fd92.chunk.js +0 -96
  174. package/build/Admin-authenticatedApp.ce646f66.chunk.js +0 -75
  175. package/build/Admin_settingsPage.d1493824.chunk.js +0 -178
  176. package/build/admin-app.25934eaa.chunk.js +0 -112
  177. package/build/admin-edit-roles-page.446b69dc.chunk.js +0 -1
  178. package/build/admin-edit-users.2ed69bfd.chunk.js +0 -10
  179. package/build/admin-users.fc003b10.chunk.js +0 -11
  180. package/build/ar-json.932794f7.chunk.js +0 -1
  181. package/build/content-manager.35ff9726.chunk.js +0 -1139
  182. package/build/content-type-builder.855db321.chunk.js +0 -126
  183. package/build/cs-json.79879fb6.chunk.js +0 -1
  184. package/build/email-settings-page.d1fcc7a3.chunk.js +0 -10
  185. package/build/en-json.1f137a90.chunk.js +0 -1
  186. package/build/es-json.e275481d.chunk.js +0 -1
  187. package/build/main.7b151630.js +0 -4377
  188. package/build/ms-json.db87d8d3.chunk.js +0 -1
  189. package/build/pt-json.62927d1e.chunk.js +0 -1
  190. package/build/runtime~main.a20d633b.js +0 -2
  191. package/build/uk-json.b2fcd567.chunk.js +0 -1
  192. package/build/upload-settings.ef64bbf9.chunk.js +0 -84
  193. package/build/upload.c5730dfa.chunk.js +0 -33
  194. package/build/vi-json.f08d7d03.chunk.js +0 -1
  195. package/build/webhook-edit-page.73e51e64.chunk.js +0 -75
  196. package/build/webhook-list-page.1134f130.chunk.js +0 -42
@@ -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 styled from 'styled-components';
4
+ import { useField } from 'formik';
5
+ import { useIntl } from 'react-intl';
6
+ import { useDispatch } from 'react-redux';
7
+ import {
8
+ Accordion,
9
+ AccordionToggle,
10
+ AccordionContent,
11
+ Box,
12
+ Grid,
13
+ GridItem,
14
+ IconButton,
15
+ TextInput,
16
+ } from '@strapi/design-system';
17
+
18
+ import { Trash } from '@strapi/icons';
19
+
20
+ import { deleteStage, updateStage } from '../../../actions';
21
+
22
+ // TODO: Delete once https://github.com/strapi/design-system/pull/858
23
+ // is merged and released.
24
+ const StyledAccordion = styled(Box)`
25
+ > div:first-child {
26
+ box-shadow: ${({ theme }) => theme.shadows.tableShadow};
27
+ }
28
+ `;
29
+
30
+ // TODO: Keep an eye on https://github.com/strapi/design-system/pull/878
31
+ const DeleteButton = styled(IconButton)`
32
+ background-color: transparent;
33
+ `;
34
+
35
+ function Stage({ id, name, index, canDelete, isOpen: isOpenDefault = false }) {
36
+ const { formatMessage } = useIntl();
37
+ const [isOpen, setIsOpen] = useState(isOpenDefault);
38
+ const fieldIdentifier = `stages.${index}.name`;
39
+ const [field, meta] = useField(fieldIdentifier);
40
+ const dispatch = useDispatch();
41
+
42
+ return (
43
+ <StyledAccordion>
44
+ <Accordion size="S" variant="primary" onToggle={() => setIsOpen(!isOpen)} expanded={isOpen}>
45
+ <AccordionToggle
46
+ title={name}
47
+ togglePosition="left"
48
+ action={
49
+ canDelete ? (
50
+ <DeleteButton
51
+ noBorder
52
+ onClick={() => dispatch(deleteStage(id))}
53
+ label={formatMessage({
54
+ id: 'Settings.review-workflows.stage.delete',
55
+ defaultMessage: 'Delete stage',
56
+ })}
57
+ icon={<Trash />}
58
+ />
59
+ ) : null
60
+ }
61
+ />
62
+ <AccordionContent padding={6} background="neutral0">
63
+ <Grid gap={4}>
64
+ <GridItem col={6}>
65
+ <TextInput
66
+ {...field}
67
+ id={fieldIdentifier}
68
+ value={name}
69
+ label={formatMessage({
70
+ id: 'Settings.review-workflows.stage.name.label',
71
+ defaultMessage: 'Stage name',
72
+ })}
73
+ error={meta.error ?? false}
74
+ onBlur={(event) => dispatch(updateStage(id, { name: event.target.value }))}
75
+ />
76
+ </GridItem>
77
+ </Grid>
78
+ </AccordionContent>
79
+ </Accordion>
80
+ </StyledAccordion>
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,77 @@
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, Stack } from '@strapi/design-system';
7
+
8
+ import { addStage } from '../../actions';
9
+ import { AddStage } from '../AddStage';
10
+ import { Stage } from './Stage';
11
+
12
+ const StagesContainer = styled(Box)`
13
+ position: relative;
14
+ `;
15
+
16
+ const Background = styled(Box)`
17
+ left: 50%;
18
+ position: absolute;
19
+ top: 0;
20
+ transform: translateX(-50%);
21
+ `;
22
+
23
+ function Stages({ stages }) {
24
+ const { formatMessage } = useIntl();
25
+ const dispatch = useDispatch();
26
+
27
+ return (
28
+ <Flex direction="column" gap={6} width="100%">
29
+ <StagesContainer spacing={4} width="100%">
30
+ <Background background="neutral200" height="100%" width={2} zIndex={1} />
31
+
32
+ <Stack spacing={6} zIndex={2} position="relative" as="ol">
33
+ {stages.map((stage, index) => {
34
+ const id = stage?.id ?? stage.__temp_key__;
35
+
36
+ return (
37
+ <Box key={`stage-${id}`} as="li">
38
+ <Stage
39
+ {...stage}
40
+ id={id}
41
+ index={index}
42
+ canDelete={stages.length > 1}
43
+ isOpen={!stage.id}
44
+ />
45
+ </Box>
46
+ );
47
+ })}
48
+ </Stack>
49
+ </StagesContainer>
50
+
51
+ <Flex spacing={6}>
52
+ <AddStage type="button" onClick={() => dispatch(addStage({ name: '' }))}>
53
+ {formatMessage({
54
+ id: 'Settings.review-workflows.stage.add',
55
+ defaultMessage: 'Add new stage',
56
+ })}
57
+ </AddStage>
58
+ </Flex>
59
+ </Flex>
60
+ );
61
+ }
62
+
63
+ export { Stages };
64
+
65
+ Stages.defaultProps = {
66
+ stages: [],
67
+ };
68
+
69
+ Stages.propTypes = {
70
+ stages: PropTypes.arrayOf(
71
+ PropTypes.shape({
72
+ id: PropTypes.number,
73
+ __temp_key__: PropTypes.number,
74
+ name: PropTypes.string.isRequired,
75
+ })
76
+ ),
77
+ };
@@ -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`;
@@ -0,0 +1,63 @@
1
+ import { useQuery, useMutation, useQueryClient } from 'react-query';
2
+ import { useFetchClient, useNotification } from '@strapi/helper-plugin';
3
+
4
+ const QUERY_BASE_KEY = 'review-workflows';
5
+ const API_BASE_URL = '/admin/review-workflows';
6
+
7
+ export function useReviewWorkflows(workflowId) {
8
+ const { get, put } = useFetchClient();
9
+ const toggleNotification = useNotification();
10
+ const client = useQueryClient();
11
+ const workflowQueryKey = [QUERY_BASE_KEY, workflowId ?? 'default'];
12
+
13
+ async function fetchWorkflows({ params = { populate: 'stages' } }) {
14
+ const {
15
+ data: { data },
16
+ } = await get(`${API_BASE_URL}/workflows/${workflowId ?? ''}`, { params });
17
+
18
+ return data;
19
+ }
20
+
21
+ async function updateRemoteWorkflowStages({ workflowId, stages }) {
22
+ const {
23
+ data: { data },
24
+ } = await put(`${API_BASE_URL}/workflows/${workflowId}/stages`, {
25
+ data: stages,
26
+ });
27
+
28
+ return data;
29
+ }
30
+
31
+ function updateWorkflowStages(workflowId, stages) {
32
+ return workflowUpdateMutation.mutateAsync({ workflowId, stages });
33
+ }
34
+
35
+ function refetchWorkflow() {
36
+ client.refetchQueries(workflowQueryKey);
37
+ }
38
+
39
+ const workflows = useQuery(workflowQueryKey, fetchWorkflows);
40
+
41
+ const workflowUpdateMutation = useMutation(updateRemoteWorkflowStages, {
42
+ async onError() {
43
+ // TODO: this should return the proper error thrown by the API
44
+ toggleNotification({
45
+ type: 'warning',
46
+ message: { id: 'notification.error', defaultMessage: 'An error occured' },
47
+ });
48
+ },
49
+
50
+ async onSuccess() {
51
+ toggleNotification({
52
+ type: 'success',
53
+ message: { id: 'notification.success.saved', defaultMessage: 'Saved' },
54
+ });
55
+ },
56
+ });
57
+
58
+ return {
59
+ workflows,
60
+ updateWorkflowStages,
61
+ refetchWorkflow,
62
+ };
63
+ }
@@ -0,0 +1,3 @@
1
+ import { ReviewWorkflowsPage } from './ReviewWorkflows';
2
+
3
+ export default ReviewWorkflowsPage;
@@ -0,0 +1,102 @@
1
+ import { current, produce } from 'immer';
2
+ import isEqual from 'lodash/isEqual';
3
+
4
+ import {
5
+ ACTION_SET_WORKFLOWS,
6
+ ACTION_DELETE_STAGE,
7
+ ACTION_ADD_STAGE,
8
+ ACTION_UPDATE_STAGE,
9
+ } from '../constants';
10
+
11
+ export const initialState = {
12
+ status: 'loading',
13
+ serverState: {
14
+ currentWorkflow: null,
15
+ workflows: [],
16
+ },
17
+ clientState: {
18
+ currentWorkflow: { data: null, isDirty: false, hasDeletedServerStages: false },
19
+ },
20
+ };
21
+
22
+ export function reducer(state = initialState, action) {
23
+ return produce(state, (draft) => {
24
+ const { payload } = action;
25
+
26
+ switch (action.type) {
27
+ case ACTION_SET_WORKFLOWS: {
28
+ const { status, workflows } = payload;
29
+
30
+ draft.status = status;
31
+
32
+ if (workflows) {
33
+ const defaultWorkflow = workflows[0];
34
+
35
+ draft.serverState.workflows = workflows;
36
+ draft.serverState.currentWorkflow = defaultWorkflow;
37
+ draft.clientState.currentWorkflow.data = defaultWorkflow;
38
+ }
39
+ break;
40
+ }
41
+
42
+ case ACTION_DELETE_STAGE: {
43
+ const { stageId } = payload;
44
+ const { currentWorkflow } = state.clientState;
45
+
46
+ draft.clientState.currentWorkflow.data.stages = currentWorkflow.data.stages.filter(
47
+ (stage) => (stage?.id ?? stage.__temp_key__) !== stageId
48
+ );
49
+
50
+ if (!currentWorkflow.hasDeletedServerStages) {
51
+ draft.clientState.currentWorkflow.hasDeletedServerStages =
52
+ !!state.serverState.currentWorkflow.stages.find((stage) => stage.id === stageId);
53
+ }
54
+
55
+ break;
56
+ }
57
+
58
+ case ACTION_ADD_STAGE: {
59
+ const { currentWorkflow } = state.clientState;
60
+
61
+ if (!currentWorkflow.data) {
62
+ draft.clientState.currentWorkflow.data = {
63
+ stages: [],
64
+ };
65
+ }
66
+
67
+ draft.clientState.currentWorkflow.data.stages.push({
68
+ ...payload,
69
+ __temp_key__: (currentWorkflow.data?.stages?.length ?? 0) + 1,
70
+ });
71
+
72
+ break;
73
+ }
74
+
75
+ case ACTION_UPDATE_STAGE: {
76
+ const { currentWorkflow } = state.clientState;
77
+ const { stageId, ...modified } = payload;
78
+
79
+ draft.clientState.currentWorkflow.data.stages = currentWorkflow.data.stages.map((stage) =>
80
+ (stage.id ?? stage.__temp_key__) === stageId
81
+ ? {
82
+ ...stage,
83
+ ...modified,
84
+ }
85
+ : stage
86
+ );
87
+
88
+ break;
89
+ }
90
+
91
+ default:
92
+ break;
93
+ }
94
+
95
+ if (state.clientState.currentWorkflow.data) {
96
+ draft.clientState.currentWorkflow.isDirty = !isEqual(
97
+ current(draft.clientState.currentWorkflow).data,
98
+ state.serverState.currentWorkflow
99
+ );
100
+ }
101
+ });
102
+ }
@@ -0,0 +1,25 @@
1
+ import * as yup from 'yup';
2
+
3
+ export function getWorkflowValidationSchema({ formatMessage }) {
4
+ return yup.object({
5
+ stages: yup.array().of(
6
+ yup.object().shape({
7
+ name: yup
8
+ .string()
9
+ .required(
10
+ formatMessage({
11
+ id: 'Settings.review-workflows.validation.stage.name',
12
+ defaultMessage: 'Name is required',
13
+ })
14
+ )
15
+ .max(
16
+ 255,
17
+ formatMessage({
18
+ id: 'Settings.review-workflows.validation.stage.max-length',
19
+ defaultMessage: 'Name can not be longer than 255 characters',
20
+ })
21
+ ),
22
+ })
23
+ ),
24
+ });
25
+ }
@@ -1,6 +1,6 @@
1
1
  const routes = [];
2
2
 
3
- if (strapi.features.isEnabled(strapi.features.SSO)) {
3
+ if (window.strapi.features.isEnabled(window.strapi.features.SSO)) {
4
4
  routes.push({
5
5
  async Component() {
6
6
  const component = await import(
@@ -14,7 +14,21 @@ if (strapi.features.isEnabled(strapi.features.SSO)) {
14
14
  });
15
15
  }
16
16
 
17
- if (strapi.features.isEnabled(strapi.features.AUDIT_LOGS)) {
17
+ if (window.strapi.isEE) {
18
+ routes.push({
19
+ async Component() {
20
+ const component = await import(
21
+ /* webpackChunkName: "review-workflows-settings" */ '../pages/ReviewWorkflows'
22
+ );
23
+
24
+ return component;
25
+ },
26
+ to: '/settings/review-workflows',
27
+ exact: true,
28
+ });
29
+ }
30
+
31
+ if (window.strapi.features.isEnabled(strapi.features.AUDIT_LOGS)) {
18
32
  routes.push({
19
33
  async Component() {
20
34
  const component = await import(
@@ -1,5 +1,12 @@
1
1
  const customPermissions = {
2
2
  settings: {
3
+ auditLogs: {
4
+ main: [{ action: 'admin::audit-logs.read', subject: null }],
5
+ read: [{ action: 'admin::audit-logs.read', subject: null }],
6
+ },
7
+ 'review-workflows': {
8
+ main: [{ action: 'admin::review-workflows.read', subject: null }],
9
+ },
3
10
  sso: {
4
11
  main: [{ action: 'admin::provider-login.read', subject: null }],
5
12
  read: [{ action: 'admin::provider-login.read', subject: null }],
@@ -17,7 +17,13 @@ module.exports = async () => {
17
17
  await actionProvider.registerMany(actions.auditLogs);
18
18
  }
19
19
 
20
- // TODO: check admin seats
20
+ if (features.isEnabled('review-workflows')) {
21
+ const { bootstrap: rwBootstrap } = getService('review-workflows');
22
+
23
+ await rwBootstrap();
24
+ await actionProvider.registerMany(actions.reviewWorkflows);
25
+ }
21
26
 
27
+ // TODO: check admin seats
22
28
  await executeCEBootstrap();
23
29
  };
@@ -29,4 +29,14 @@ module.exports = {
29
29
  subCategory: 'options',
30
30
  },
31
31
  ],
32
+ reviewWorkflows: [
33
+ {
34
+ uid: 'review-workflows.read',
35
+ displayName: 'Read',
36
+ pluginName: 'admin',
37
+ section: 'settings',
38
+ category: 'review workflows',
39
+ subCategory: 'options',
40
+ },
41
+ ],
32
42
  };
@@ -0,0 +1,14 @@
1
+ [
2
+ {
3
+ "name": "To do"
4
+ },
5
+ {
6
+ "name": "Ready to review"
7
+ },
8
+ {
9
+ "name": "In progress"
10
+ },
11
+ {
12
+ "name": "Reviewed"
13
+ }
14
+ ]
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ // TODO concatenate admin + content type singular name
4
+ module.exports = {
5
+ WORKFLOW_MODEL_UID: 'admin::workflow',
6
+ STAGE_MODEL_UID: 'admin::workflow-stage',
7
+ };
@@ -0,0 +1,9 @@
1
+ 'use strict';
2
+
3
+ const workflow = require('./workflow');
4
+ const workflowStage = require('./workflow-stage');
5
+
6
+ module.exports = {
7
+ workflow,
8
+ 'workflow-stage': workflowStage,
9
+ };
@@ -0,0 +1,34 @@
1
+ 'use strict';
2
+
3
+ module.exports = {
4
+ schema: {
5
+ collectionName: 'strapi_workflows',
6
+ info: {
7
+ name: 'Workflow',
8
+ description: '',
9
+ singularName: 'workflow',
10
+ pluralName: 'workflows',
11
+ displayName: 'Workflow',
12
+ },
13
+ options: {},
14
+ pluginOptions: {
15
+ 'content-manager': {
16
+ visible: false,
17
+ },
18
+ 'content-type-builder': {
19
+ visible: false,
20
+ },
21
+ },
22
+ attributes: {
23
+ uid: {
24
+ type: 'string',
25
+ },
26
+ stages: {
27
+ type: 'relation',
28
+ target: 'admin::workflow-stage',
29
+ relation: 'oneToMany',
30
+ mappedBy: 'workflow',
31
+ },
32
+ },
33
+ },
34
+ };