@strapi/review-workflows 5.0.0-beta.3 → 5.0.0-beta.4

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