@strapi/review-workflows 0.0.0-experimental.74c69aeafc770d59d5b3d5d37cd249934ef395ba → 0.0.0-experimental.779667bd163026468f566293decf331a0246fff9

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 (150) hide show
  1. package/LICENSE +12 -17
  2. package/dist/_chunks/Layout-D3DDQmMI.js +252 -0
  3. package/dist/_chunks/Layout-D3DDQmMI.js.map +1 -0
  4. package/dist/_chunks/Layout-Da8jupiA.mjs +234 -0
  5. package/dist/_chunks/Layout-Da8jupiA.mjs.map +1 -0
  6. package/dist/_chunks/_id-CnRDP3Xx.js +1264 -0
  7. package/dist/_chunks/_id-CnRDP3Xx.js.map +1 -0
  8. package/dist/_chunks/_id-xSrgPSJs.mjs +1244 -0
  9. package/dist/_chunks/_id-xSrgPSJs.mjs.map +1 -0
  10. package/dist/_chunks/en-D9ZrQAV6.mjs +12 -0
  11. package/dist/_chunks/en-D9ZrQAV6.mjs.map +1 -0
  12. package/dist/_chunks/en-xcewH2pC.js +12 -0
  13. package/dist/_chunks/en-xcewH2pC.js.map +1 -0
  14. package/dist/_chunks/index-4kp4AkBB.js +253 -0
  15. package/dist/_chunks/index-4kp4AkBB.js.map +1 -0
  16. package/dist/_chunks/index-DDsJlZQw.mjs +801 -0
  17. package/dist/_chunks/index-DDsJlZQw.mjs.map +1 -0
  18. package/dist/_chunks/index-DhPxWpsW.js +819 -0
  19. package/dist/_chunks/index-DhPxWpsW.js.map +1 -0
  20. package/dist/_chunks/index-jfO5GxtW.mjs +234 -0
  21. package/dist/_chunks/index-jfO5GxtW.mjs.map +1 -0
  22. package/dist/_chunks/purchase-review-workflows-BN-5Ube7.mjs +52 -0
  23. package/dist/_chunks/purchase-review-workflows-BN-5Ube7.mjs.map +1 -0
  24. package/dist/_chunks/purchase-review-workflows-DlCDg0fD.js +52 -0
  25. package/dist/_chunks/purchase-review-workflows-DlCDg0fD.js.map +1 -0
  26. package/dist/_chunks/router-DOEULOpS.mjs +24 -0
  27. package/dist/_chunks/router-DOEULOpS.mjs.map +1 -0
  28. package/dist/_chunks/router-DSXpeew4.js +24 -0
  29. package/dist/_chunks/router-DSXpeew4.js.map +1 -0
  30. package/dist/admin/index.js +4 -0
  31. package/dist/admin/index.js.map +1 -0
  32. package/dist/admin/index.mjs +5 -0
  33. package/dist/admin/index.mjs.map +1 -0
  34. package/dist/admin/src/components/LimitsModal.d.ts +13 -0
  35. package/dist/admin/src/constants.d.ts +9 -0
  36. package/dist/admin/src/index.d.ts +3 -0
  37. package/dist/admin/src/modules/hooks.d.ts +7 -0
  38. package/dist/admin/src/router.d.ts +2 -0
  39. package/dist/admin/src/routes/content-manager/[model]/[id]/components/AssigneeSelect.d.ts +2 -0
  40. package/dist/admin/src/routes/content-manager/[model]/[id]/components/Panel.d.ts +3 -0
  41. package/dist/admin/src/routes/content-manager/[model]/[id]/components/StageSelect.d.ts +1 -0
  42. package/dist/admin/src/routes/content-manager/[model]/[id]/components/constants.d.ts +2 -0
  43. package/dist/admin/src/routes/content-manager/[model]/components/AssigneeFilter.d.ts +7 -0
  44. package/dist/admin/src/routes/content-manager/[model]/components/StageFilter.d.ts +8 -0
  45. package/dist/admin/src/routes/content-manager/[model]/components/TableColumns.d.ts +18 -0
  46. package/dist/admin/src/routes/content-manager/[model]/configure/constants.d.ts +7 -0
  47. package/dist/admin/src/routes/content-manager/[model]/constants.d.ts +71 -0
  48. package/dist/admin/src/routes/purchase-review-workflows.d.ts +2 -0
  49. package/dist/admin/src/routes/settings/:id.d.ts +2 -0
  50. package/dist/admin/src/routes/settings/components/AddStage.d.ts +2 -0
  51. package/dist/admin/src/routes/settings/components/Layout.d.ts +12 -0
  52. package/dist/admin/src/routes/settings/components/StageDragPreview.d.ts +6 -0
  53. package/dist/admin/src/routes/settings/components/Stages.d.ts +12 -0
  54. package/dist/admin/src/routes/settings/components/WorkflowAttributes.d.ts +6 -0
  55. package/dist/admin/src/routes/settings/constants.d.ts +2 -0
  56. package/dist/admin/src/routes/settings/hooks/useDragAndDrop.d.ts +51 -0
  57. package/dist/admin/src/routes/settings/hooks/useKeyboardDragAndDrop.d.ts +14 -0
  58. package/dist/admin/src/routes/settings/hooks/useReviewWorkflows.d.ts +25 -0
  59. package/dist/admin/src/routes/settings/index.d.ts +3 -0
  60. package/dist/admin/src/services/admin.d.ts +5 -0
  61. package/dist/admin/src/services/api.d.ts +2 -0
  62. package/dist/admin/src/services/content-manager.d.ts +38 -0
  63. package/dist/admin/src/services/settings.d.ts +13 -0
  64. package/dist/admin/src/utils/api.d.ts +23 -0
  65. package/dist/admin/src/utils/cm-hooks.d.ts +46 -0
  66. package/dist/admin/src/utils/colors.d.ts +9 -0
  67. package/dist/admin/src/utils/translations.d.ts +3 -0
  68. package/dist/admin/src/utils/users.d.ts +6 -0
  69. package/dist/server/index.js +8256 -0
  70. package/dist/server/index.js.map +1 -0
  71. package/dist/server/index.mjs +8257 -0
  72. package/dist/server/index.mjs.map +1 -0
  73. package/dist/server/src/bootstrap.d.ts +3 -0
  74. package/dist/server/src/bootstrap.d.ts.map +1 -0
  75. package/dist/server/src/config/actions.d.ts +19 -0
  76. package/dist/server/src/config/actions.d.ts.map +1 -0
  77. package/dist/server/src/constants/webhook-events.d.ts +6 -0
  78. package/dist/server/src/constants/webhook-events.d.ts.map +1 -0
  79. package/dist/server/src/constants/workflows.d.ts +34 -0
  80. package/dist/server/src/constants/workflows.d.ts.map +1 -0
  81. package/dist/server/src/content-types/index.d.ts +90 -0
  82. package/dist/server/src/content-types/index.d.ts.map +1 -0
  83. package/dist/server/src/content-types/workflow/index.d.ts +41 -0
  84. package/dist/server/src/content-types/workflow/index.d.ts.map +1 -0
  85. package/dist/server/src/content-types/workflow-stage/index.d.ts +49 -0
  86. package/dist/server/src/content-types/workflow-stage/index.d.ts.map +1 -0
  87. package/dist/server/src/controllers/assignees.d.ts +19 -0
  88. package/dist/server/src/controllers/assignees.d.ts.map +1 -0
  89. package/dist/server/src/controllers/index.d.ts +21 -0
  90. package/dist/server/src/controllers/index.d.ts.map +1 -0
  91. package/dist/server/src/controllers/stages.d.ts +39 -0
  92. package/dist/server/src/controllers/stages.d.ts.map +1 -0
  93. package/dist/server/src/controllers/workflows.d.ts +32 -0
  94. package/dist/server/src/controllers/workflows.d.ts.map +1 -0
  95. package/dist/server/src/destroy.d.ts +6 -0
  96. package/dist/server/src/destroy.d.ts.map +1 -0
  97. package/dist/server/src/index.d.ts +322 -0
  98. package/dist/server/src/index.d.ts.map +1 -0
  99. package/dist/server/src/middlewares/review-workflows.d.ts +17 -0
  100. package/dist/server/src/middlewares/review-workflows.d.ts.map +1 -0
  101. package/dist/server/src/migrations/handle-deleted-ct-in-workflows.d.ts +6 -0
  102. package/dist/server/src/migrations/handle-deleted-ct-in-workflows.d.ts.map +1 -0
  103. package/dist/server/src/migrations/multiple-workflows.d.ts +3 -0
  104. package/dist/server/src/migrations/multiple-workflows.d.ts.map +1 -0
  105. package/dist/server/src/migrations/set-stages-default-color.d.ts +6 -0
  106. package/dist/server/src/migrations/set-stages-default-color.d.ts.map +1 -0
  107. package/dist/server/src/migrations/set-stages-roles.d.ts +6 -0
  108. package/dist/server/src/migrations/set-stages-roles.d.ts.map +1 -0
  109. package/dist/server/src/migrations/set-workflow-default-name.d.ts +7 -0
  110. package/dist/server/src/migrations/set-workflow-default-name.d.ts.map +1 -0
  111. package/dist/server/src/migrations/shorten-stage-attribute.d.ts +6 -0
  112. package/dist/server/src/migrations/shorten-stage-attribute.d.ts.map +1 -0
  113. package/dist/server/src/register.d.ts +6 -0
  114. package/dist/server/src/register.d.ts.map +1 -0
  115. package/dist/server/src/routes/index.d.ts +21 -0
  116. package/dist/server/src/routes/index.d.ts.map +1 -0
  117. package/dist/server/src/routes/review-workflows.d.ts +19 -0
  118. package/dist/server/src/routes/review-workflows.d.ts.map +1 -0
  119. package/dist/server/src/routes/utils.d.ts +2 -0
  120. package/dist/server/src/routes/utils.d.ts.map +1 -0
  121. package/dist/server/src/services/assignees.d.ts +13 -0
  122. package/dist/server/src/services/assignees.d.ts.map +1 -0
  123. package/dist/server/src/services/document-service-middleware.d.ts +7 -0
  124. package/dist/server/src/services/document-service-middleware.d.ts.map +1 -0
  125. package/dist/server/src/services/index.d.ts +92 -0
  126. package/dist/server/src/services/index.d.ts.map +1 -0
  127. package/dist/server/src/services/metrics/index.d.ts +21 -0
  128. package/dist/server/src/services/metrics/index.d.ts.map +1 -0
  129. package/dist/server/src/services/metrics/weekly-metrics.d.ts +16 -0
  130. package/dist/server/src/services/metrics/weekly-metrics.d.ts.map +1 -0
  131. package/dist/server/src/services/stage-permissions.d.ts +11 -0
  132. package/dist/server/src/services/stage-permissions.d.ts.map +1 -0
  133. package/dist/server/src/services/stages.d.ts +46 -0
  134. package/dist/server/src/services/stages.d.ts.map +1 -0
  135. package/dist/server/src/services/validation.d.ts +26 -0
  136. package/dist/server/src/services/validation.d.ts.map +1 -0
  137. package/dist/server/src/services/workflow-content-types.d.ts +21 -0
  138. package/dist/server/src/services/workflow-content-types.d.ts.map +1 -0
  139. package/dist/server/src/services/workflows.d.ts +84 -0
  140. package/dist/server/src/services/workflows.d.ts.map +1 -0
  141. package/dist/server/src/utils/index.d.ts +17 -0
  142. package/dist/server/src/utils/index.d.ts.map +1 -0
  143. package/dist/server/src/utils/review-workflows.d.ts +31 -0
  144. package/dist/server/src/utils/review-workflows.d.ts.map +1 -0
  145. package/dist/server/src/validation/review-workflows.d.ts +31 -0
  146. package/dist/server/src/validation/review-workflows.d.ts.map +1 -0
  147. package/dist/shared/contracts/review-workflows.d.ts +152 -0
  148. package/dist/shared/contracts/review-workflows.d.ts.map +1 -0
  149. package/package.json +29 -18
  150. package/strapi-server.js +3 -0
