mirror of
https://github.com/strapi/strapi.git
synced 2025-12-25 06:04:29 +00:00
Content type attributes permissions integration
Signed-off-by: HichamELBSI <elabbassih@gmail.com>
This commit is contained in:
parent
3f3f36a576
commit
3d4a43545e
@ -22,8 +22,8 @@
|
||||
"type": "string"
|
||||
},
|
||||
"categories": {
|
||||
"via": "addresses",
|
||||
"collection": "category",
|
||||
"via": "addresses",
|
||||
"dominant": true
|
||||
},
|
||||
"cover": {
|
||||
|
||||
@ -15,8 +15,8 @@
|
||||
"type": "text"
|
||||
},
|
||||
"addresses": {
|
||||
"collection": "address",
|
||||
"via": "categories"
|
||||
"via": "categories",
|
||||
"collection": "address"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,7 +124,7 @@ const CreatePage = () => {
|
||||
/>
|
||||
</FormCard>
|
||||
{!isLayoutLoading && (
|
||||
<Padded top size="md">
|
||||
<Padded top bottom size="md">
|
||||
<Permissions />
|
||||
</Padded>
|
||||
)}
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
import styled from 'styled-components';
|
||||
import { Flex } from '@buffetjs/core';
|
||||
|
||||
const CollapseLabel = styled(Flex)`
|
||||
padding-right: 10px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
flex: 1;
|
||||
`;
|
||||
|
||||
export default CollapseLabel;
|
||||
@ -0,0 +1,57 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Flex, Text, Checkbox, Padded } from '@buffetjs/core';
|
||||
|
||||
import PermissionCheckbox from '../../PermissionCheckbox';
|
||||
import PermissionName from '../PermissionName';
|
||||
import CollapseLabel from '../../CollapseLabel';
|
||||
import Chevron from '../Chevron';
|
||||
import PermissionWrapper from '../PermissionWrapper';
|
||||
import AttributeRowWrapper from './AttributeRowWrapper';
|
||||
|
||||
const AttributeRow = ({ attribute }) => {
|
||||
const isCollapsable = attribute.type === 'component';
|
||||
|
||||
const handleToggleAttributes = () => {
|
||||
console.log('openAttribute');
|
||||
};
|
||||
|
||||
return (
|
||||
<AttributeRowWrapper isCollapsable={isCollapsable} alignItems="center">
|
||||
<Flex style={{ flex: 1 }}>
|
||||
<Padded left size="sm" />
|
||||
<PermissionName width="15rem">
|
||||
<Checkbox someChecked />
|
||||
<CollapseLabel
|
||||
title={attribute.attributeName}
|
||||
alignItems="center"
|
||||
onClick={handleToggleAttributes}
|
||||
>
|
||||
<Text
|
||||
color="grey"
|
||||
ellipsis
|
||||
fontSize="xs"
|
||||
fontWeight="bold"
|
||||
lineHeight="20px"
|
||||
textTransform="uppercase"
|
||||
>
|
||||
{attribute.attributeName}
|
||||
</Text>
|
||||
<Chevron icon="chevron-down" />
|
||||
</CollapseLabel>
|
||||
</PermissionName>
|
||||
<PermissionWrapper>
|
||||
<PermissionCheckbox />
|
||||
<PermissionCheckbox />
|
||||
<PermissionCheckbox />
|
||||
</PermissionWrapper>
|
||||
</Flex>
|
||||
</AttributeRowWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
AttributeRow.propTypes = {
|
||||
attribute: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default AttributeRow;
|
||||
@ -0,0 +1,22 @@
|
||||
/* eslint-disable indent */
|
||||
import styled from 'styled-components';
|
||||
import { Flex } from '@buffetjs/core';
|
||||
|
||||
import Chevron from '../Chevron';
|
||||
|
||||
const AttributeRowWrapper = styled(Flex)`
|
||||
padding: 1rem 0;
|
||||
flex: 1;
|
||||
height: 36px;
|
||||
${({ isCollapsable }) =>
|
||||
isCollapsable &&
|
||||
`
|
||||
&:hover {
|
||||
${Chevron} {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
export default AttributeRowWrapper;
|
||||
@ -0,0 +1,8 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
border: 1px solid ${({ theme }) => theme.main.colors.darkBlue};
|
||||
border-top: none;
|
||||
`;
|
||||
|
||||
export default Wrapper;
|
||||
@ -0,0 +1,80 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled from 'styled-components';
|
||||
import { Padded, Flex, Text } from '@buffetjs/core';
|
||||
import { useGlobalContext } from 'strapi-helper-plugin';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import { getAttributesToDisplay } from '../../../../../../utils';
|
||||
import AttributeRow from './AttributeRow';
|
||||
import Wrapper from './Wrapper';
|
||||
|
||||
// Those styles are very specific.
|
||||
// so it is not a big problem to use custom paddings and widths.
|
||||
const ActionTitle = styled.div`
|
||||
width: 12rem;
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
`;
|
||||
const FieldsTitleWrapper = styled.div`
|
||||
width: 18rem;
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
padding-left: 3.5rem;
|
||||
`;
|
||||
|
||||
const Attributes = ({ attributes }) => {
|
||||
const { plugins } = useGlobalContext();
|
||||
const { formatMessage } = useIntl();
|
||||
const attributesToDisplay = getAttributesToDisplay(plugins, attributes);
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<Flex>
|
||||
<FieldsTitleWrapper>
|
||||
<Text fontWeight="bold">
|
||||
{formatMessage({
|
||||
id: 'Settings.roles.form.permissions.fieldsPermissions',
|
||||
defaultMessage: 'Fields permissions',
|
||||
})}
|
||||
</Text>
|
||||
</FieldsTitleWrapper>
|
||||
<ActionTitle>
|
||||
<Text fontWeight="bold">
|
||||
{formatMessage({
|
||||
id: 'Settings.roles.form.permissions.create',
|
||||
defaultMessage: 'Create',
|
||||
})}
|
||||
</Text>
|
||||
</ActionTitle>
|
||||
<ActionTitle>
|
||||
<Text fontWeight="bold">
|
||||
{formatMessage({
|
||||
id: 'Settings.roles.form.permissions.read',
|
||||
defaultMessage: 'Read',
|
||||
})}
|
||||
</Text>
|
||||
</ActionTitle>
|
||||
<ActionTitle>
|
||||
<Text fontWeight="bold">
|
||||
{formatMessage({
|
||||
id: 'Settings.roles.form.permissions.update',
|
||||
defaultMessage: 'Update',
|
||||
})}
|
||||
</Text>
|
||||
</ActionTitle>
|
||||
</Flex>
|
||||
<Padded left size="md">
|
||||
{attributesToDisplay.map(attribute => (
|
||||
<AttributeRow attribute={attribute} key={attribute.attributeName} />
|
||||
))}
|
||||
</Padded>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
Attributes.propTypes = {
|
||||
attributes: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default Attributes;
|
||||
@ -8,7 +8,7 @@ const PermissionName = styled.div`
|
||||
`;
|
||||
|
||||
PermissionName.defaultProps = {
|
||||
width: '20rem',
|
||||
width: '18rem',
|
||||
};
|
||||
PermissionName.propTypes = {
|
||||
width: PropTypes.string,
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
import styled from 'styled-components';
|
||||
import { Flex } from '@buffetjs/core';
|
||||
|
||||
const PermissionWrapper = styled(Flex)`
|
||||
flex: 1;
|
||||
`;
|
||||
|
||||
export default PermissionWrapper;
|
||||
@ -1,23 +1,19 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled from 'styled-components';
|
||||
import { Checkbox, Flex, Text, Padded } from '@buffetjs/core';
|
||||
|
||||
import Chevron from './Chevron';
|
||||
import PermissionCheckbox from '../PermissionCheckbox';
|
||||
import PermissionName from './PermissionName';
|
||||
import Chevron from './Chevron';
|
||||
import StyledRow from './StyledRow';
|
||||
|
||||
// No need to create an other file for this style. It will be used only in this file.
|
||||
const CollapseLabel = styled(Flex)`
|
||||
cursor: pointer;
|
||||
`;
|
||||
import Attributes from './Attributes';
|
||||
import PermissionWrapper from './PermissionWrapper';
|
||||
import CollapseLabel from '../CollapseLabel';
|
||||
|
||||
const ContentTypeRow = ({
|
||||
openContentTypeAttributes,
|
||||
openedContentTypeAttributes,
|
||||
contentType,
|
||||
index,
|
||||
openContentTypeAttributes,
|
||||
openedContentTypeAttributes,
|
||||
}) => {
|
||||
const isActive = openedContentTypeAttributes === contentType.name;
|
||||
|
||||
@ -26,31 +22,40 @@ const ContentTypeRow = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledRow isActive={isActive} isGrey={index % 2 === 0}>
|
||||
<Flex>
|
||||
<Padded left size="sm" />
|
||||
<PermissionName>
|
||||
<Checkbox someChecked />
|
||||
<CollapseLabel alignItems="center" onClick={handleToggleAttributes}>
|
||||
<Text
|
||||
color="grey"
|
||||
fontWeight="bold"
|
||||
fontSize="xs"
|
||||
textTransform="uppercase"
|
||||
lineHeight="20px"
|
||||
<>
|
||||
<StyledRow isActive={isActive} isGrey={index % 2 === 0}>
|
||||
<Flex style={{ flex: 1 }}>
|
||||
<Padded left size="sm" />
|
||||
<PermissionName>
|
||||
<Checkbox someChecked />
|
||||
<CollapseLabel
|
||||
title={contentType.name}
|
||||
alignItems="center"
|
||||
onClick={handleToggleAttributes}
|
||||
>
|
||||
{contentType.name}
|
||||
</Text>
|
||||
<Chevron icon={isActive ? 'chevron-up' : 'chevron-down'} />
|
||||
</CollapseLabel>
|
||||
</PermissionName>
|
||||
<PermissionCheckbox />
|
||||
<PermissionCheckbox />
|
||||
<PermissionCheckbox />
|
||||
<PermissionCheckbox />
|
||||
<PermissionCheckbox />
|
||||
</Flex>
|
||||
</StyledRow>
|
||||
<Text
|
||||
color="grey"
|
||||
ellipsis
|
||||
fontSize="xs"
|
||||
fontWeight="bold"
|
||||
lineHeight="20px"
|
||||
textTransform="uppercase"
|
||||
>
|
||||
{contentType.name}
|
||||
</Text>
|
||||
<Chevron icon={isActive ? 'chevron-up' : 'chevron-down'} />
|
||||
</CollapseLabel>
|
||||
</PermissionName>
|
||||
<PermissionWrapper>
|
||||
<PermissionCheckbox />
|
||||
<PermissionCheckbox />
|
||||
<PermissionCheckbox />
|
||||
<PermissionCheckbox />
|
||||
</PermissionWrapper>
|
||||
</Flex>
|
||||
</StyledRow>
|
||||
{isActive && <Attributes attributes={contentType.schema.attributes} />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -2,7 +2,9 @@ import styled from 'styled-components';
|
||||
import { Checkbox } from '@buffetjs/core';
|
||||
|
||||
const PermissionCheckbox = styled(Checkbox)`
|
||||
width: 10rem;
|
||||
min-width: 10rem;
|
||||
max-width: 12rem;
|
||||
flex: 1;
|
||||
`;
|
||||
|
||||
export default PermissionCheckbox;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const PermissionsHeaderWrapper = styled.div`
|
||||
padding-left: 211px;
|
||||
padding-left: 165px;
|
||||
padding-bottom: 25px;
|
||||
padding-top: 26px;
|
||||
`;
|
||||
|
||||
@ -1,18 +1,40 @@
|
||||
import React from 'react';
|
||||
import { Flex } from '@buffetjs/core';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import PermissionCheckbox from '../PermissionCheckbox';
|
||||
import Wrapper from './Wrapper';
|
||||
|
||||
const PermissionsHeader = () => {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<Flex>
|
||||
<PermissionCheckbox message="Create" />
|
||||
<PermissionCheckbox message="Read" />
|
||||
<PermissionCheckbox message="Update" />
|
||||
<PermissionCheckbox message="Delete" />
|
||||
<PermissionCheckbox message="Publish" />
|
||||
<PermissionCheckbox
|
||||
message={formatMessage({
|
||||
id: 'Settings.roles.form.permissions.create',
|
||||
defaultMessage: 'Create',
|
||||
})}
|
||||
/>
|
||||
<PermissionCheckbox
|
||||
message={formatMessage({
|
||||
id: 'Settings.roles.form.permissions.read',
|
||||
defaultMessage: 'Read',
|
||||
})}
|
||||
/>
|
||||
<PermissionCheckbox
|
||||
message={formatMessage({
|
||||
id: 'Settings.roles.form.permissions.update',
|
||||
defaultMessage: 'Update',
|
||||
})}
|
||||
/>
|
||||
<PermissionCheckbox
|
||||
message={formatMessage({
|
||||
id: 'Settings.roles.form.permissions.delete',
|
||||
defaultMessage: 'Delete',
|
||||
})}
|
||||
/>
|
||||
</Flex>
|
||||
</Wrapper>
|
||||
);
|
||||
|
||||
@ -2,6 +2,11 @@ import styled from 'styled-components';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
background-color: ${({ theme }) => theme.main.colors.white};
|
||||
overflow: auto;
|
||||
|
||||
::-webkit-scrollbar {
|
||||
height: 10px;
|
||||
}
|
||||
`;
|
||||
|
||||
export default Wrapper;
|
||||
|
||||
@ -112,7 +112,7 @@ const EditPage = () => {
|
||||
role={role}
|
||||
/>
|
||||
{!isLayoutLoading && !isRoleLoading && (
|
||||
<Padded top size="md">
|
||||
<Padded top bottom size="md">
|
||||
<Permissions />
|
||||
</Padded>
|
||||
)}
|
||||
|
||||
@ -3,7 +3,7 @@ import { request } from 'strapi-helper-plugin';
|
||||
|
||||
import reducer, { initialState } from './reducer';
|
||||
|
||||
const Permissions = () => {
|
||||
const useContentTypes = () => {
|
||||
const [{ collectionTypes, singleTypes }, dispatch] = useReducer(reducer, initialState);
|
||||
|
||||
useEffect(() => {
|
||||
@ -11,6 +11,10 @@ const Permissions = () => {
|
||||
}, []);
|
||||
|
||||
const fetchContentTypes = async () => {
|
||||
dispatch({
|
||||
type: 'GET_CONTENT_TYPES',
|
||||
});
|
||||
|
||||
try {
|
||||
const { data } = await request('/content-manager/content-types', {
|
||||
method: 'GET',
|
||||
@ -31,7 +35,8 @@ const Permissions = () => {
|
||||
return {
|
||||
singleTypes,
|
||||
collectionTypes,
|
||||
getData: fetchContentTypes,
|
||||
};
|
||||
};
|
||||
|
||||
export default Permissions;
|
||||
export default useContentTypes;
|
||||
|
||||
@ -10,6 +10,12 @@ export const initialState = {
|
||||
const reducer = (state, action) =>
|
||||
produce(state, draftState => {
|
||||
switch (action.type) {
|
||||
case 'GET_CONTENT_TYPES': {
|
||||
draftState.collectionTypes = initialState.collectionTypes;
|
||||
draftState.singleTypes = initialState.singleTypes;
|
||||
draftState.isLoading = true;
|
||||
break;
|
||||
}
|
||||
case 'GET_CONTENT_TYPES_SUCCEDED': {
|
||||
const getContentTypeByKind = kind =>
|
||||
action.data.filter(
|
||||
|
||||
@ -39,6 +39,26 @@ describe('ADMIN | HOOKS | useContentTypes | reducer', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET_DATA', () => {
|
||||
it('should set isLoading to true to start getting the data', () => {
|
||||
const action = {
|
||||
type: 'GET_CONTENT_TYPES',
|
||||
};
|
||||
const initialState = {
|
||||
collectionTypes: [],
|
||||
singleTypes: [],
|
||||
isLoading: true,
|
||||
};
|
||||
const expected = {
|
||||
collectionTypes: [],
|
||||
singleTypes: [],
|
||||
isLoading: true,
|
||||
};
|
||||
|
||||
expect(reducer(initialState, action)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET_CONTENT_TYPES_SUCCEDED', () => {
|
||||
it('should return the state with the collectionTypes and singleTypes', () => {
|
||||
const action = {
|
||||
|
||||
@ -274,8 +274,13 @@
|
||||
"Settings.roles.form.input.description": "Description",
|
||||
"Settings.roles.form.input.name": "Name",
|
||||
"Settings.roles.form.title": "Details",
|
||||
"Settings.roles.form.description": "Select the granted permissions for the token.",
|
||||
"Settings.roles.form.description": "Name and description of the role",
|
||||
"Settings.roles.form.button.users-with-role": "Users with this role",
|
||||
"Settings.roles.form.permissions.create": "Create",
|
||||
"Settings.roles.form.permissions.fieldsPermissions": "Fields permissions",
|
||||
"Settings.roles.form.permissions.read": "Read",
|
||||
"Settings.roles.form.permissions.update": "Update",
|
||||
"Settings.roles.form.permissions.delete": "Delete",
|
||||
"Settings.roles.list.button.add": "Add new role",
|
||||
"Settings.roles.title.singular": "role",
|
||||
"Settings.roles.list.title.singular": "{number} role",
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
import { get } from 'lodash';
|
||||
|
||||
const getAttributesToDisplay = (plugins, attributes) => {
|
||||
const timestamps = get(
|
||||
plugins,
|
||||
['upload', 'fileModel', 'schema', 'options', 'timestamps'],
|
||||
['created_at', 'updated_at']
|
||||
);
|
||||
const matchingAttributes = Object.keys(attributes).filter(
|
||||
attribute => !['id', ...timestamps].includes(attribute)
|
||||
);
|
||||
const attributesToDisplay = matchingAttributes.map(attributeName => ({
|
||||
...attributes[attributeName],
|
||||
attributeName,
|
||||
}));
|
||||
|
||||
return attributesToDisplay;
|
||||
};
|
||||
|
||||
export default getAttributesToDisplay;
|
||||
@ -4,3 +4,4 @@ export { default as retrieveGlobalLinks } from './retrieveGlobalLinks';
|
||||
export { default as retrievePluginsMenu } from './retrievePluginsMenu';
|
||||
export { default as roleTabsLabel } from './roleTabsLabel';
|
||||
export { default as fakePermissionsData } from './fakePermissionsData';
|
||||
export { default as getAttributesToDisplay } from './getAttributesToDisplay';
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
import { getAttributesToDisplay } from '../index';
|
||||
|
||||
describe('ADMIN | utils | getAttributesToDisplay', () => {
|
||||
it('should return attributes without id and timestamps', () => {
|
||||
const attributes = {
|
||||
id: { type: 'number' },
|
||||
title: { type: 'string' },
|
||||
description: { type: 'string' },
|
||||
created_at: { type: 'timestamp' },
|
||||
updated_at: { type: 'timestamp' },
|
||||
};
|
||||
const actual = getAttributesToDisplay(attributes);
|
||||
const expectedAttributes = [
|
||||
{ type: 'string', attributeName: 'title' },
|
||||
{ type: 'string', attributeName: 'description' },
|
||||
];
|
||||
expect(actual).toEqual(expectedAttributes);
|
||||
});
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user