@strapi/admin 4.13.6 → 4.14.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/admin/src/content-manager/components/Wysiwyg/EditorLayout.js +1 -1
- package/admin/src/content-manager/pages/ListView/components/BulkActionButtons/ConfirmBulkActionDialog/index.js +3 -0
- package/admin/src/content-manager/pages/ListView/components/BulkActionButtons/SelectedEntriesModal/index.js +2 -1
- package/admin/src/hooks/useAdminRoles/index.js +17 -7
- package/admin/src/hooks/useAdminUsers/useAdminUsers.js +16 -7
- package/admin/src/hooks/useContentTypes/useContentTypes.js +18 -7
- package/admin/src/index.js +1 -7
- package/build/1227.4f48119b.chunk.js +1 -0
- package/build/{3483.19381b40.chunk.js → 3483.f6b2439f.chunk.js} +1 -1
- package/build/4174.924ebd4c.chunk.js +1 -0
- package/build/6266.c652bdb1.chunk.js +146 -0
- package/build/7897.cf22d5fe.chunk.js +6 -0
- package/build/{Admin-authenticatedApp.69c7ea72.chunk.js → Admin-authenticatedApp.a687d9c6.chunk.js} +1 -1
- package/build/{admin-app.1fde8f7a.chunk.js → admin-app.4654dc77.chunk.js} +11 -11
- package/build/admin-edit-roles-page.6597d934.chunk.js +267 -0
- package/build/admin-edit-users.3014605e.chunk.js +10 -0
- package/build/admin-roles-list.ab6fcfb7.chunk.js +22 -0
- package/build/admin-users.81bf5f4d.chunk.js +11 -0
- package/build/audit-logs-settings-page.4eb6cdf8.chunk.js +1 -0
- package/build/{content-manager.f9abb63f.chunk.js → content-manager.9187db78.chunk.js} +37 -37
- package/build/index.html +1 -1
- package/build/{main.9ed36326.js → main.da000219.js} +17 -17
- package/build/review-workflows-settings-create-view.5d8806b2.chunk.js +1 -0
- package/build/review-workflows-settings-edit-view.634903ed.chunk.js +1 -0
- package/build/review-workflows-settings-list-view.d138c3b5.chunk.js +56 -0
- package/build/{runtime~main.562cc892.js → runtime~main.9589b498.js} +2 -2
- package/build/sso-settings-page.caa35f7b.chunk.js +1 -0
- package/ee/admin/content-manager/pages/EditView/InformationBox/components/StageSelect/StageSelect.js +65 -53
- package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/actions/index.js +50 -5
- package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/Stage/Stage.js +257 -21
- package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/WorkflowAttributes/WorkflowAttributes.js +8 -23
- package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/constants.js +6 -0
- package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/hooks/useReviewWorkflows.js +17 -7
- package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/hooks/useReviewWorkflowsStages.js +36 -0
- package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/CreateView/CreateView.js +68 -19
- package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/EditView/EditView.js +105 -35
- package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/reducer/index.js +68 -27
- package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/selectors.js +45 -0
- package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/utils/validateWorkflow.js +20 -0
- package/ee/server/config/admin-actions.js +6 -0
- package/ee/server/constants/workflows.js +13 -0
- package/ee/server/content-types/workflow-stage/index.js +6 -0
- package/ee/server/controllers/workflows/index.js +41 -16
- package/ee/server/controllers/workflows/stages/index.js +93 -6
- package/ee/server/migrations/review-workflows-stages-roles.js +68 -0
- package/ee/server/migrations/review-workflows-workflow-name.js +7 -0
- package/ee/server/register.js +2 -0
- package/ee/server/routes/review-workflows.js +10 -9
- package/ee/server/services/index.js +1 -0
- package/ee/server/services/review-workflows/stage-permissions.js +60 -0
- package/ee/server/services/review-workflows/stages.js +91 -12
- package/ee/server/services/review-workflows/workflows/index.js +20 -7
- package/ee/server/validation/review-workflows.js +11 -0
- package/package.json +8 -8
- package/server/content-types/Permission.js +6 -0
- package/server/domain/permission/index.js +11 -2
- package/server/services/role.js +12 -4
- package/server/validation/action-provider.js +1 -1
- package/server/validation/common-validators.js +92 -100
- package/server/validation/permission.js +0 -3
- package/build/1227.c72e74e9.chunk.js +0 -1
- package/build/2237.a7992513.chunk.js +0 -114
- package/build/4174.6efb0dc6.chunk.js +0 -1
- package/build/4724.a0ce68f3.chunk.js +0 -6
- package/build/admin-edit-roles-page.f76cb0aa.chunk.js +0 -267
- package/build/admin-edit-users.186c9d90.chunk.js +0 -10
- package/build/admin-roles-list.3a4edfa2.chunk.js +0 -22
- package/build/admin-users.e276bd9a.chunk.js +0 -11
- package/build/audit-logs-settings-page.0d9bc0f6.chunk.js +0 -1
- package/build/review-workflows-settings-create-view.b96b7be1.chunk.js +0 -1
- package/build/review-workflows-settings-edit-view.21a682a0.chunk.js +0 -1
- package/build/review-workflows-settings-list-view.14212324.chunk.js +0 -56
- package/build/sso-settings-page.d93bd81f.chunk.js +0 -1
|
@@ -9,25 +9,91 @@ import {
|
|
|
9
9
|
Grid,
|
|
10
10
|
GridItem,
|
|
11
11
|
IconButton,
|
|
12
|
+
MultiSelect,
|
|
13
|
+
MultiSelectGroup,
|
|
14
|
+
MultiSelectOption,
|
|
12
15
|
SingleSelect,
|
|
13
16
|
SingleSelectOption,
|
|
14
17
|
TextInput,
|
|
18
|
+
Typography,
|
|
15
19
|
VisuallyHidden,
|
|
16
20
|
} from '@strapi/design-system';
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
21
|
+
import { Menu, MenuItem } from '@strapi/design-system/v2';
|
|
22
|
+
import {
|
|
23
|
+
ConfirmDialog,
|
|
24
|
+
useNotification,
|
|
25
|
+
NotAllowedInput,
|
|
26
|
+
useTracking,
|
|
27
|
+
} from '@strapi/helper-plugin';
|
|
28
|
+
import { Duplicate, Drag, More } from '@strapi/icons';
|
|
19
29
|
import { useField } from 'formik';
|
|
20
30
|
import PropTypes from 'prop-types';
|
|
21
31
|
import { getEmptyImage } from 'react-dnd-html5-backend';
|
|
22
32
|
import { useIntl } from 'react-intl';
|
|
23
|
-
import { useDispatch } from 'react-redux';
|
|
33
|
+
import { useDispatch, useSelector } from 'react-redux';
|
|
34
|
+
import styled from 'styled-components';
|
|
24
35
|
|
|
25
36
|
import { useDragAndDrop } from '../../../../../../../../../admin/src/content-manager/hooks';
|
|
26
37
|
import { composeRefs } from '../../../../../../../../../admin/src/content-manager/utils';
|
|
27
|
-
import {
|
|
38
|
+
import {
|
|
39
|
+
cloneStage,
|
|
40
|
+
deleteStage,
|
|
41
|
+
updateStage,
|
|
42
|
+
updateStagePosition,
|
|
43
|
+
updateStages,
|
|
44
|
+
} from '../../../actions';
|
|
28
45
|
import { DRAG_DROP_TYPES } from '../../../constants';
|
|
46
|
+
import { selectRoles } from '../../../selectors';
|
|
29
47
|
import { getAvailableStageColors, getStageColorByHex } from '../../../utils/colors';
|
|
30
48
|
|
|
49
|
+
const NestedOption = styled(MultiSelectOption)`
|
|
50
|
+
padding-left: ${({ theme }) => theme.spaces[7]};
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
// Grow the size of the permission Select
|
|
54
|
+
const PermissionWrapper = styled(Flex)`
|
|
55
|
+
> * {
|
|
56
|
+
flex-grow: 1;
|
|
57
|
+
}
|
|
58
|
+
`;
|
|
59
|
+
|
|
60
|
+
const DeleteMenuItem = styled(MenuItem)`
|
|
61
|
+
color: ${({ theme }) => theme.colors.danger600};
|
|
62
|
+
`;
|
|
63
|
+
|
|
64
|
+
// Removing the font-size from the child-span aligns the
|
|
65
|
+
// more icon vertically
|
|
66
|
+
const ContextMenuTrigger = styled(Menu.Trigger)`
|
|
67
|
+
:hover,
|
|
68
|
+
:focus {
|
|
69
|
+
background-color: ${({ theme }) => theme.colors.neutral100};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
> span {
|
|
73
|
+
font-size: 0;
|
|
74
|
+
}
|
|
75
|
+
`;
|
|
76
|
+
|
|
77
|
+
// As soon as either `as` or `forwardedAs` is set, the component
|
|
78
|
+
// resets some styles and e.g. the `hasBorder` prop no longer works,
|
|
79
|
+
// which is why this bit of CSS has been added manually ¯\_(ツ)_/¯
|
|
80
|
+
const DragIconButton = styled(IconButton)`
|
|
81
|
+
align-items: center;
|
|
82
|
+
border-radius: ${({ theme }) => theme.borderRadius};
|
|
83
|
+
display: flex;
|
|
84
|
+
justify-content: center;
|
|
85
|
+
|
|
86
|
+
:hover,
|
|
87
|
+
:focus {
|
|
88
|
+
background-color: ${({ theme }) => theme.colors.neutral100};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
svg {
|
|
92
|
+
height: auto;
|
|
93
|
+
width: ${({ theme }) => theme.spaces[3]}};
|
|
94
|
+
}
|
|
95
|
+
`;
|
|
96
|
+
|
|
31
97
|
const AVAILABLE_COLORS = getAvailableStageColors();
|
|
32
98
|
|
|
33
99
|
function StageDropPreview() {
|
|
@@ -137,13 +203,23 @@ export function Stage({
|
|
|
137
203
|
dispatch(updateStagePosition(oldIndex, newIndex));
|
|
138
204
|
};
|
|
139
205
|
|
|
206
|
+
const handleApplyPermissionsToAllStages = () => {
|
|
207
|
+
setIsApplyAllConfirmationOpen(true);
|
|
208
|
+
};
|
|
209
|
+
|
|
140
210
|
const [liveText, setLiveText] = React.useState(null);
|
|
141
211
|
const { formatMessage } = useIntl();
|
|
142
212
|
const { trackUsage } = useTracking();
|
|
143
213
|
const dispatch = useDispatch();
|
|
214
|
+
const toggleNotification = useNotification();
|
|
144
215
|
const [isOpen, setIsOpen] = React.useState(isOpenDefault);
|
|
216
|
+
const [isApplyAllConfirmationOpen, setIsApplyAllConfirmationOpen] = React.useState(false);
|
|
145
217
|
const [nameField, nameMeta, nameHelper] = useField(`stages.${index}.name`);
|
|
146
218
|
const [colorField, colorMeta, colorHelper] = useField(`stages.${index}.color`);
|
|
219
|
+
const [permissionsField, permissionsMeta, permissionsHelper] = useField(
|
|
220
|
+
`stages.${index}.permissions`
|
|
221
|
+
);
|
|
222
|
+
const roles = useSelector(selectRoles);
|
|
147
223
|
const [{ handlerId, isDragging, handleKeyDown }, stageRef, dropRef, dragRef, dragPreviewRef] =
|
|
148
224
|
useDragAndDrop(canReorder, {
|
|
149
225
|
index,
|
|
@@ -171,12 +247,17 @@ export function Stage({
|
|
|
171
247
|
color: hex,
|
|
172
248
|
}));
|
|
173
249
|
|
|
250
|
+
const { themeColorName } = getStageColorByHex(colorField.value) ?? {};
|
|
251
|
+
|
|
252
|
+
const filteredRoles = roles
|
|
253
|
+
// Super admins always have permissions to do everything and therefore
|
|
254
|
+
// there is no point for this role to show up in the role combobox
|
|
255
|
+
.filter((role) => role.code !== 'strapi-super-admin');
|
|
256
|
+
|
|
174
257
|
React.useEffect(() => {
|
|
175
258
|
dragPreviewRef(getEmptyImage(), { captureDraggingState: false });
|
|
176
259
|
}, [dragPreviewRef, index]);
|
|
177
260
|
|
|
178
|
-
const { themeColorName } = getStageColorByHex(colorField.value) ?? {};
|
|
179
|
-
|
|
180
261
|
return (
|
|
181
262
|
<Box ref={composedRef}>
|
|
182
263
|
{liveText && <VisuallyHidden aria-live="assertive">{liveText}</VisuallyHidden>}
|
|
@@ -196,7 +277,7 @@ export function Stage({
|
|
|
196
277
|
}}
|
|
197
278
|
expanded={isOpen}
|
|
198
279
|
shadow="tableShadow"
|
|
199
|
-
error={nameMeta.error ?? colorMeta?.error ?? false}
|
|
280
|
+
error={nameMeta.error ?? colorMeta?.error ?? permissionsMeta?.error ?? false}
|
|
200
281
|
hasErrorMessage={false}
|
|
201
282
|
>
|
|
202
283
|
<AccordionToggle
|
|
@@ -205,23 +286,47 @@ export function Stage({
|
|
|
205
286
|
action={
|
|
206
287
|
(canDelete || canUpdate) && (
|
|
207
288
|
<Flex>
|
|
208
|
-
|
|
209
|
-
<
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
289
|
+
<Menu.Root>
|
|
290
|
+
<ContextMenuTrigger size="S" endIcon={null} paddingLeft={2} paddingRight={2}>
|
|
291
|
+
<More aria-hidden focusable={false} />
|
|
292
|
+
<VisuallyHidden as="span">
|
|
293
|
+
{formatMessage({
|
|
294
|
+
id: '[tbdb].components.DynamicZone.more-actions',
|
|
295
|
+
defaultMessage: 'More actions',
|
|
296
|
+
})}
|
|
297
|
+
</VisuallyHidden>
|
|
298
|
+
</ContextMenuTrigger>
|
|
299
|
+
{/* z-index needs to be as big as the one defined for the wrapper in Stages, otherwise the menu
|
|
300
|
+
* disappears behind the accordion
|
|
301
|
+
*/}
|
|
302
|
+
<Menu.Content popoverPlacement="bottom-end" zIndex={2}>
|
|
303
|
+
<Menu.SubRoot>
|
|
304
|
+
{canUpdate && (
|
|
305
|
+
<MenuItem onClick={() => dispatch(cloneStage(id))}>
|
|
306
|
+
{formatMessage({
|
|
307
|
+
id: 'Settings.review-workflows.stage.delete',
|
|
308
|
+
defaultMessage: 'Duplicate stage',
|
|
309
|
+
})}
|
|
310
|
+
</MenuItem>
|
|
311
|
+
)}
|
|
312
|
+
|
|
313
|
+
{canDelete && (
|
|
314
|
+
<DeleteMenuItem onClick={() => dispatch(deleteStage(id))}>
|
|
315
|
+
{formatMessage({
|
|
316
|
+
id: 'Settings.review-workflows.stage.delete',
|
|
317
|
+
defaultMessage: 'Delete',
|
|
318
|
+
})}
|
|
319
|
+
</DeleteMenuItem>
|
|
320
|
+
)}
|
|
321
|
+
</Menu.SubRoot>
|
|
322
|
+
</Menu.Content>
|
|
323
|
+
</Menu.Root>
|
|
220
324
|
|
|
221
325
|
{canUpdate && (
|
|
222
|
-
<
|
|
326
|
+
<DragIconButton
|
|
223
327
|
background="transparent"
|
|
224
328
|
forwardedAs="div"
|
|
329
|
+
hasRadius
|
|
225
330
|
role="button"
|
|
226
331
|
noBorder
|
|
227
332
|
tabIndex={0}
|
|
@@ -235,7 +340,7 @@ export function Stage({
|
|
|
235
340
|
onKeyDown={handleKeyDown}
|
|
236
341
|
>
|
|
237
342
|
<Drag />
|
|
238
|
-
</
|
|
343
|
+
</DragIconButton>
|
|
239
344
|
)}
|
|
240
345
|
</Flex>
|
|
241
346
|
)
|
|
@@ -315,10 +420,141 @@ export function Stage({
|
|
|
315
420
|
})}
|
|
316
421
|
</SingleSelect>
|
|
317
422
|
</GridItem>
|
|
423
|
+
|
|
424
|
+
<GridItem col={6}>
|
|
425
|
+
{filteredRoles.length === 0 ? (
|
|
426
|
+
<NotAllowedInput
|
|
427
|
+
description={{
|
|
428
|
+
id: 'Settings.review-workflows.stage.permissions.noPermissions.description',
|
|
429
|
+
defaultMessage: 'You don’t have the permission to see roles',
|
|
430
|
+
}}
|
|
431
|
+
intlLabel={{
|
|
432
|
+
id: 'Settings.review-workflows.stage.permissions.label',
|
|
433
|
+
defaultMessage: 'Roles that can change this stage',
|
|
434
|
+
}}
|
|
435
|
+
name={permissionsField.name}
|
|
436
|
+
/>
|
|
437
|
+
) : (
|
|
438
|
+
<Flex alignItems="flex-end" gap={3}>
|
|
439
|
+
<PermissionWrapper grow={1}>
|
|
440
|
+
<MultiSelect
|
|
441
|
+
{...permissionsField}
|
|
442
|
+
disabled={!canUpdate}
|
|
443
|
+
error={permissionsMeta.error ?? false}
|
|
444
|
+
id={permissionsField.name}
|
|
445
|
+
label={formatMessage({
|
|
446
|
+
id: 'Settings.review-workflows.stage.permissions.label',
|
|
447
|
+
defaultMessage: 'Roles that can change this stage',
|
|
448
|
+
})}
|
|
449
|
+
onChange={(values) => {
|
|
450
|
+
// Because the select components expects strings for values, but
|
|
451
|
+
// the yup schema validates we are sending full permission objects to the API,
|
|
452
|
+
// we must coerce the string value back to an object
|
|
453
|
+
const permissions = values.map((value) => ({
|
|
454
|
+
role: parseInt(value, 10),
|
|
455
|
+
action: 'admin::review-workflows.stage.transition',
|
|
456
|
+
}));
|
|
457
|
+
|
|
458
|
+
permissionsHelper.setValue(permissions);
|
|
459
|
+
dispatch(updateStage(id, { permissions }));
|
|
460
|
+
}}
|
|
461
|
+
placeholder={formatMessage({
|
|
462
|
+
id: 'Settings.review-workflows.stage.permissions.placeholder',
|
|
463
|
+
defaultMessage: 'Select a role',
|
|
464
|
+
})}
|
|
465
|
+
required
|
|
466
|
+
// The Select component expects strings for values
|
|
467
|
+
value={(permissionsField.value ?? []).map(
|
|
468
|
+
(permission) => `${permission.role}`
|
|
469
|
+
)}
|
|
470
|
+
withTags
|
|
471
|
+
>
|
|
472
|
+
{[
|
|
473
|
+
{
|
|
474
|
+
label: formatMessage({
|
|
475
|
+
id: 'Settings.review-workflows.stage.permissions.allRoles.label',
|
|
476
|
+
defaultMessage: 'All roles',
|
|
477
|
+
}),
|
|
478
|
+
|
|
479
|
+
children: filteredRoles.map((role) => ({
|
|
480
|
+
value: `${role.id}`,
|
|
481
|
+
label: role.name,
|
|
482
|
+
})),
|
|
483
|
+
},
|
|
484
|
+
].map((role) => {
|
|
485
|
+
if ('children' in role) {
|
|
486
|
+
return (
|
|
487
|
+
<MultiSelectGroup
|
|
488
|
+
key={role.label}
|
|
489
|
+
label={role.label}
|
|
490
|
+
values={role.children.map((child) => child.value)}
|
|
491
|
+
>
|
|
492
|
+
{role.children.map((role) => {
|
|
493
|
+
return (
|
|
494
|
+
<NestedOption key={role.value} value={role.value}>
|
|
495
|
+
{role.label}
|
|
496
|
+
</NestedOption>
|
|
497
|
+
);
|
|
498
|
+
})}
|
|
499
|
+
</MultiSelectGroup>
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
return (
|
|
504
|
+
<MultiSelectOption key={role.value} value={role.value}>
|
|
505
|
+
{role.label}
|
|
506
|
+
</MultiSelectOption>
|
|
507
|
+
);
|
|
508
|
+
})}
|
|
509
|
+
</MultiSelect>
|
|
510
|
+
</PermissionWrapper>
|
|
511
|
+
|
|
512
|
+
<IconButton
|
|
513
|
+
disabled={!canUpdate}
|
|
514
|
+
icon={<Duplicate />}
|
|
515
|
+
label={formatMessage({
|
|
516
|
+
id: 'Settings.review-workflows.stage.permissions.apply.label',
|
|
517
|
+
defaultMessage: 'Apply to all stages',
|
|
518
|
+
})}
|
|
519
|
+
size="L"
|
|
520
|
+
variant="secondary"
|
|
521
|
+
onClick={() => handleApplyPermissionsToAllStages(permissionsField.value)}
|
|
522
|
+
/>
|
|
523
|
+
</Flex>
|
|
524
|
+
)}
|
|
525
|
+
</GridItem>
|
|
318
526
|
</Grid>
|
|
319
527
|
</AccordionContent>
|
|
320
528
|
</Accordion>
|
|
321
529
|
)}
|
|
530
|
+
|
|
531
|
+
<ConfirmDialog.Root
|
|
532
|
+
iconRightButton={null}
|
|
533
|
+
isOpen={isApplyAllConfirmationOpen}
|
|
534
|
+
onToggleDialog={() => setIsApplyAllConfirmationOpen(false)}
|
|
535
|
+
onConfirm={() => {
|
|
536
|
+
dispatch(updateStages({ permissions: permissionsField.value }));
|
|
537
|
+
setIsApplyAllConfirmationOpen(false);
|
|
538
|
+
toggleNotification({
|
|
539
|
+
type: 'success',
|
|
540
|
+
message: formatMessage({
|
|
541
|
+
id: 'Settings.review-workflows.page.edit.confirm.stages.permissions.copy.success',
|
|
542
|
+
defaultMessage: 'Applied roles to all other stages of the workflow',
|
|
543
|
+
}),
|
|
544
|
+
});
|
|
545
|
+
}}
|
|
546
|
+
variantRightButton="primary"
|
|
547
|
+
>
|
|
548
|
+
<ConfirmDialog.Body>
|
|
549
|
+
<Typography textAlign="center" variant="omega">
|
|
550
|
+
{formatMessage({
|
|
551
|
+
id: 'Settings.review-workflows.page.edit.confirm.stages.permissions.copy',
|
|
552
|
+
defaultMessage:
|
|
553
|
+
'Roles that can change that stage will be applied to all the other stages.',
|
|
554
|
+
})}
|
|
555
|
+
</Typography>
|
|
556
|
+
</ConfirmDialog.Body>
|
|
557
|
+
</ConfirmDialog.Root>
|
|
322
558
|
</Box>
|
|
323
559
|
);
|
|
324
560
|
}
|
|
@@ -13,10 +13,11 @@ import { useCollator } from '@strapi/helper-plugin';
|
|
|
13
13
|
import { useField } from 'formik';
|
|
14
14
|
import PropTypes from 'prop-types';
|
|
15
15
|
import { useIntl } from 'react-intl';
|
|
16
|
-
import { useDispatch } from 'react-redux';
|
|
16
|
+
import { useDispatch, useSelector } from 'react-redux';
|
|
17
17
|
import styled from 'styled-components';
|
|
18
18
|
|
|
19
19
|
import { updateWorkflow } from '../../actions';
|
|
20
|
+
import { selectContentTypes, selectCurrentWorkflow, selectWorkflows } from '../../selectors';
|
|
20
21
|
|
|
21
22
|
const NestedOption = styled(MultiSelectOption)`
|
|
22
23
|
padding-left: ${({ theme }) => theme.spaces[7]};
|
|
@@ -26,14 +27,12 @@ const ContentTypeTakeNotice = styled(Typography)`
|
|
|
26
27
|
font-style: italic;
|
|
27
28
|
`;
|
|
28
29
|
|
|
29
|
-
export function WorkflowAttributes({
|
|
30
|
-
canUpdate,
|
|
31
|
-
contentTypes: { collectionTypes, singleTypes },
|
|
32
|
-
currentWorkflow,
|
|
33
|
-
workflows,
|
|
34
|
-
}) {
|
|
30
|
+
export function WorkflowAttributes({ canUpdate }) {
|
|
35
31
|
const { formatMessage, locale } = useIntl();
|
|
36
32
|
const dispatch = useDispatch();
|
|
33
|
+
const { collectionTypes, singleTypes } = useSelector(selectContentTypes);
|
|
34
|
+
const currentWorkflow = useSelector(selectCurrentWorkflow);
|
|
35
|
+
const workflows = useSelector(selectWorkflows);
|
|
37
36
|
const [nameField, nameMeta, nameHelper] = useField('name');
|
|
38
37
|
const [contentTypesField, contentTypesMeta, contentTypesHelper] = useField('contentTypes');
|
|
39
38
|
const formatter = useCollator(locale, {
|
|
@@ -97,7 +96,7 @@ export function WorkflowAttributes({
|
|
|
97
96
|
id: 'Settings.review-workflows.workflow.contentTypes.collectionTypes.label',
|
|
98
97
|
defaultMessage: 'Collection Types',
|
|
99
98
|
}),
|
|
100
|
-
children: collectionTypes
|
|
99
|
+
children: [...collectionTypes]
|
|
101
100
|
.sort((a, b) => formatter.compare(a.info.displayName, b.info.displayName))
|
|
102
101
|
.map((contentType) => ({
|
|
103
102
|
label: contentType.info.displayName,
|
|
@@ -114,7 +113,7 @@ export function WorkflowAttributes({
|
|
|
114
113
|
id: 'Settings.review-workflows.workflow.contentTypes.singleTypes.label',
|
|
115
114
|
defaultMessage: 'Single Types',
|
|
116
115
|
}),
|
|
117
|
-
children: singleTypes.map((contentType) => ({
|
|
116
|
+
children: [...singleTypes].map((contentType) => ({
|
|
118
117
|
label: contentType.info.displayName,
|
|
119
118
|
value: contentType.uid,
|
|
120
119
|
})),
|
|
@@ -178,24 +177,10 @@ export function WorkflowAttributes({
|
|
|
178
177
|
);
|
|
179
178
|
}
|
|
180
179
|
|
|
181
|
-
const ContentTypeType = PropTypes.shape({
|
|
182
|
-
uid: PropTypes.string.isRequired,
|
|
183
|
-
info: PropTypes.shape({
|
|
184
|
-
displayName: PropTypes.string.isRequired,
|
|
185
|
-
}).isRequired,
|
|
186
|
-
});
|
|
187
|
-
|
|
188
180
|
WorkflowAttributes.defaultProps = {
|
|
189
181
|
canUpdate: true,
|
|
190
|
-
currentWorkflow: undefined,
|
|
191
182
|
};
|
|
192
183
|
|
|
193
184
|
WorkflowAttributes.propTypes = {
|
|
194
185
|
canUpdate: PropTypes.bool,
|
|
195
|
-
contentTypes: PropTypes.shape({
|
|
196
|
-
collectionTypes: PropTypes.arrayOf(ContentTypeType).isRequired,
|
|
197
|
-
singleTypes: PropTypes.arrayOf(ContentTypeType).isRequired,
|
|
198
|
-
}).isRequired,
|
|
199
|
-
currentWorkflow: PropTypes.object,
|
|
200
|
-
workflows: PropTypes.array.isRequired,
|
|
201
186
|
};
|
|
@@ -3,10 +3,16 @@ import { lightTheme } from '@strapi/design-system';
|
|
|
3
3
|
export const REDUX_NAMESPACE = 'settings_review-workflows';
|
|
4
4
|
|
|
5
5
|
export const ACTION_RESET_WORKFLOW = `Settings/Review_Workflows/RESET_WORKFLOW`;
|
|
6
|
+
export const ACTION_SET_CONTENT_TYPES = `Settings/Review_Workflows/SET_CONTENT_TYPES`;
|
|
7
|
+
export const ACTION_SET_IS_LOADING = `Settings/Review_Workflows/SET_IS_LOADING`;
|
|
8
|
+
export const ACTION_SET_ROLES = `Settings/Review_Workflows/SET_ROLES`;
|
|
6
9
|
export const ACTION_SET_WORKFLOW = `Settings/Review_Workflows/SET_WORKFLOW`;
|
|
10
|
+
export const ACTION_SET_WORKFLOWS = `Settings/Review_Workflows/SET_WORKFLOWS`;
|
|
7
11
|
export const ACTION_DELETE_STAGE = `Settings/Review_Workflows/WORKFLOW_DELETE_STAGE`;
|
|
8
12
|
export const ACTION_ADD_STAGE = `Settings/Review_Workflows/WORKFLOW_ADD_STAGE`;
|
|
13
|
+
export const ACTION_CLONE_STAGE = `Settings/Review_Workflows/WORKFLOW_CLONE_STAGE`;
|
|
9
14
|
export const ACTION_UPDATE_STAGE = `Settings/Review_Workflows/WORKFLOW_UPDATE_STAGE`;
|
|
15
|
+
export const ACTION_UPDATE_STAGES = `Settings/Review_Workflows/WORKFLOW_UPDATE_STAGES`;
|
|
10
16
|
export const ACTION_UPDATE_STAGE_POSITION = `Settings/Review_Workflows/WORKFLOW_UPDATE_STAGE_POSITION`;
|
|
11
17
|
export const ACTION_UPDATE_WORKFLOW = `Settings/Review_Workflows/WORKFLOW_UPDATE`;
|
|
12
18
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
1
3
|
import { useFetchClient } from '@strapi/helper-plugin';
|
|
2
4
|
import { useQuery } from 'react-query';
|
|
3
5
|
|
|
@@ -20,18 +22,26 @@ export function useReviewWorkflows(params = {}) {
|
|
|
20
22
|
}
|
|
21
23
|
);
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+
// the return value needs to be memoized, because intantiating
|
|
26
|
+
// an empty array as default value would lead to an unstable return
|
|
27
|
+
// value, which later on triggers infinite loops if used in the
|
|
28
|
+
// dependency arrays of other hooks
|
|
29
|
+
|
|
30
|
+
const workflows = React.useMemo(() => {
|
|
31
|
+
if (id && data?.data) {
|
|
32
|
+
return [data.data];
|
|
33
|
+
}
|
|
34
|
+
if (Array.isArray(data?.data)) {
|
|
35
|
+
return data.data;
|
|
36
|
+
}
|
|
24
37
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
} else if (Array.isArray(data?.data)) {
|
|
28
|
-
workflows = data.data;
|
|
29
|
-
}
|
|
38
|
+
return [];
|
|
39
|
+
}, [data?.data, id]);
|
|
30
40
|
|
|
31
41
|
return {
|
|
32
42
|
// meta contains e.g. the total of all workflows. we can not use
|
|
33
43
|
// the pagination object here, because the list is not paginated.
|
|
34
|
-
meta: data?.meta ?? {},
|
|
44
|
+
meta: React.useMemo(() => data?.meta ?? {}, [data?.meta]),
|
|
35
45
|
workflows,
|
|
36
46
|
isLoading,
|
|
37
47
|
status,
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import { useFetchClient } from '@strapi/helper-plugin';
|
|
4
|
+
import { useQuery } from 'react-query';
|
|
5
|
+
|
|
6
|
+
export function useReviewWorkflowsStages({ id, layout } = {}, queryOptions = {}) {
|
|
7
|
+
const { kind, uid } = layout;
|
|
8
|
+
const slug = kind === 'collectionType' ? 'collection-types' : 'single-types';
|
|
9
|
+
|
|
10
|
+
const { get } = useFetchClient();
|
|
11
|
+
|
|
12
|
+
const { data, isLoading, refetch } = useQuery(
|
|
13
|
+
['content-manager', slug, layout.uid, id, 'stages'],
|
|
14
|
+
async () => {
|
|
15
|
+
const { data } = await get(`/admin/content-manager/${slug}/${uid}/${id}/stages`);
|
|
16
|
+
|
|
17
|
+
return data;
|
|
18
|
+
},
|
|
19
|
+
queryOptions
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
// these return values need to be memoized, because the default value
|
|
23
|
+
// would lead to infinite rendering loops when used in a dependency array
|
|
24
|
+
// on an effect
|
|
25
|
+
const meta = React.useMemo(() => data?.meta ?? {}, [data?.meta]);
|
|
26
|
+
const stages = React.useMemo(() => data?.data ?? [], [data?.data]);
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
// meta contains e.g. the total of all workflows. we can not use
|
|
30
|
+
// the pagination object here, because the list is not paginated.
|
|
31
|
+
meta,
|
|
32
|
+
stages,
|
|
33
|
+
isLoading,
|
|
34
|
+
refetch,
|
|
35
|
+
};
|
|
36
|
+
}
|