@@ -0,0 +1,1244 @@
1
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { useTracking, useForm, useField, InputRenderer as InputRenderer$1, useNotification, ConfirmDialog, Page, useAPIErrorHandler, useRBAC, Form, BackButton } from "@strapi/admin/strapi-admin";
4
+ import { useLicenseLimits } from "@strapi/admin/strapi-admin/ee";
5
+ import { Box, Typography, Flex, Accordion, MenuItem, Menu, MultiSelectOption, useComposedRefs, VisuallyHidden, IconButton, Grid, Field, SingleSelect, SingleSelectOption, TextInput, MultiSelect, MultiSelectGroup, Dialog, useCollator, Button } from "@strapi/design-system";
6
+ import { PlusCircle, More, Drag, EyeStriked, Duplicate, Check } from "@strapi/icons";
7
+ import { generateNKeysBetween } from "fractional-indexing";
8
+ import { useIntl } from "react-intl";
9
+ import { useDispatch } from "react-redux";
10
+ import { useParams, useNavigate } from "react-router-dom";
11
+ import * as yup from "yup";
12
+ import { r as reviewWorkflowsApi, A as AVAILABLE_COLORS, g as getStageColorByHex, u as useGetContentTypesQuery, a as useTypedSelector, C as CHARGEBEE_WORKFLOW_ENTITLEMENT_NAME, b as CHARGEBEE_STAGES_PER_WORKFLOW_ENTITLEMENT_NAME, L as LimitsModal, i as isBaseQueryError } from "./index-DDsJlZQw.mjs";
13
+ import { D as DRAG_DROP_TYPES, u as useReviewWorkflows, a as DragLayerRendered, H as Header, R as Root } from "./Layout-Da8jupiA.mjs";
14
+ import { getEmptyImage } from "react-dnd-html5-backend";
15
+ import { styled } from "styled-components";
16
+ import { useDrop, useDrag } from "react-dnd";
17
+ const adminApi = reviewWorkflowsApi.injectEndpoints({
18
+ endpoints(builder) {
19
+ return {
20
+ getAdminRoles: builder.query({
21
+ query: () => ({
22
+ url: `/admin/roles`,
23
+ method: "GET"
24
+ }),
25
+ transformResponse: (res) => {
26
+ return res.data;
27
+ }
28
+ })
29
+ };
30
+ }
31
+ });
32
+ const { useGetAdminRolesQuery } = adminApi;
33
+ const useKeyboardDragAndDrop = (active, index, { onCancel, onDropItem, onGrabItem, onMoveItem }) => {
34
+ const [isSelected, setIsSelected] = React.useState(false);
35
+ const handleMove = (movement) => {
36
+ if (!isSelected) {
37
+ return;
38
+ }
39
+ if (typeof index === "number" && onMoveItem) {
40
+ if (movement === "UP") {
41
+ onMoveItem(index - 1, index);
42
+ } else if (movement === "DOWN") {
43
+ onMoveItem(index + 1, index);
44
+ }
45
+ }
46
+ };
47
+ const handleDragClick = () => {
48
+ if (isSelected) {
49
+ if (onDropItem) {
50
+ onDropItem(index);
51
+ }
52
+ setIsSelected(false);
53
+ } else {
54
+ if (onGrabItem) {
55
+ onGrabItem(index);
56
+ }
57
+ setIsSelected(true);
58
+ }
59
+ };
60
+ const handleCancel = () => {
61
+ if (isSelected) {
62
+ setIsSelected(false);
63
+ if (onCancel) {
64
+ onCancel(index);
65
+ }
66
+ }
67
+ };
68
+ const handleKeyDown = (e) => {
69
+ if (!active) {
70
+ return;
71
+ }
72
+ if (e.key === "Tab" && !isSelected) {
73
+ return;
74
+ }
75
+ e.preventDefault();
76
+ switch (e.key) {
77
+ case " ":
78
+ case "Enter":
79
+ handleDragClick();
80
+ break;
81
+ case "Escape":
82
+ handleCancel();
83
+ break;
84
+ case "ArrowDown":
85
+ case "ArrowRight":
86
+ handleMove("DOWN");
87
+ break;
88
+ case "ArrowUp":
89
+ case "ArrowLeft":
90
+ handleMove("UP");
91
+ break;
92
+ }
93
+ };
94
+ return handleKeyDown;
95
+ };
96
+ const DIRECTIONS = {
97
+ UPWARD: "upward",
98
+ DOWNWARD: "downward"
99
+ };
100
+ const DROP_SENSITIVITY = {
101
+ REGULAR: "regular",
102
+ IMMEDIATE: "immediate"
103
+ };
104
+ const useDragAndDrop = (active, {
105
+ type = "STRAPI_DND",
106
+ index,
107
+ item,
108
+ onStart,
109
+ onEnd,
110
+ onGrabItem,
111
+ onDropItem,
112
+ onCancel,
113
+ onMoveItem,
114
+ dropSensitivity = DROP_SENSITIVITY.REGULAR
115
+ }) => {
116
+ const objectRef = React.useRef(null);
117
+ const [{ handlerId, isOver }, dropRef] = useDrop({
118
+ accept: type,
119
+ collect(monitor) {
120
+ return {
121
+ handlerId: monitor.getHandlerId(),
122
+ isOver: monitor.isOver({ shallow: true })
123
+ };
124
+ },
125
+ drop(item2) {
126
+ const draggedIndex = item2.index;
127
+ const newIndex = index;
128
+ if (isOver && onDropItem) {
129
+ onDropItem(draggedIndex, newIndex);
130
+ }
131
+ },
132
+ hover(item2, monitor) {
133
+ if (!objectRef.current || !onMoveItem) {
134
+ return;
135
+ }
136
+ const dragIndex = item2.index;
137
+ const newIndex = index;
138
+ const hoverBoundingRect = objectRef.current?.getBoundingClientRect();
139
+ const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
140
+ const clientOffset = monitor.getClientOffset();
141
+ if (!clientOffset)
142
+ return;
143
+ const hoverClientY = clientOffset && clientOffset.y - hoverBoundingRect.top;
144
+ if (typeof dragIndex === "number" && typeof newIndex === "number") {
145
+ if (dragIndex === newIndex) {
146
+ return;
147
+ }
148
+ if (dropSensitivity === DROP_SENSITIVITY.REGULAR) {
149
+ if (dragIndex < newIndex && hoverClientY < hoverMiddleY) {
150
+ return;
151
+ }
152
+ if (dragIndex > newIndex && hoverClientY > hoverMiddleY) {
153
+ return;
154
+ }
155
+ }
156
+ onMoveItem(newIndex, dragIndex);
157
+ item2.index = newIndex;
158
+ } else {
159
+ if (Array.isArray(dragIndex) && Array.isArray(newIndex)) {
160
+ const minLength = Math.min(dragIndex.length, newIndex.length);
161
+ let areEqual = true;
162
+ let isLessThan = false;
163
+ let isGreaterThan = false;
164
+ for (let i = 0; i < minLength; i++) {
165
+ if (dragIndex[i] < newIndex[i]) {
166
+ isLessThan = true;
167
+ areEqual = false;
168
+ break;
169
+ } else if (dragIndex[i] > newIndex[i]) {
170
+ isGreaterThan = true;
171
+ areEqual = false;
172
+ break;
173
+ }
174
+ }
175
+ if (areEqual && dragIndex.length === newIndex.length) {
176
+ return;
177
+ }
178
+ if (dropSensitivity === DROP_SENSITIVITY.REGULAR) {
179
+ if (isLessThan && !isGreaterThan && hoverClientY < hoverMiddleY) {
180
+ return;
181
+ }
182
+ if (isGreaterThan && !isLessThan && hoverClientY > hoverMiddleY) {
183
+ return;
184
+ }
185
+ }
186
+ }
187
+ onMoveItem(newIndex, dragIndex);
188
+ item2.index = newIndex;
189
+ }
190
+ }
191
+ });
192
+ const getDragDirection = (monitor) => {
193
+ if (monitor && monitor.isDragging() && !monitor.didDrop() && monitor.getInitialClientOffset() && monitor.getClientOffset()) {
194
+ const deltaY = monitor.getInitialClientOffset().y - monitor.getClientOffset().y;
195
+ if (deltaY > 0)
196
+ return DIRECTIONS.UPWARD;
197
+ if (deltaY < 0)
198
+ return DIRECTIONS.DOWNWARD;
199
+ return null;
200
+ }
201
+ return null;
202
+ };
203
+ const [{ isDragging, direction }, dragRef, dragPreviewRef] = useDrag({
204
+ type,
205
+ item() {
206
+ if (onStart) {
207
+ onStart();
208
+ }
209
+ const { width } = objectRef.current?.getBoundingClientRect() ?? {};
210
+ return { index, width, ...item };
211
+ },
212
+ end() {
213
+ if (onEnd) {
214
+ onEnd();
215
+ }
216
+ },
217
+ canDrag: active,
218
+ /**
219
+ * This is useful when the item is in a virtualized list.
220
+ * However, if we don't have an ID then we want the libraries
221
+ * defaults to take care of this.
222
+ */
223
+ isDragging: item?.id ? (monitor) => {
224
+ return item.id === monitor.getItem().id;
225
+ } : void 0,
226
+ collect: (monitor) => ({
227
+ isDragging: monitor.isDragging(),
228
+ initialOffset: monitor.getInitialClientOffset(),
229
+ currentOffset: monitor.getClientOffset(),
230
+ direction: getDragDirection(monitor)
231
+ })
232
+ });
233
+ const handleKeyDown = useKeyboardDragAndDrop(active, index, {
234
+ onGrabItem,
235
+ onDropItem,
236
+ onCancel,
237
+ onMoveItem
238
+ });
239
+ return [
240
+ { handlerId, isDragging, handleKeyDown, isOverDropTarget: isOver, direction },
241
+ objectRef,
242
+ dropRef,
243
+ dragRef,
244
+ dragPreviewRef
245
+ ];
246
+ };
247
+ const AddStage = ({ children, ...props }) => {
248
+ return /* @__PURE__ */ jsx(
249
+ StyledButton,
250
+ {
251
+ tag: "button",
252
+ background: "neutral0",
253
+ borderColor: "neutral150",
254
+ paddingBottom: 3,
255
+ paddingLeft: 4,
256
+ paddingRight: 4,
257
+ paddingTop: 3,
258
+ shadow: "filterShadow",
259
+ ...props,
260
+ children: /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", children: /* @__PURE__ */ jsxs(Flex, { tag: "span", gap: 2, children: [
261
+ /* @__PURE__ */ jsx(PlusCircle, { width: "2.4rem", height: "2.4rem", "aria-hidden": true }),
262
+ children
263
+ ] }) })
264
+ }
265
+ );
266
+ };
267
+ const StyledButton = styled(Box)`
268
+ border-radius: 26px;
269
+ color: ${({ theme }) => theme.colors.neutral500};
270
+
271
+ &:hover {
272
+ color: ${({ theme }) => theme.colors.primary600};
273
+ }
274
+
275
+ &:active {
276
+ color: ${({ theme }) => theme.colors.primary600};
277
+ }
278
+ `;
279
+ const Stages = ({ canDelete = true, canUpdate = true, isCreating }) => {
280
+ const { formatMessage } = useIntl();
281
+ const { trackUsage } = useTracking();
282
+ const addFieldRow = useForm("Stages", (state) => state.addFieldRow);
283
+ const { value: stages = [] } = useField("stages");
284
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 6, width: "100%", children: [
285
+ /* @__PURE__ */ jsxs(Box, { position: "relative", width: "100%", children: [
286
+ /* @__PURE__ */ jsx(
287
+ Background,
288
+ {
289
+ background: "neutral200",
290
+ height: "100%",
291
+ left: "50%",
292
+ position: "absolute",
293
+ top: "0",
294
+ width: 2
295
+ }
296
+ ),
297
+ /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 6, position: "relative", tag: "ol", children: stages.map((stage, index) => {
298
+ return /* @__PURE__ */ jsx(Box, { tag: "li", children: /* @__PURE__ */ jsx(
299
+ Stage,
300
+ {
301
+ index,
302
+ canDelete: stages.length > 1 && canDelete,
303
+ canReorder: stages.length > 1,
304
+ canUpdate,
305
+ stagesCount: stages.length,
306
+ defaultOpen: isCreating,
307
+ ...stage
308
+ }
309
+ ) }, stage.__temp_key__);
310
+ }) })
311
+ ] }),
312
+ canUpdate && /* @__PURE__ */ jsx(
313
+ AddStage,
314
+ {
315
+ type: "button",
316
+ onClick: () => {
317
+ addFieldRow("stages", { name: "" });
318
+ trackUsage("willCreateStage");
319
+ },
320
+ children: formatMessage({
321
+ id: "Settings.review-workflows.stage.add",
322
+ defaultMessage: "Add new stage"
323
+ })
324
+ }
325
+ )
326
+ ] });
327
+ };
328
+ const Background = styled(Box)`
329
+ transform: translateX(-50%);
330
+ `;
331
+ const Stage = ({
332
+ index,
333
+ canDelete = false,
334
+ canReorder = false,
335
+ canUpdate = false,
336
+ stagesCount,
337
+ name,
338
+ permissions,
339
+ color,
340
+ defaultOpen
341
+ }) => {
342
+ const [liveText, setLiveText] = React.useState();
343
+ const { formatMessage } = useIntl();
344
+ const { trackUsage } = useTracking();
345
+ const stageErrors = useForm("Stages", (state) => state.errors.stages);
346
+ const error = stageErrors?.[index];
347
+ const addFieldRow = useForm("Stage", (state) => state.addFieldRow);
348
+ const moveFieldRow = useForm("Stage", (state) => state.moveFieldRow);
349
+ const removeFieldRow = useForm("Stage", (state) => state.removeFieldRow);
350
+ const getItemPos = (index2) => `${index2 + 1} of ${stagesCount}`;
351
+ const handleGrabStage = (index2) => {
352
+ setLiveText(
353
+ formatMessage(
354
+ {
355
+ id: "dnd.grab-item",
356
+ defaultMessage: `{item}, grabbed. Current position in list: {position}. Press up and down arrow to change position, Spacebar to drop, Escape to cancel.`
357
+ },
358
+ {
359
+ item: name,
360
+ position: getItemPos(index2)
361
+ }
362
+ )
363
+ );
364
+ };
365
+ const handleDropStage = (index2) => {
366
+ setLiveText(
367
+ formatMessage(
368
+ {
369
+ id: "dnd.drop-item",
370
+ defaultMessage: `{item}, dropped. Final position in list: {position}.`
371
+ },
372
+ {
373
+ item: name,
374
+ position: getItemPos(index2)
375
+ }
376
+ )
377
+ );
378
+ };
379
+ const handleCancelDragStage = () => {
380
+ setLiveText(
381
+ formatMessage(
382
+ {
383
+ id: "dnd.cancel-item",
384
+ defaultMessage: "{item}, dropped. Re-order cancelled."
385
+ },
386
+ {
387
+ item: name
388
+ }
389
+ )
390
+ );
391
+ };
392
+ const handleMoveStage = (newIndex, oldIndex) => {
393
+ setLiveText(
394
+ formatMessage(
395
+ {
396
+ id: "dnd.reorder",
397
+ defaultMessage: "{item}, moved. New position in list: {position}."
398
+ },
399
+ {
400
+ item: name,
401
+ position: getItemPos(newIndex)
402
+ }
403
+ )
404
+ );
405
+ moveFieldRow("stages", oldIndex, newIndex);
406
+ };
407
+ const [{ handlerId, isDragging, handleKeyDown }, stageRef, dropRef, dragRef, dragPreviewRef] = useDragAndDrop(canReorder, {
408
+ index,
409
+ item: {
410
+ index,
411
+ name
412
+ },
413
+ onGrabItem: handleGrabStage,
414
+ onDropItem: handleDropStage,
415
+ onMoveItem: handleMoveStage,
416
+ onCancel: handleCancelDragStage,
417
+ type: DRAG_DROP_TYPES.STAGE
418
+ });
419
+ const composedRef = useComposedRefs(stageRef, dropRef);
420
+ React.useEffect(() => {
421
+ dragPreviewRef(getEmptyImage(), { captureDraggingState: false });
422
+ }, [dragPreviewRef, index]);
423
+ const handleCloneClick = () => {
424
+ addFieldRow("stages", { name, color, permissions });
425
+ };
426
+ const id = React.useId();
427
+ return /* @__PURE__ */ jsxs(Box, { ref: composedRef, shadow: "tableShadow", children: [
428
+ liveText && /* @__PURE__ */ jsx(VisuallyHidden, { "aria-live": "assertive", children: liveText }),
429
+ isDragging ? /* @__PURE__ */ jsx(
430
+ Box,
431
+ {
432
+ background: "primary100",
433
+ borderStyle: "dashed",
434
+ borderColor: "primary600",
435
+ borderWidth: "1px",
436
+ display: "block",
437
+ hasRadius: true,
438
+ padding: 6
439
+ }
440
+ ) : /* @__PURE__ */ jsx(
441
+ AccordionRoot,
442
+ {
443
+ onValueChange: (value) => {
444
+ if (value) {
445
+ trackUsage("willEditStage");
446
+ }
447
+ },
448
+ defaultValue: defaultOpen ? id : void 0,
449
+ $error: Object.values(error ?? {}).length > 0,
450
+ children: /* @__PURE__ */ jsxs(Accordion.Item, { value: id, children: [
451
+ /* @__PURE__ */ jsxs(Accordion.Header, { children: [
452
+ /* @__PURE__ */ jsx(Accordion.Trigger, { children: name }),
453
+ /* @__PURE__ */ jsx(Accordion.Actions, { children: canDelete || canUpdate ? /* @__PURE__ */ jsxs(Fragment, { children: [
454
+ /* @__PURE__ */ jsxs(Menu.Root, { children: [
455
+ /* @__PURE__ */ jsxs(ContextMenuTrigger, { size: "S", endIcon: null, paddingLeft: 2, paddingRight: 2, children: [
456
+ /* @__PURE__ */ jsx(More, { "aria-hidden": true, focusable: false }),
457
+ /* @__PURE__ */ jsx(VisuallyHidden, { tag: "span", children: formatMessage({
458
+ id: "[tbdb].components.DynamicZone.more-actions",
459
+ defaultMessage: "More actions"
460
+ }) })
461
+ ] }),
462
+ /* @__PURE__ */ jsx(Menu.Content, { popoverPlacement: "bottom-end", zIndex: 2, children: /* @__PURE__ */ jsxs(Menu.SubRoot, { children: [
463
+ canUpdate && /* @__PURE__ */ jsx(MenuItem, { onClick: handleCloneClick, children: formatMessage({
464
+ id: "Settings.review-workflows.stage.delete",
465
+ defaultMessage: "Duplicate stage"
466
+ }) }),
467
+ canDelete && /* @__PURE__ */ jsx(DeleteMenuItem, { onClick: () => removeFieldRow("stages", index), children: formatMessage({
468
+ id: "Settings.review-workflows.stage.delete",
469
+ defaultMessage: "Delete"
470
+ }) })
471
+ ] }) })
472
+ ] }),
473
+ canUpdate && /* @__PURE__ */ jsx(
474
+ IconButton,
475
+ {
476
+ background: "transparent",
477
+ hasRadius: true,
478
+ borderWidth: 0,
479
+ "data-handler-id": handlerId,
480
+ ref: dragRef,
481
+ label: formatMessage({
482
+ id: "Settings.review-workflows.stage.drag",
483
+ defaultMessage: "Drag"
484
+ }),
485
+ onClick: (e) => e.stopPropagation(),
486
+ onKeyDown: handleKeyDown,
487
+ children: /* @__PURE__ */ jsx(Drag, {})
488
+ }
489
+ )
490
+ ] }) : null })
491
+ ] }),
492
+ /* @__PURE__ */ jsx(Accordion.Content, { children: /* @__PURE__ */ jsx(Grid.Root, { gap: 4, padding: 6, children: [
493
+ {
494
+ disabled: !canUpdate,
495
+ label: formatMessage({
496
+ id: "Settings.review-workflows.stage.name.label",
497
+ defaultMessage: "Stage name"
498
+ }),
499
+ name: `stages.${index}.name`,
500
+ required: true,
501
+ size: 6,
502
+ type: "string"
503
+ },
504
+ {
505
+ disabled: !canUpdate,
506
+ label: formatMessage({
507
+ id: "content-manager.reviewWorkflows.stage.color",
508
+ defaultMessage: "Color"
509
+ }),
510
+ name: `stages.${index}.color`,
511
+ required: true,
512
+ size: 6,
513
+ type: "color"
514
+ },
515
+ {
516
+ disabled: !canUpdate,
517
+ label: formatMessage({
518
+ id: "Settings.review-workflows.stage.permissions.label",
519
+ defaultMessage: "Roles that can change this stage"
520
+ }),
521
+ name: `stages.${index}.permissions`,
522
+ placeholder: formatMessage({
523
+ id: "Settings.review-workflows.stage.permissions.placeholder",
524
+ defaultMessage: "Select a role"
525
+ }),
526
+ required: true,
527
+ size: 6,
528
+ type: "permissions"
529
+ }
530
+ ].map(({ size, ...field }) => /* @__PURE__ */ jsx(Grid.Item, { col: size, children: /* @__PURE__ */ jsx(InputRenderer, { ...field }) }, field.name)) }) })
531
+ ] })
532
+ }
533
+ )
534
+ ] });
535
+ };
536
+ const AccordionRoot = styled(Accordion.Root)`
537
+ border: 1px solid
538
+ ${({ theme, $error }) => $error ? theme.colors.danger600 : theme.colors.neutral200};
539
+ `;
540
+ const DeleteMenuItem = styled(MenuItem)`
541
+ color: ${({ theme }) => theme.colors.danger600};
542
+ `;
543
+ const ContextMenuTrigger = styled(Menu.Trigger)`
544
+ :hover,
545
+ :focus {
546
+ background-color: ${({ theme }) => theme.colors.neutral100};
547
+ }
548
+
549
+ > span {
550
+ font-size: 0;
551
+ }
552
+ `;
553
+ const InputRenderer = (props) => {
554
+ switch (props.type) {
555
+ case "color":
556
+ return /* @__PURE__ */ jsx(ColorSelector, { ...props });
557
+ case "permissions":
558
+ return /* @__PURE__ */ jsx(PermissionsField, { ...props });
559
+ default:
560
+ return /* @__PURE__ */ jsx(InputRenderer$1, { ...props });
561
+ }
562
+ };
563
+ const ColorSelector = ({ disabled, label, name, required }) => {
564
+ const { formatMessage } = useIntl();
565
+ const { value, error, onChange } = useField(name);
566
+ const colorOptions = AVAILABLE_COLORS.map(({ hex, name: name2 }) => ({
567
+ value: hex,
568
+ label: formatMessage(
569
+ {
570
+ id: "Settings.review-workflows.stage.color.name",
571
+ defaultMessage: "{name}"
572
+ },
573
+ { name: name2 }
574
+ ),
575
+ color: hex
576
+ }));
577
+ const { themeColorName } = getStageColorByHex(value) ?? {};
578
+ return /* @__PURE__ */ jsxs(Field.Root, { error, name, required, children: [
579
+ /* @__PURE__ */ jsx(Field.Label, { children: label }),
580
+ /* @__PURE__ */ jsx(
581
+ SingleSelect,
582
+ {
583
+ disabled,
584
+ onChange: (v) => {
585
+ onChange(name, v.toString());
586
+ },
587
+ value: value?.toUpperCase(),
588
+ startIcon: /* @__PURE__ */ jsx(
589
+ Flex,
590
+ {
591
+ tag: "span",
592
+ height: 2,
593
+ background: value,
594
+ borderColor: themeColorName === "neutral0" ? "neutral150" : "transparent",
595
+ hasRadius: true,
596
+ shrink: 0,
597
+ width: 2
598
+ }
599
+ ),
600
+ children: colorOptions.map(({ value: value2, label: label2, color }) => {
601
+ const { themeColorName: themeColorName2 } = getStageColorByHex(color) || {};
602
+ return /* @__PURE__ */ jsx(
603
+ SingleSelectOption,
604
+ {
605
+ value: value2,
606
+ startIcon: /* @__PURE__ */ jsx(
607
+ Flex,
608
+ {
609
+ tag: "span",
610
+ height: 2,
611
+ background: color,
612
+ borderColor: themeColorName2 === "neutral0" ? "neutral150" : "transparent",
613
+ hasRadius: true,
614
+ shrink: 0,
615
+ width: 2
616
+ }
617
+ ),
618
+ children: label2
619
+ },
620
+ value2
621
+ );
622
+ })
623
+ }
624
+ ),
625
+ /* @__PURE__ */ jsx(Field.Error, {})
626
+ ] });
627
+ };
628
+ const PermissionsField = ({ disabled, name, placeholder, required }) => {
629
+ const { formatMessage } = useIntl();
630
+ const { toggleNotification } = useNotification();
631
+ const [isApplyAllConfirmationOpen, setIsApplyAllConfirmationOpen] = React.useState(false);
632
+ const { value = [], error, onChange } = useField(name);
633
+ const allStages = useForm("PermissionsField", (state) => state.values.stages);
634
+ const onFormValueChange = useForm("PermissionsField", (state) => state.onChange);
635
+ const rolesErrorCount = React.useRef(0);
636
+ const { data: roles = [], isLoading, error: getRolesError } = useGetAdminRolesQuery();
637
+ const filteredRoles = roles?.filter((role) => role.code !== "strapi-super-admin") ?? [];
638
+ React.useEffect(() => {
639
+ if (!isLoading && getRolesError && "status" in getRolesError && getRolesError.status == 403 && rolesErrorCount.current === 0) {
640
+ rolesErrorCount.current = 1;
641
+ toggleNotification({
642
+ blockTransition: true,
643
+ type: "danger",
644
+ message: formatMessage({
645
+ id: "review-workflows.stage.permissions.noPermissions.description",
646
+ defaultMessage: "You don’t have the permission to see roles. Contact your administrator."
647
+ })
648
+ });
649
+ }
650
+ }, [formatMessage, isLoading, roles, toggleNotification, getRolesError]);
651
+ if (!isLoading && filteredRoles.length === 0) {
652
+ return /* @__PURE__ */ jsxs(
653
+ Field.Root,
654
+ {
655
+ name,
656
+ hint: formatMessage({
657
+ id: "Settings.review-workflows.stage.permissions.noPermissions.description",
658
+ defaultMessage: "You don’t have the permission to see roles"
659
+ }),
660
+ required,
661
+ children: [
662
+ /* @__PURE__ */ jsx(Field.Label, { children: formatMessage({
663
+ id: "Settings.review-workflows.stage.permissions.label",
664
+ defaultMessage: "Roles that can change this stage"
665
+ }) }),
666
+ /* @__PURE__ */ jsx(
667
+ TextInput,
668
+ {
669
+ disabled: true,
670
+ placeholder: formatMessage({
671
+ id: "components.NotAllowedInput.text",
672
+ defaultMessage: "No permissions to see this field"
673
+ }),
674
+ startAction: /* @__PURE__ */ jsx(EyeStriked, { fill: "neutral600" }),
675
+ type: "text",
676
+ value: ""
677
+ }
678
+ ),
679
+ /* @__PURE__ */ jsx(Field.Hint, {})
680
+ ]
681
+ }
682
+ );
683
+ }
684
+ return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(Flex, { alignItems: "flex-end", gap: 3, children: [
685
+ /* @__PURE__ */ jsx(PermissionWrapper, { grow: 1, children: /* @__PURE__ */ jsxs(Field.Root, { error, name, required: true, children: [
686
+ /* @__PURE__ */ jsx(Field.Label, { children: formatMessage({
687
+ id: "Settings.review-workflows.stage.permissions.label",
688
+ defaultMessage: "Roles that can change this stage"
689
+ }) }),
690
+ /* @__PURE__ */ jsx(
691
+ MultiSelect,
692
+ {
693
+ disabled,
694
+ onChange: (values) => {
695
+ const permissions = values.map((value2) => ({
696
+ role: parseInt(value2, 10),
697
+ action: "admin::review-workflows.stage.transition"
698
+ }));
699
+ onChange(name, permissions);
700
+ },
701
+ placeholder,
702
+ value: value.map((permission) => `${permission.role}`),
703
+ withTags: true,
704
+ children: /* @__PURE__ */ jsx(
705
+ MultiSelectGroup,
706
+ {
707
+ label: formatMessage({
708
+ id: "Settings.review-workflows.stage.permissions.allRoles.label",
709
+ defaultMessage: "All roles"
710
+ }),
711
+ values: filteredRoles.map((r) => `${r.id}`),
712
+ children: filteredRoles.map((role) => {
713
+ return /* @__PURE__ */ jsx(NestedOption$1, { value: `${role.id}`, children: role.name }, role.id);
714
+ })
715
+ }
716
+ )
717
+ }
718
+ ),
719
+ /* @__PURE__ */ jsx(Field.Error, {})
720
+ ] }) }),
721
+ /* @__PURE__ */ jsxs(Dialog.Root, { open: isApplyAllConfirmationOpen, onOpenChange: setIsApplyAllConfirmationOpen, children: [
722
+ /* @__PURE__ */ jsx(Dialog.Trigger, { children: /* @__PURE__ */ jsx(
723
+ IconButton,
724
+ {
725
+ disabled,
726
+ label: formatMessage({
727
+ id: "Settings.review-workflows.stage.permissions.apply.label",
728
+ defaultMessage: "Apply to all stages"
729
+ }),
730
+ size: "L",
731
+ variant: "secondary",
732
+ children: /* @__PURE__ */ jsx(Duplicate, {})
733
+ }
734
+ ) }),
735
+ /* @__PURE__ */ jsx(
736
+ ConfirmDialog,
737
+ {
738
+ onConfirm: () => {
739
+ onFormValueChange(
740
+ "stages",
741
+ allStages.map((stage) => ({
742
+ ...stage,
743
+ permissions: value
744
+ }))
745
+ );
746
+ setIsApplyAllConfirmationOpen(false);
747
+ toggleNotification({
748
+ type: "success",
749
+ message: formatMessage({
750
+ id: "Settings.review-workflows.page.edit.confirm.stages.permissions.copy.success",
751
+ defaultMessage: "Applied roles to all other stages of the workflow"
752
+ })
753
+ });
754
+ },
755
+ variant: "default",
756
+ children: formatMessage({
757
+ id: "Settings.review-workflows.page.edit.confirm.stages.permissions.copy",
758
+ defaultMessage: "Roles that can change that stage will be applied to all the other stages."
759
+ })
760
+ }
761
+ )
762
+ ] })
763
+ ] }) });
764
+ };
765
+ const NestedOption$1 = styled(MultiSelectOption)`
766
+ padding-left: ${({ theme }) => theme.spaces[7]};
767
+ `;
768
+ const PermissionWrapper = styled(Flex)`
769
+ > * {
770
+ flex-grow: 1;
771
+ }
772
+ `;
773
+ const WorkflowAttributes = ({ canUpdate = true }) => {
774
+ const { formatMessage } = useIntl();
775
+ return /* @__PURE__ */ jsxs(Grid.Root, { background: "neutral0", hasRadius: true, gap: 4, padding: 6, shadow: "tableShadow", children: [
776
+ /* @__PURE__ */ jsx(Grid.Item, { col: 6, children: /* @__PURE__ */ jsx(
777
+ InputRenderer$1,
778
+ {
779
+ disabled: !canUpdate,
780
+ label: formatMessage({
781
+ id: "Settings.review-workflows.workflow.name.label",
782
+ defaultMessage: "Workflow Name"
783
+ }),
784
+ name: "name",
785
+ required: true,
786
+ type: "string"
787
+ }
788
+ ) }),
789
+ /* @__PURE__ */ jsx(Grid.Item, { col: 6, children: /* @__PURE__ */ jsx(ContentTypesSelector, { disabled: !canUpdate }) })
790
+ ] });
791
+ };
792
+ const ContentTypesSelector = ({ disabled }) => {
793
+ const { formatMessage, locale } = useIntl();
794
+ const { data: contentTypes, isLoading } = useGetContentTypesQuery();
795
+ const { workflows } = useReviewWorkflows();
796
+ const currentWorkflow = useForm("ContentTypesSelector", (state) => state.values);
797
+ const { error, value, onChange } = useField("contentTypes");
798
+ const formatter = useCollator(locale, {
799
+ sensitivity: "base"
800
+ });
801
+ const isDisabled = disabled || isLoading || !contentTypes || contentTypes.collectionType.length === 0 && contentTypes.singleType.length === 0;
802
+ const collectionTypes = (contentTypes?.collectionType ?? []).toSorted((a, b) => formatter.compare(a.info.displayName, b.info.displayName)).map((contentType) => ({
803
+ label: contentType.info.displayName,
804
+ value: contentType.uid
805
+ }));
806
+ const singleTypes = (contentTypes?.singleType ?? []).map((contentType) => ({
807
+ label: contentType.info.displayName,
808
+ value: contentType.uid
809
+ }));
810
+ return /* @__PURE__ */ jsxs(Field.Root, { error, name: "contentTypes", children: [
811
+ /* @__PURE__ */ jsx(Field.Label, { children: formatMessage({
812
+ id: "Settings.review-workflows.workflow.contentTypes.label",
813
+ defaultMessage: "Associated to"
814
+ }) }),
815
+ /* @__PURE__ */ jsx(
816
+ MultiSelect,
817
+ {
818
+ customizeContent: (value2) => formatMessage(
819
+ {
820
+ id: "Settings.review-workflows.workflow.contentTypes.displayValue",
821
+ defaultMessage: "{count} {count, plural, one {content type} other {content types}} selected"
822
+ },
823
+ { count: value2?.length }
824
+ ),
825
+ disabled: isDisabled,
826
+ onChange: (values) => {
827
+ onChange("contentTypes", values);
828
+ },
829
+ value,
830
+ placeholder: formatMessage({
831
+ id: "Settings.review-workflows.workflow.contentTypes.placeholder",
832
+ defaultMessage: "Select"
833
+ }),
834
+ children: [
835
+ ...collectionTypes.length > 0 ? [
836
+ {
837
+ label: formatMessage({
838
+ id: "Settings.review-workflows.workflow.contentTypes.collectionTypes.label",
839
+ defaultMessage: "Collection Types"
840
+ }),
841
+ children: collectionTypes
842
+ }
843
+ ] : [],
844
+ ...singleTypes.length > 0 ? [
845
+ {
846
+ label: formatMessage({
847
+ id: "Settings.review-workflows.workflow.contentTypes.singleTypes.label",
848
+ defaultMessage: "Single Types"
849
+ }),
850
+ children: singleTypes
851
+ }
852
+ ] : []
853
+ ].map((opt) => {
854
+ return /* @__PURE__ */ jsx(
855
+ MultiSelectGroup,
856
+ {
857
+ label: opt.label,
858
+ values: opt.children.map((child) => child.value.toString()),
859
+ children: opt.children.map((child) => {
860
+ const { name: assignedWorkflowName } = workflows?.find(
861
+ (workflow) => (currentWorkflow && workflow.id !== currentWorkflow.id || !currentWorkflow) && workflow.contentTypes.includes(child.value)
862
+ ) ?? {};
863
+ return /* @__PURE__ */ jsx(NestedOption, { value: child.value, children: /* @__PURE__ */ jsx(Typography, {
864
+ // @ts-expect-error - formatMessage options doesn't expect to be a React component but that's what we need actually for the <i> and <em> components
865
+ children: formatMessage(
866
+ {
867
+ id: "Settings.review-workflows.workflow.contentTypes.assigned.notice",
868
+ defaultMessage: "{label} {name, select, undefined {} other {<i>(assigned to <em>{name}</em> workflow)</i>}}"
869
+ },
870
+ {
871
+ label: child.label,
872
+ name: assignedWorkflowName,
873
+ em: (...children) => /* @__PURE__ */ jsx(Typography, { tag: "em", fontWeight: "bold", children }),
874
+ i: (...children) => /* @__PURE__ */ jsx(ContentTypeTakeNotice, { children })
875
+ }
876
+ )
877
+ }) }, child.value);
878
+ })
879
+ },
880
+ opt.label
881
+ );
882
+ })
883
+ }
884
+ )
885
+ ] });
886
+ };
887
+ const NestedOption = styled(MultiSelectOption)`
888
+ padding-left: ${({ theme }) => theme.spaces[7]};
889
+ `;
890
+ const ContentTypeTakeNotice = styled(Typography)`
891
+ font-style: italic;
892
+ `;
893
+ const WORKFLOW_SCHEMA = yup.object({
894
+ contentTypes: yup.array().of(yup.string()),
895
+ name: yup.string().max(255, {
896
+ id: "review-workflows.validation.name.max-length",
897
+ defaultMessage: "Name can not be longer than 255 characters"
898
+ }).required(),
899
+ stages: yup.array().of(
900
+ yup.object().shape({
901
+ name: yup.string().required({
902
+ id: "review-workflows.validation.stage.name",
903
+ defaultMessage: "Name is required"
904
+ }).max(255, {
905
+ id: "review-workflows.validation.stage.max-length",
906
+ defaultMessage: "Name can not be longer than 255 characters"
907
+ }).test(
908
+ "unique-name",
909
+ {
910
+ id: "review-workflows.validation.stage.duplicate",
911
+ defaultMessage: "Stage name must be unique"
912
+ },
913
+ (stageName, context) => {
914
+ const { stages } = context.from[1].value;
915
+ return stages.filter((stage) => stage.name === stageName).length === 1;
916
+ }
917
+ ),
918
+ color: yup.string().required({
919
+ id: "review-workflows.validation.stage.color",
920
+ defaultMessage: "Color is required"
921
+ }).matches(/^#(?:[0-9a-fA-F]{3}){1,2}$/i),
922
+ permissions: yup.array(
923
+ yup.object({
924
+ role: yup.number().strict().typeError({
925
+ id: "review-workflows.validation.stage.permissions.role.number",
926
+ defaultMessage: "Role must be of type number"
927
+ }).required(),
928
+ action: yup.string().required({
929
+ id: "review-workflows.validation.stage.permissions.action.required",
930
+ defaultMessage: "Action is a required argument"
931
+ })
932
+ })
933
+ ).strict()
934
+ })
935
+ ).min(1)
936
+ });
937
+ const EditPage = () => {
938
+ const { id = "" } = useParams();
939
+ const isCreatingWorkflow = id === "create";
940
+ const { formatMessage } = useIntl();
941
+ const { _unstableFormatValidationErrors: formatValidationErrors } = useAPIErrorHandler();
942
+ const navigate = useNavigate();
943
+ const { toggleNotification } = useNotification();
944
+ const dispatch = useDispatch();
945
+ const {
946
+ isLoading: isLoadingWorkflow,
947
+ meta,
948
+ workflows,
949
+ error,
950
+ update,
951
+ create
952
+ } = useReviewWorkflows({ id: isCreatingWorkflow ? void 0 : id });
953
+ const permissions = useTypedSelector(
954
+ (state) => state.admin_app.permissions["settings"]?.["review-workflows"]
955
+ );
956
+ const {
957
+ allowedActions: { canDelete, canUpdate, canCreate }
958
+ } = useRBAC(permissions);
959
+ const [savePrompts, setSavePrompts] = React.useState({});
960
+ const { getFeature, isLoading: isLicenseLoading } = useLicenseLimits();
961
+ const [showLimitModal, setShowLimitModal] = React.useState(null);
962
+ const currentWorkflow = workflows?.find((workflow) => workflow.id === parseInt(id, 10));
963
+ const contentTypesFromOtherWorkflows = workflows?.filter((workflow) => workflow.id !== parseInt(id, 10)).flatMap((workflow) => workflow.contentTypes);
964
+ const limits = getFeature("review-workflows");
965
+ const numberOfWorkflows = limits?.[CHARGEBEE_WORKFLOW_ENTITLEMENT_NAME];
966
+ const stagesPerWorkflow = limits?.[CHARGEBEE_STAGES_PER_WORKFLOW_ENTITLEMENT_NAME];
967
+ const submitForm = async (data, helpers) => {
968
+ try {
969
+ if (!isCreatingWorkflow) {
970
+ const res = await update(id, {
971
+ ...data,
972
+ // compare permissions of stages and only submit them if at least one has
973
+ // changed; this enables partial updates e.g. for users who don't have
974
+ // permissions to see roles
975
+ stages: data.stages.map((stage) => {
976
+ let hasUpdatedPermissions = true;
977
+ const serverStage = currentWorkflow?.stages?.find(
978
+ (serverStage2) => serverStage2.id === stage?.id
979
+ );
980
+ if (serverStage) {
981
+ hasUpdatedPermissions = serverStage.permissions?.length !== stage.permissions?.length || !serverStage.permissions?.every(
982
+ (serverPermission) => !!stage.permissions?.find(
983
+ (permission) => permission.role === serverPermission.role
984
+ )
985
+ );
986
+ }
987
+ return {
988
+ ...stage,
989
+ permissions: hasUpdatedPermissions ? stage.permissions : void 0
990
+ };
991
+ })
992
+ });
993
+ if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
994
+ helpers.setErrors(formatValidationErrors(res.error));
995
+ } else if ("data" in res) {
996
+ for (const uid of res.data.contentTypes) {
997
+ dispatch({
998
+ type: "contentManagerApi/invalidateTags",
999
+ payload: [
1000
+ {
1001
+ type: "ContentTypesConfiguration",
1002
+ id: uid
1003
+ }
1004
+ ]
1005
+ });
1006
+ }
1007
+ }
1008
+ } else {
1009
+ const res = await create(data);
1010
+ if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1011
+ helpers.setErrors(formatValidationErrors(res.error));
1012
+ } else if ("data" in res) {
1013
+ for (const uid of res.data.contentTypes) {
1014
+ dispatch({
1015
+ type: "contentManagerApi/invalidateTags",
1016
+ payload: [
1017
+ {
1018
+ type: "ContentTypesConfiguration",
1019
+ id: uid
1020
+ }
1021
+ ]
1022
+ });
1023
+ }
1024
+ navigate(`../${res.data.id}`);
1025
+ }
1026
+ }
1027
+ } catch (error2) {
1028
+ toggleNotification({
1029
+ type: "danger",
1030
+ message: formatMessage({
1031
+ id: "notification.error",
1032
+ defaultMessage: "An error occurred"
1033
+ })
1034
+ });
1035
+ }
1036
+ setSavePrompts({});
1037
+ };
1038
+ const handleConfirmDeleteDialog = (data, helpers) => async () => {
1039
+ await submitForm(data, helpers);
1040
+ };
1041
+ const handleConfirmClose = () => {
1042
+ setSavePrompts({});
1043
+ };
1044
+ const handleSubmit = async (data, helpers) => {
1045
+ const isContentTypeReassignment = data.contentTypes.some(
1046
+ (contentType) => contentTypesFromOtherWorkflows?.includes(contentType)
1047
+ );
1048
+ const hasDeletedServerStages = !isCreatingWorkflow && !currentWorkflow?.stages.every(
1049
+ (stage) => data.stages.some((newStage) => newStage.id === stage.id)
1050
+ );
1051
+ if (meta && numberOfWorkflows && meta?.workflowCount > parseInt(numberOfWorkflows, 10)) {
1052
+ setShowLimitModal("workflow");
1053
+ } else if (data.stages && stagesPerWorkflow && data.stages.length > parseInt(stagesPerWorkflow, 10)) {
1054
+ setShowLimitModal("stage");
1055
+ } else if (hasDeletedServerStages || isContentTypeReassignment) {
1056
+ if (hasDeletedServerStages) {
1057
+ setSavePrompts((prev) => ({ ...prev, hasDeletedServerStages: true }));
1058
+ }
1059
+ if (isContentTypeReassignment) {
1060
+ setSavePrompts((prev) => ({ ...prev, hasReassignedContentTypes: true }));
1061
+ }
1062
+ } else {
1063
+ await submitForm(data, helpers);
1064
+ }
1065
+ };
1066
+ React.useEffect(() => {
1067
+ if (!isLoadingWorkflow && !isLicenseLoading) {
1068
+ if (meta && numberOfWorkflows && meta?.workflowCount > parseInt(numberOfWorkflows, 10)) {
1069
+ setShowLimitModal("workflow");
1070
+ } else if (currentWorkflow && currentWorkflow.stages && stagesPerWorkflow && currentWorkflow.stages.length > parseInt(stagesPerWorkflow, 10)) {
1071
+ setShowLimitModal("stage");
1072
+ }
1073
+ }
1074
+ }, [
1075
+ currentWorkflow,
1076
+ isLicenseLoading,
1077
+ isLoadingWorkflow,
1078
+ limits,
1079
+ meta,
1080
+ numberOfWorkflows,
1081
+ stagesPerWorkflow
1082
+ ]);
1083
+ const initialValues = React.useMemo(() => {
1084
+ if (isCreatingWorkflow || !currentWorkflow) {
1085
+ return {
1086
+ name: "",
1087
+ stages: [],
1088
+ contentTypes: []
1089
+ };
1090
+ } else {
1091
+ return {
1092
+ name: currentWorkflow.name,
1093
+ stages: addTmpKeysToStages(currentWorkflow.stages),
1094
+ contentTypes: currentWorkflow.contentTypes
1095
+ };
1096
+ }
1097
+ }, [currentWorkflow, isCreatingWorkflow]);
1098
+ if (isLoadingWorkflow) {
1099
+ return /* @__PURE__ */ jsx(Page.Loading, {});
1100
+ }
1101
+ if (error) {
1102
+ return /* @__PURE__ */ jsx(Page.Error, {});
1103
+ }
1104
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1105
+ /* @__PURE__ */ jsx(DragLayerRendered, {}),
1106
+ /* @__PURE__ */ jsx(
1107
+ Form,
1108
+ {
1109
+ method: isCreatingWorkflow ? "POST" : "PUT",
1110
+ initialValues,
1111
+ validationSchema: WORKFLOW_SCHEMA,
1112
+ onSubmit: handleSubmit,
1113
+ children: ({ modified, isSubmitting, values, setErrors }) => /* @__PURE__ */ jsxs(Fragment, { children: [
1114
+ /* @__PURE__ */ jsx(
1115
+ Header,
1116
+ {
1117
+ navigationAction: /* @__PURE__ */ jsx(BackButton, {}),
1118
+ primaryAction: canUpdate || canCreate ? /* @__PURE__ */ jsx(
1119
+ Button,
1120
+ {
1121
+ startIcon: /* @__PURE__ */ jsx(Check, {}),
1122
+ type: "submit",
1123
+ size: "M",
1124
+ disabled: !modified || isSubmitting || values.stages.length === 0,
1125
+ loading: !Boolean(Object.keys(savePrompts).length > 0) && isSubmitting,
1126
+ children: formatMessage({
1127
+ id: "global.save",
1128
+ defaultMessage: "Save"
1129
+ })
1130
+ }
1131
+ ) : null,
1132
+ subtitle: formatMessage(
1133
+ {
1134
+ id: "review-workflows.page.subtitle",
1135
+ defaultMessage: "{count, plural, one {# stage} other {# stages}}"
1136
+ },
1137
+ { count: currentWorkflow?.stages?.length ?? 0 }
1138
+ ),
1139
+ title: currentWorkflow?.name || formatMessage({
1140
+ id: "Settings.review-workflows.create.page.title",
1141
+ defaultMessage: "Create Review Workflow"
1142
+ })
1143
+ }
1144
+ ),
1145
+ /* @__PURE__ */ jsx(Root, { children: /* @__PURE__ */ jsxs(Flex, { alignItems: "stretch", direction: "column", gap: 7, children: [
1146
+ /* @__PURE__ */ jsx(WorkflowAttributes, { canUpdate: canUpdate || canCreate }),
1147
+ /* @__PURE__ */ jsx(
1148
+ Stages,
1149
+ {
1150
+ canDelete,
1151
+ canUpdate: canUpdate || canCreate,
1152
+ isCreating: isCreatingWorkflow
1153
+ }
1154
+ )
1155
+ ] }) }),
1156
+ /* @__PURE__ */ jsx(
1157
+ Dialog.Root,
1158
+ {
1159
+ open: Object.keys(savePrompts).length > 0,
1160
+ onOpenChange: handleConfirmClose,
1161
+ children: /* @__PURE__ */ jsx(ConfirmDialog, { onConfirm: handleConfirmDeleteDialog(values, { setErrors }), children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 5, children: [
1162
+ savePrompts.hasDeletedServerStages && /* @__PURE__ */ jsx(Typography, { textAlign: "center", variant: "omega", children: formatMessage({
1163
+ id: "review-workflows.page.delete.confirm.stages.body",
1164
+ defaultMessage: "All entries assigned to deleted stages will be moved to the previous stage."
1165
+ }) }),
1166
+ savePrompts.hasReassignedContentTypes && /* @__PURE__ */ jsx(Typography, { textAlign: "center", variant: "omega", children: formatMessage(
1167
+ {
1168
+ id: "review-workflows.page.delete.confirm.contentType.body",
1169
+ defaultMessage: "{count} {count, plural, one {content-type} other {content-types}} {count, plural, one {is} other {are}} already mapped to {count, plural, one {another workflow} other {other workflows}}. If you save changes, {count, plural, one {this} other {these}} {count, plural, one {content-type} other {{count} content-types}} will no more be mapped to the {count, plural, one {another workflow} other {other workflows}} and all corresponding information will be removed."
1170
+ },
1171
+ {
1172
+ count: contentTypesFromOtherWorkflows?.filter(
1173
+ (contentType) => currentWorkflow?.contentTypes?.includes(contentType)
1174
+ ).length ?? 0
1175
+ }
1176
+ ) }),
1177
+ /* @__PURE__ */ jsx(Typography, { textAlign: "center", variant: "omega", children: formatMessage({
1178
+ id: "review-workflows.page.delete.confirm.confirm",
1179
+ defaultMessage: "Are you sure you want to save?"
1180
+ }) })
1181
+ ] }) })
1182
+ }
1183
+ )
1184
+ ] })
1185
+ }
1186
+ ),
1187
+ /* @__PURE__ */ jsxs(
1188
+ LimitsModal.Root,
1189
+ {
1190
+ open: showLimitModal === "workflow",
1191
+ onOpenChange: () => setShowLimitModal(null),
1192
+ children: [
1193
+ /* @__PURE__ */ jsx(LimitsModal.Title, { children: formatMessage({
1194
+ id: "review-workflows.edit.page.workflows.limit.title",
1195
+ defaultMessage: "You’ve reached the limit of workflows in your plan"
1196
+ }) }),
1197
+ /* @__PURE__ */ jsx(LimitsModal.Body, { children: formatMessage({
1198
+ id: "review-workflows.edit.page.workflows.limit.body",
1199
+ defaultMessage: "Delete a workflow or contact Sales to enable more workflows."
1200
+ }) })
1201
+ ]
1202
+ }
1203
+ ),
1204
+ /* @__PURE__ */ jsxs(
1205
+ LimitsModal.Root,
1206
+ {
1207
+ open: showLimitModal === "stage",
1208
+ onOpenChange: () => setShowLimitModal(null),
1209
+ children: [
1210
+ /* @__PURE__ */ jsx(LimitsModal.Title, { children: formatMessage({
1211
+ id: "review-workflows.edit.page.stages.limit.title",
1212
+ defaultMessage: "You have reached the limit of stages for this workflow in your plan"
1213
+ }) }),
1214
+ /* @__PURE__ */ jsx(LimitsModal.Body, { children: formatMessage({
1215
+ id: "review-workflows.edit.page.stages.limit.body",
1216
+ defaultMessage: "Try deleting some stages or contact Sales to enable more stages."
1217
+ }) })
1218
+ ]
1219
+ }
1220
+ )
1221
+ ] });
1222
+ };
1223
+ const addTmpKeysToStages = (data) => {
1224
+ const keys = generateNKeysBetween(void 0, void 0, data.length);
1225
+ return data.map((datum, index) => ({
1226
+ ...datum,
1227
+ __temp_key__: keys[index]
1228
+ }));
1229
+ };
1230
+ const ProtectedEditPage = () => {
1231
+ const permissions = useTypedSelector((state) => {
1232
+ const {
1233
+ create = [],
1234
+ update = [],
1235
+ read = []
1236
+ } = state.admin_app.permissions.settings?.["review-workflows"] ?? {};
1237
+ return [...create, ...update, ...read];
1238
+ });
1239
+ return /* @__PURE__ */ jsx(Page.Protect, { permissions, children: /* @__PURE__ */ jsx(EditPage, {}) });
1240
+ };
1241
+ export {
1242
+ ProtectedEditPage
1243
+ };
1244
+ //# sourceMappingURL=_id-xSrgPSJs.mjs.map