mirror of
https://github.com/strapi/strapi.git
synced 2025-09-17 12:27:33 +00:00
Merge pull request #17358 from strapi/feat/review-workflows-rbac-fe
Feat: Add RBAC permissions for review-workflow settings pages
This commit is contained in:
commit
a2b088974e
@ -6,6 +6,9 @@ export const ADMIN_PERMISSIONS_EE = {
|
|||||||
},
|
},
|
||||||
'review-workflows': {
|
'review-workflows': {
|
||||||
main: [{ action: 'admin::review-workflows.read', subject: null }],
|
main: [{ action: 'admin::review-workflows.read', subject: null }],
|
||||||
|
create: [{ action: 'admin::review-workflows.create', subject: null }],
|
||||||
|
delete: [{ action: 'admin::review-workflows.delete', subject: null }],
|
||||||
|
update: [{ action: 'admin::review-workflows.update', subject: null }],
|
||||||
},
|
},
|
||||||
sso: {
|
sso: {
|
||||||
main: [{ action: 'admin::provider-login.read', subject: null }],
|
main: [{ action: 'admin::provider-login.read', subject: null }],
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
import { CheckPagePermissions } from '@strapi/helper-plugin';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { useSelector } from 'react-redux';
|
|
||||||
|
|
||||||
import { selectAdminPermissions } from '../../../../../../../../admin/src/pages/App/selectors';
|
|
||||||
|
|
||||||
export function ProtectedPage({ children }) {
|
|
||||||
const permissions = useSelector(selectAdminPermissions);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<CheckPagePermissions permissions={permissions.settings['review-workflows'].main}>
|
|
||||||
{children}
|
|
||||||
</CheckPagePermissions>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
ProtectedPage.propTypes = {
|
|
||||||
children: PropTypes.node.isRequired,
|
|
||||||
};
|
|
@ -1 +0,0 @@
|
|||||||
export * from './ProtectedPage';
|
|
@ -50,6 +50,7 @@ export function Stage({
|
|||||||
index,
|
index,
|
||||||
canDelete,
|
canDelete,
|
||||||
canReorder,
|
canReorder,
|
||||||
|
canUpdate,
|
||||||
isOpen: isOpenDefault = false,
|
isOpen: isOpenDefault = false,
|
||||||
stagesCount,
|
stagesCount,
|
||||||
}) {
|
}) {
|
||||||
@ -216,6 +217,7 @@ export function Stage({
|
|||||||
|
|
||||||
<IconButton
|
<IconButton
|
||||||
background="transparent"
|
background="transparent"
|
||||||
|
disabled={!canUpdate}
|
||||||
forwardedAs="div"
|
forwardedAs="div"
|
||||||
role="button"
|
role="button"
|
||||||
noBorder
|
noBorder
|
||||||
@ -240,6 +242,7 @@ export function Stage({
|
|||||||
<TextInput
|
<TextInput
|
||||||
{...nameField}
|
{...nameField}
|
||||||
id={nameField.name}
|
id={nameField.name}
|
||||||
|
disabled={!canUpdate}
|
||||||
label={formatMessage({
|
label={formatMessage({
|
||||||
id: 'Settings.review-workflows.stage.name.label',
|
id: 'Settings.review-workflows.stage.name.label',
|
||||||
defaultMessage: 'Stage name',
|
defaultMessage: 'Stage name',
|
||||||
@ -255,6 +258,7 @@ export function Stage({
|
|||||||
|
|
||||||
<GridItem col={6}>
|
<GridItem col={6}>
|
||||||
<SingleSelect
|
<SingleSelect
|
||||||
|
disabled={!canUpdate}
|
||||||
error={colorMeta?.error ?? false}
|
error={colorMeta?.error ?? false}
|
||||||
id={colorField.name}
|
id={colorField.name}
|
||||||
required
|
required
|
||||||
@ -319,5 +323,6 @@ Stage.propTypes = PropTypes.shape({
|
|||||||
color: PropTypes.string.isRequired,
|
color: PropTypes.string.isRequired,
|
||||||
canDelete: PropTypes.bool.isRequired,
|
canDelete: PropTypes.bool.isRequired,
|
||||||
canReorder: PropTypes.bool.isRequired,
|
canReorder: PropTypes.bool.isRequired,
|
||||||
|
canUpdate: PropTypes.bool.isRequired,
|
||||||
stagesCount: PropTypes.number.isRequired,
|
stagesCount: PropTypes.number.isRequired,
|
||||||
}).isRequired;
|
}).isRequired;
|
||||||
|
@ -115,4 +115,13 @@ describe('Admin | Settings | Review Workflow | Stage', () => {
|
|||||||
|
|
||||||
expect(getByRole('textbox').value).toBe('something');
|
expect(getByRole('textbox').value).toBe('something');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('disables all input fields, if canUpdate = false', async () => {
|
||||||
|
const { container, getByRole } = setup({ canUpdate: false });
|
||||||
|
|
||||||
|
await user.click(container.querySelector('button[aria-expanded]'));
|
||||||
|
|
||||||
|
expect(getByRole('textbox')).toHaveAttribute('disabled');
|
||||||
|
expect(getByRole('combobox')).toHaveAttribute('data-disabled');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -12,26 +12,27 @@ import { AddStage } from '../AddStage';
|
|||||||
|
|
||||||
import { Stage } from './Stage';
|
import { Stage } from './Stage';
|
||||||
|
|
||||||
const StagesContainer = styled(Box)`
|
|
||||||
position: relative;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Background = styled(Box)`
|
const Background = styled(Box)`
|
||||||
left: 50%;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
function Stages({ stages }) {
|
export function Stages({ canDelete, canUpdate, stages }) {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const { trackUsage } = useTracking();
|
const { trackUsage } = useTracking();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex direction="column" gap={6} width="100%">
|
<Flex direction="column" gap={6} width="100%">
|
||||||
<StagesContainer spacing={4} width="100%">
|
<Box position="relative" spacing={4} width="100%">
|
||||||
<Background background="neutral200" height="100%" width={2} zIndex={1} />
|
<Background
|
||||||
|
background="neutral200"
|
||||||
|
height="100%"
|
||||||
|
left="50%"
|
||||||
|
position="absolute"
|
||||||
|
top="0"
|
||||||
|
width={2}
|
||||||
|
zIndex={1}
|
||||||
|
/>
|
||||||
|
|
||||||
<Flex
|
<Flex
|
||||||
direction="column"
|
direction="column"
|
||||||
@ -49,18 +50,19 @@ function Stages({ stages }) {
|
|||||||
<Stage
|
<Stage
|
||||||
id={id}
|
id={id}
|
||||||
index={index}
|
index={index}
|
||||||
canDelete={stages.length > 1}
|
|
||||||
isOpen={!stage.id}
|
isOpen={!stage.id}
|
||||||
|
canDelete={stages.length > 1 && canDelete}
|
||||||
canReorder={stages.length > 1}
|
canReorder={stages.length > 1}
|
||||||
|
canUpdate={canUpdate}
|
||||||
stagesCount={stages.length}
|
stagesCount={stages.length}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Flex>
|
</Flex>
|
||||||
</StagesContainer>
|
</Box>
|
||||||
|
|
||||||
<Flex direction="column" gap={6}>
|
{canUpdate && (
|
||||||
<AddStage
|
<AddStage
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@ -73,18 +75,20 @@ function Stages({ stages }) {
|
|||||||
defaultMessage: 'Add new stage',
|
defaultMessage: 'Add new stage',
|
||||||
})}
|
})}
|
||||||
</AddStage>
|
</AddStage>
|
||||||
</Flex>
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Stages };
|
|
||||||
|
|
||||||
Stages.defaultProps = {
|
Stages.defaultProps = {
|
||||||
|
canDelete: true,
|
||||||
|
canUpdate: true,
|
||||||
stages: [],
|
stages: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
Stages.propTypes = {
|
Stages.propTypes = {
|
||||||
|
canDelete: PropTypes.bool,
|
||||||
|
canUpdate: PropTypes.bool,
|
||||||
stages: PropTypes.arrayOf(
|
stages: PropTypes.arrayOf(
|
||||||
PropTypes.shape({
|
PropTypes.shape({
|
||||||
id: PropTypes.number,
|
id: PropTypes.number,
|
||||||
|
@ -122,4 +122,10 @@ describe('Admin | Settings | Review Workflow | Stages', () => {
|
|||||||
name: 'New name',
|
name: 'New name',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not render the "add stage" button if canUpdate = false', () => {
|
||||||
|
const { queryByText } = setup({ canUpdate: false });
|
||||||
|
|
||||||
|
expect(queryByText('Add new stage')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -9,7 +9,7 @@ import { useDispatch } from 'react-redux';
|
|||||||
|
|
||||||
import { updateWorkflow } from '../../actions';
|
import { updateWorkflow } from '../../actions';
|
||||||
|
|
||||||
export function WorkflowAttributes({ contentTypes: { collectionTypes, singleTypes } }) {
|
export function WorkflowAttributes({ canUpdate, contentTypes: { collectionTypes, singleTypes } }) {
|
||||||
const { formatMessage, locale } = useIntl();
|
const { formatMessage, locale } = useIntl();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const [nameField, nameMeta, nameHelper] = useField('name');
|
const [nameField, nameMeta, nameHelper] = useField('name');
|
||||||
@ -24,6 +24,7 @@ export function WorkflowAttributes({ contentTypes: { collectionTypes, singleType
|
|||||||
<TextInput
|
<TextInput
|
||||||
{...nameField}
|
{...nameField}
|
||||||
id={nameField.name}
|
id={nameField.name}
|
||||||
|
disabled={!canUpdate}
|
||||||
label={formatMessage({
|
label={formatMessage({
|
||||||
id: 'Settings.review-workflows.workflow.name.label',
|
id: 'Settings.review-workflows.workflow.name.label',
|
||||||
defaultMessage: 'Workflow Name',
|
defaultMessage: 'Workflow Name',
|
||||||
@ -50,6 +51,7 @@ export function WorkflowAttributes({ contentTypes: { collectionTypes, singleType
|
|||||||
{ count: value.length }
|
{ count: value.length }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
disabled={!canUpdate}
|
||||||
error={contentTypesMeta.error ?? false}
|
error={contentTypesMeta.error ?? false}
|
||||||
id={contentTypesField.name}
|
id={contentTypesField.name}
|
||||||
label={formatMessage({
|
label={formatMessage({
|
||||||
@ -102,7 +104,12 @@ const ContentTypeType = PropTypes.shape({
|
|||||||
}).isRequired,
|
}).isRequired,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
WorkflowAttributes.defaultProps = {
|
||||||
|
canUpdate: true,
|
||||||
|
};
|
||||||
|
|
||||||
WorkflowAttributes.propTypes = {
|
WorkflowAttributes.propTypes = {
|
||||||
|
canUpdate: PropTypes.bool,
|
||||||
contentTypes: PropTypes.shape({
|
contentTypes: PropTypes.shape({
|
||||||
collectionTypes: PropTypes.arrayOf(ContentTypeType).isRequired,
|
collectionTypes: PropTypes.arrayOf(ContentTypeType).isRequired,
|
||||||
singleTypes: PropTypes.arrayOf(ContentTypeType).isRequired,
|
singleTypes: PropTypes.arrayOf(ContentTypeType).isRequired,
|
||||||
|
@ -74,6 +74,9 @@ describe('Admin | Settings | Review Workflow | WorkflowAttributes', () => {
|
|||||||
expect(getByRole('textbox')).toHaveValue('workflow name');
|
expect(getByRole('textbox')).toHaveValue('workflow name');
|
||||||
expect(getByText(/2 content types selected/i)).toBeInTheDocument();
|
expect(getByText(/2 content types selected/i)).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(getByRole('textbox')).not.toHaveAttribute('disabled');
|
||||||
|
expect(getByRole('combobox', { name: /associated to/i })).not.toHaveAttribute('data-disabled');
|
||||||
|
|
||||||
await user.click(contentTypesSelect);
|
await user.click(contentTypesSelect);
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
@ -81,4 +84,13 @@ describe('Admin | Settings | Review Workflow | WorkflowAttributes', () => {
|
|||||||
expect(getByRole('option', { name: /content type 2/i })).toBeInTheDocument();
|
expect(getByRole('option', { name: /content type 2/i })).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should disabled fields if canUpdate = false', async () => {
|
||||||
|
const { getByRole } = setup({ canUpdate: false });
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(getByRole('textbox')).toHaveAttribute('disabled');
|
||||||
|
expect(getByRole('combobox', { name: /associated to/i })).toHaveAttribute('data-disabled');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import { Button, Flex, Loader } from '@strapi/design-system';
|
import { Button, Flex, Loader } from '@strapi/design-system';
|
||||||
import { useAPIErrorHandler, useFetchClient, useNotification } from '@strapi/helper-plugin';
|
import {
|
||||||
|
useAPIErrorHandler,
|
||||||
|
useFetchClient,
|
||||||
|
useNotification,
|
||||||
|
useRBAC,
|
||||||
|
} from '@strapi/helper-plugin';
|
||||||
import { Check } from '@strapi/icons';
|
import { Check } from '@strapi/icons';
|
||||||
import { useFormik, Form, FormikProvider } from 'formik';
|
import { useFormik, Form, FormikProvider } from 'formik';
|
||||||
import set from 'lodash/set';
|
import set from 'lodash/set';
|
||||||
@ -12,6 +17,7 @@ import { useHistory } from 'react-router-dom';
|
|||||||
|
|
||||||
import { useContentTypes } from '../../../../../../../../admin/src/hooks/useContentTypes';
|
import { useContentTypes } from '../../../../../../../../admin/src/hooks/useContentTypes';
|
||||||
import { useInjectReducer } from '../../../../../../../../admin/src/hooks/useInjectReducer';
|
import { useInjectReducer } from '../../../../../../../../admin/src/hooks/useInjectReducer';
|
||||||
|
import { selectAdminPermissions } from '../../../../../../../../admin/src/pages/App/selectors';
|
||||||
import { useLicenseLimits } from '../../../../../../hooks';
|
import { useLicenseLimits } from '../../../../../../hooks';
|
||||||
import { resetWorkflow } from '../../actions';
|
import { resetWorkflow } from '../../actions';
|
||||||
import * as Layout from '../../components/Layout';
|
import * as Layout from '../../components/Layout';
|
||||||
@ -29,6 +35,7 @@ export function ReviewWorkflowsCreateView() {
|
|||||||
const { push } = useHistory();
|
const { push } = useHistory();
|
||||||
const { formatAPIError } = useAPIErrorHandler();
|
const { formatAPIError } = useAPIErrorHandler();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
const permissions = useSelector(selectAdminPermissions);
|
||||||
const toggleNotification = useNotification();
|
const toggleNotification = useNotification();
|
||||||
const { collectionTypes, singleTypes, isLoading: isLoadingModels } = useContentTypes();
|
const { collectionTypes, singleTypes, isLoading: isLoadingModels } = useContentTypes();
|
||||||
const {
|
const {
|
||||||
@ -36,6 +43,9 @@ export function ReviewWorkflowsCreateView() {
|
|||||||
currentWorkflow: { data: currentWorkflow, isDirty: currentWorkflowIsDirty },
|
currentWorkflow: { data: currentWorkflow, isDirty: currentWorkflowIsDirty },
|
||||||
},
|
},
|
||||||
} = useSelector((state) => state?.[REDUX_NAMESPACE] ?? initialState);
|
} = useSelector((state) => state?.[REDUX_NAMESPACE] ?? initialState);
|
||||||
|
const {
|
||||||
|
allowedActions: { canCreate },
|
||||||
|
} = useRBAC(permissions.settings['review-workflows']);
|
||||||
const [showLimitModal, setShowLimitModal] = React.useState(false);
|
const [showLimitModal, setShowLimitModal] = React.useState(false);
|
||||||
const { isLoading: isLicenseLoading, getFeature } = useLicenseLimits();
|
const { isLoading: isLicenseLoading, getFeature } = useLicenseLimits();
|
||||||
const { meta, isLoading: isWorkflowLoading } = useReviewWorkflows();
|
const { meta, isLoading: isWorkflowLoading } = useReviewWorkflows();
|
||||||
@ -191,7 +201,7 @@ export function ReviewWorkflowsCreateView() {
|
|||||||
startIcon={<Check />}
|
startIcon={<Check />}
|
||||||
type="submit"
|
type="submit"
|
||||||
size="M"
|
size="M"
|
||||||
disabled={!currentWorkflowIsDirty}
|
disabled={!currentWorkflowIsDirty || !canCreate}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
>
|
>
|
||||||
{formatMessage({
|
{formatMessage({
|
||||||
|
@ -1,13 +1,18 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { ProtectedPage } from '../../components/ProtectedPage';
|
import { CheckPagePermissions } from '@strapi/helper-plugin';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
|
||||||
|
import { selectAdminPermissions } from '../../../../../../../../admin/src/pages/App/selectors';
|
||||||
|
|
||||||
import { ReviewWorkflowsCreateView } from './CreateView';
|
import { ReviewWorkflowsCreateView } from './CreateView';
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
|
const permissions = useSelector(selectAdminPermissions);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ProtectedPage>
|
<CheckPagePermissions permissions={permissions.settings['review-workflows'].create}>
|
||||||
<ReviewWorkflowsCreateView />
|
<ReviewWorkflowsCreateView />
|
||||||
</ProtectedPage>
|
</CheckPagePermissions>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
useAPIErrorHandler,
|
useAPIErrorHandler,
|
||||||
useFetchClient,
|
useFetchClient,
|
||||||
useNotification,
|
useNotification,
|
||||||
|
useRBAC,
|
||||||
} from '@strapi/helper-plugin';
|
} from '@strapi/helper-plugin';
|
||||||
import { Check } from '@strapi/icons';
|
import { Check } from '@strapi/icons';
|
||||||
import { useFormik, Form, FormikProvider } from 'formik';
|
import { useFormik, Form, FormikProvider } from 'formik';
|
||||||
@ -17,6 +18,7 @@ import { useParams } from 'react-router-dom';
|
|||||||
|
|
||||||
import { useContentTypes } from '../../../../../../../../admin/src/hooks/useContentTypes';
|
import { useContentTypes } from '../../../../../../../../admin/src/hooks/useContentTypes';
|
||||||
import { useInjectReducer } from '../../../../../../../../admin/src/hooks/useInjectReducer';
|
import { useInjectReducer } from '../../../../../../../../admin/src/hooks/useInjectReducer';
|
||||||
|
import { selectAdminPermissions } from '../../../../../../../../admin/src/pages/App/selectors';
|
||||||
import { useLicenseLimits } from '../../../../../../hooks';
|
import { useLicenseLimits } from '../../../../../../hooks';
|
||||||
import { setWorkflow } from '../../actions';
|
import { setWorkflow } from '../../actions';
|
||||||
import * as Layout from '../../components/Layout';
|
import * as Layout from '../../components/Layout';
|
||||||
@ -30,6 +32,7 @@ import { validateWorkflow } from '../../utils/validateWorkflow';
|
|||||||
|
|
||||||
export function ReviewWorkflowsEditView() {
|
export function ReviewWorkflowsEditView() {
|
||||||
const { workflowId } = useParams();
|
const { workflowId } = useParams();
|
||||||
|
const permissions = useSelector(selectAdminPermissions);
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const { put } = useFetchClient();
|
const { put } = useFetchClient();
|
||||||
@ -53,6 +56,9 @@ export function ReviewWorkflowsEditView() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
} = useSelector((state) => state?.[REDUX_NAMESPACE] ?? initialState);
|
} = useSelector((state) => state?.[REDUX_NAMESPACE] ?? initialState);
|
||||||
|
const {
|
||||||
|
allowedActions: { canDelete, canUpdate },
|
||||||
|
} = useRBAC(permissions.settings['review-workflows']);
|
||||||
const [isConfirmDeleteDialogOpen, setIsConfirmDeleteDialogOpen] = React.useState(false);
|
const [isConfirmDeleteDialogOpen, setIsConfirmDeleteDialogOpen] = React.useState(false);
|
||||||
const { getFeature, isLoading: isLicenseLoading } = useLicenseLimits();
|
const { getFeature, isLoading: isLicenseLoading } = useLicenseLimits();
|
||||||
const [showLimitModal, setShowLimitModal] = React.useState(false);
|
const [showLimitModal, setShowLimitModal] = React.useState(false);
|
||||||
@ -225,7 +231,7 @@ export function ReviewWorkflowsEditView() {
|
|||||||
startIcon={<Check />}
|
startIcon={<Check />}
|
||||||
type="submit"
|
type="submit"
|
||||||
size="M"
|
size="M"
|
||||||
disabled={!currentWorkflowIsDirty}
|
disabled={!currentWorkflowIsDirty || !canUpdate}
|
||||||
// if the confirm dialog is open the loading state is on
|
// if the confirm dialog is open the loading state is on
|
||||||
// the confirm button already
|
// the confirm button already
|
||||||
loading={!isConfirmDeleteDialogOpen && isLoading}
|
loading={!isConfirmDeleteDialogOpen && isLoading}
|
||||||
@ -256,8 +262,15 @@ export function ReviewWorkflowsEditView() {
|
|||||||
</Loader>
|
</Loader>
|
||||||
) : (
|
) : (
|
||||||
<Flex alignItems="stretch" direction="column" gap={7}>
|
<Flex alignItems="stretch" direction="column" gap={7}>
|
||||||
<WorkflowAttributes contentTypes={{ collectionTypes, singleTypes }} />
|
<WorkflowAttributes
|
||||||
<Stages stages={formik.values?.stages} />
|
canUpdate={canUpdate}
|
||||||
|
contentTypes={{ collectionTypes, singleTypes }}
|
||||||
|
/>
|
||||||
|
<Stages
|
||||||
|
canDelete={canDelete}
|
||||||
|
canUpdate={canUpdate}
|
||||||
|
stages={formik.values?.stages}
|
||||||
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
</Layout.Root>
|
</Layout.Root>
|
||||||
|
@ -1,13 +1,18 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { ProtectedPage } from '../../components/ProtectedPage';
|
import { CheckPagePermissions } from '@strapi/helper-plugin';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
|
||||||
|
import { selectAdminPermissions } from '../../../../../../../../admin/src/pages/App/selectors';
|
||||||
|
|
||||||
import { ReviewWorkflowsEditView } from './EditView';
|
import { ReviewWorkflowsEditView } from './EditView';
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
|
const permissions = useSelector(selectAdminPermissions);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ProtectedPage>
|
<CheckPagePermissions permissions={permissions.settings['review-workflows'].main}>
|
||||||
<ReviewWorkflowsEditView />
|
<ReviewWorkflowsEditView />
|
||||||
</ProtectedPage>
|
</CheckPagePermissions>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -23,15 +23,18 @@ import {
|
|||||||
useAPIErrorHandler,
|
useAPIErrorHandler,
|
||||||
useFetchClient,
|
useFetchClient,
|
||||||
useNotification,
|
useNotification,
|
||||||
|
useRBAC,
|
||||||
useTracking,
|
useTracking,
|
||||||
} from '@strapi/helper-plugin';
|
} from '@strapi/helper-plugin';
|
||||||
import { Pencil, Plus, Trash } from '@strapi/icons';
|
import { Pencil, Plus, Trash } from '@strapi/icons';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { useMutation } from 'react-query';
|
import { useMutation } from 'react-query';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import { useContentTypes } from '../../../../../../../../admin/src/hooks/useContentTypes';
|
import { useContentTypes } from '../../../../../../../../admin/src/hooks/useContentTypes';
|
||||||
|
import { selectAdminPermissions } from '../../../../../../../../admin/src/pages/App/selectors';
|
||||||
import { useLicenseLimits } from '../../../../../../hooks';
|
import { useLicenseLimits } from '../../../../../../hooks';
|
||||||
import * as Layout from '../../components/Layout';
|
import * as Layout from '../../components/Layout';
|
||||||
import * as LimitsModal from '../../components/LimitsModal';
|
import * as LimitsModal from '../../components/LimitsModal';
|
||||||
@ -76,6 +79,10 @@ export function ReviewWorkflowsListView() {
|
|||||||
const toggleNotification = useNotification();
|
const toggleNotification = useNotification();
|
||||||
const { getFeature, isLoading: isLicenseLoading } = useLicenseLimits();
|
const { getFeature, isLoading: isLicenseLoading } = useLicenseLimits();
|
||||||
const { trackUsage } = useTracking();
|
const { trackUsage } = useTracking();
|
||||||
|
const permissions = useSelector(selectAdminPermissions);
|
||||||
|
const {
|
||||||
|
allowedActions: { canCreate, canDelete },
|
||||||
|
} = useRBAC(permissions.settings['review-workflows']);
|
||||||
|
|
||||||
const limits = getFeature('review-workflows');
|
const limits = getFeature('review-workflows');
|
||||||
|
|
||||||
@ -166,6 +173,7 @@ export function ReviewWorkflowsListView() {
|
|||||||
<Layout.Header
|
<Layout.Header
|
||||||
primaryAction={
|
primaryAction={
|
||||||
<LinkButton
|
<LinkButton
|
||||||
|
disabled={!canCreate}
|
||||||
startIcon={<Plus />}
|
startIcon={<Plus />}
|
||||||
size="S"
|
size="S"
|
||||||
to="/settings/review-workflows/create"
|
to="/settings/review-workflows/create"
|
||||||
@ -218,31 +226,36 @@ export function ReviewWorkflowsListView() {
|
|||||||
colCount={3}
|
colCount={3}
|
||||||
footer={
|
footer={
|
||||||
// TODO: we should be able to use a link here instead of an (inaccessible onClick) handler
|
// TODO: we should be able to use a link here instead of an (inaccessible onClick) handler
|
||||||
<TFooter
|
canCreate && (
|
||||||
icon={<Plus />}
|
<TFooter
|
||||||
onClick={() => {
|
icon={<Plus />}
|
||||||
/**
|
onClick={() => {
|
||||||
* If the current license has a workflow limit:
|
/**
|
||||||
* check if the total count of workflows exceeds that limit
|
* If the current license has a workflow limit:
|
||||||
*
|
* check if the total count of workflows exceeds that limit
|
||||||
* If the current license does not have a limit (e.g. offline license):
|
*
|
||||||
* allow the user to navigate to the create-view. In case they exceed the
|
* If the current license does not have a limit (e.g. offline license):
|
||||||
* current hard-limit of 200 they will see an error thrown by the API.
|
* allow the user to navigate to the create-view. In case they exceed the
|
||||||
*/
|
* current hard-limit of 200 they will see an error thrown by the API.
|
||||||
|
*/
|
||||||
|
|
||||||
if (limits?.workflows && meta?.workflowCount >= parseInt(limits.workflows, 10)) {
|
if (
|
||||||
setShowLimitModal(true);
|
limits?.workflows &&
|
||||||
} else {
|
meta?.workflowCount >= parseInt(limits.workflows, 10)
|
||||||
push('/settings/review-workflows/create');
|
) {
|
||||||
trackUsage('willCreateWorkflow');
|
setShowLimitModal(true);
|
||||||
}
|
} else {
|
||||||
}}
|
push('/settings/review-workflows/create');
|
||||||
>
|
trackUsage('willCreateWorkflow');
|
||||||
{formatMessage({
|
}
|
||||||
id: 'Settings.review-workflows.list.page.create',
|
}}
|
||||||
defaultMessage: 'Create new workflow',
|
>
|
||||||
})}
|
{formatMessage({
|
||||||
</TFooter>
|
id: 'Settings.review-workflows.list.page.create',
|
||||||
|
defaultMessage: 'Create new workflow',
|
||||||
|
})}
|
||||||
|
</TFooter>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
rowCount={1}
|
rowCount={1}
|
||||||
>
|
>
|
||||||
@ -334,7 +347,7 @@ export function ReviewWorkflowsListView() {
|
|||||||
},
|
},
|
||||||
{ name: 'Default workflow' }
|
{ name: 'Default workflow' }
|
||||||
)}
|
)}
|
||||||
disabled={workflows.length === 1}
|
disabled={workflows.length === 1 || !canDelete}
|
||||||
icon={<Trash />}
|
icon={<Trash />}
|
||||||
noBorder
|
noBorder
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -1,13 +1,18 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { ProtectedPage } from '../../components/ProtectedPage';
|
import { CheckPagePermissions } from '@strapi/helper-plugin';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
|
||||||
|
import { selectAdminPermissions } from '../../../../../../../../admin/src/pages/App/selectors';
|
||||||
|
|
||||||
import { ReviewWorkflowsListView } from './ListView';
|
import { ReviewWorkflowsListView } from './ListView';
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
|
const permissions = useSelector(selectAdminPermissions);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ProtectedPage>
|
<CheckPagePermissions permissions={permissions.settings['review-workflows'].main}>
|
||||||
<ReviewWorkflowsListView />
|
<ReviewWorkflowsListView />
|
||||||
</ProtectedPage>
|
</CheckPagePermissions>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user