@strapi/review-workflows 5.9.0 → 5.10.1

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