mirror of
https://github.com/strapi/strapi.git
synced 2025-11-11 07:39:16 +00:00
Merge pull request #9368 from strapi/i18n/rbac-refactoring
[I18n] RBAC CT & ST refactoring
This commit is contained in:
commit
43b44f3b59
@ -10,6 +10,7 @@ import Tab from './Tab';
|
|||||||
const Tabs = ({ children, isLoading, tabsLabel }) => {
|
const Tabs = ({ children, isLoading, tabsLabel }) => {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
const [selectedTabIndex, setSelectedTabIndex] = useState(0);
|
const [selectedTabIndex, setSelectedTabIndex] = useState(0);
|
||||||
|
const selectedChild = React.Children.toArray(children)[selectedTabIndex];
|
||||||
|
|
||||||
const handleSelectedTab = index => {
|
const handleSelectedTab = index => {
|
||||||
if (index !== selectedTabIndex) {
|
if (index !== selectedTabIndex) {
|
||||||
@ -38,7 +39,7 @@ const Tabs = ({ children, isLoading, tabsLabel }) => {
|
|||||||
</Tab>
|
</Tab>
|
||||||
))}
|
))}
|
||||||
</Flex>
|
</Flex>
|
||||||
{children[selectedTabIndex]}
|
{selectedChild}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</TabsWrapper>
|
</TabsWrapper>
|
||||||
|
|||||||
@ -23,15 +23,15 @@ window.strapi = Object.assign(window.strapi || {}, {
|
|||||||
});
|
});
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
'strapi-plugin-documentation': require('../../../strapi-plugin-documentation/admin/src').default,
|
// 'strapi-plugin-documentation': require('../../../strapi-plugin-documentation/admin/src').default,
|
||||||
'strapi-plugin-users-permissions': require('../../../strapi-plugin-users-permissions/admin/src')
|
// 'strapi-plugin-users-permissions': require('../../../strapi-plugin-users-permissions/admin/src')
|
||||||
.default,
|
// .default,
|
||||||
'strapi-plugin-content-manager': require('../../../strapi-plugin-content-manager/admin/src')
|
// 'strapi-plugin-content-manager': require('../../../strapi-plugin-content-manager/admin/src')
|
||||||
.default,
|
// .default,
|
||||||
'strapi-plugin-content-type-builder': require('../../../strapi-plugin-content-type-builder/admin/src')
|
// 'strapi-plugin-content-type-builder': require('../../../strapi-plugin-content-type-builder/admin/src')
|
||||||
.default,
|
// .default,
|
||||||
'strapi-plugin-email': require('../../../strapi-plugin-email/admin/src').default,
|
// 'strapi-plugin-email': require('../../../strapi-plugin-email/admin/src').default,
|
||||||
'strapi-plugin-upload': require('../../../strapi-plugin-upload/admin/src').default,
|
// 'strapi-plugin-upload': require('../../../strapi-plugin-upload/admin/src').default,
|
||||||
'strapi-plugin-graphql': require('../../../strapi-plugin-graphql/admin/src').default,
|
// 'strapi-plugin-graphql': require('../../../strapi-plugin-graphql/admin/src').default,
|
||||||
'strapi-plugin-i18n': require('../../../strapi-plugin-i18n/admin/src').default,
|
// 'strapi-plugin-i18n': require('../../../strapi-plugin-i18n/admin/src').default,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -112,6 +112,7 @@
|
|||||||
"Settings.roles.form.description": "Name and description of the role",
|
"Settings.roles.form.description": "Name and description of the role",
|
||||||
"Settings.roles.form.input.description": "Description",
|
"Settings.roles.form.input.description": "Description",
|
||||||
"Settings.roles.form.input.name": "Name",
|
"Settings.roles.form.input.name": "Name",
|
||||||
|
"Settings.roles.form.permission.property-label": "{label} permissions",
|
||||||
"Settings.roles.form.permissions.attributesPermissions": "Fields permissions",
|
"Settings.roles.form.permissions.attributesPermissions": "Fields permissions",
|
||||||
"Settings.roles.form.permissions.create": "Create",
|
"Settings.roles.form.permissions.create": "Create",
|
||||||
"Settings.roles.form.permissions.delete": "Delete",
|
"Settings.roles.form.permissions.delete": "Delete",
|
||||||
|
|||||||
@ -1,83 +1,83 @@
|
|||||||
import ar from './ar.json';
|
// import ar from './ar.json';
|
||||||
import cs from './cs.json';
|
// import cs from './cs.json';
|
||||||
import de from './de.json';
|
// import de from './de.json';
|
||||||
import dk from './dk.json';
|
// import dk from './dk.json';
|
||||||
import en from './en.json';
|
import en from './en.json';
|
||||||
import es from './es.json';
|
// import es from './es.json';
|
||||||
import fr from './fr.json';
|
import fr from './fr.json';
|
||||||
import he from './he.json';
|
// import he from './he.json';
|
||||||
import id from './id.json';
|
// import id from './id.json';
|
||||||
import it from './it.json';
|
// import it from './it.json';
|
||||||
import ja from './ja.json';
|
// import ja from './ja.json';
|
||||||
import ko from './ko.json';
|
// import ko from './ko.json';
|
||||||
import ms from './ms.json';
|
// import ms from './ms.json';
|
||||||
import nl from './nl.json';
|
// import nl from './nl.json';
|
||||||
import pl from './pl.json';
|
// import pl from './pl.json';
|
||||||
import ptBR from './pt-BR.json';
|
// import ptBR from './pt-BR.json';
|
||||||
import pt from './pt.json';
|
// import pt from './pt.json';
|
||||||
import ru from './ru.json';
|
// import ru from './ru.json';
|
||||||
import th from './th.json';
|
// import th from './th.json';
|
||||||
import tr from './tr.json';
|
// import tr from './tr.json';
|
||||||
import vi from './vi.json';
|
// import vi from './vi.json';
|
||||||
import zhHans from './zh-Hans.json';
|
// import zhHans from './zh-Hans.json';
|
||||||
import zh from './zh.json';
|
// import zh from './zh.json';
|
||||||
import sk from './sk.json';
|
// import sk from './sk.json';
|
||||||
import uk from './uk.json';
|
// import uk from './uk.json';
|
||||||
|
|
||||||
const trads = {
|
const trads = {
|
||||||
ar,
|
// ar,
|
||||||
cs,
|
// cs,
|
||||||
de,
|
// de,
|
||||||
dk,
|
// dk,
|
||||||
en,
|
en,
|
||||||
es,
|
// es,
|
||||||
fr,
|
fr,
|
||||||
he,
|
// he,
|
||||||
id,
|
// id,
|
||||||
it,
|
// it,
|
||||||
ja,
|
// ja,
|
||||||
ko,
|
// ko,
|
||||||
ms,
|
// ms,
|
||||||
nl,
|
// nl,
|
||||||
pl,
|
// pl,
|
||||||
'pt-BR': ptBR,
|
// 'pt-BR': ptBR,
|
||||||
pt,
|
// pt,
|
||||||
ru,
|
// ru,
|
||||||
sk,
|
// sk,
|
||||||
th,
|
// th,
|
||||||
tr,
|
// tr,
|
||||||
uk,
|
// uk,
|
||||||
vi,
|
// vi,
|
||||||
'zh-Hans': zhHans,
|
// 'zh-Hans': zhHans,
|
||||||
zh,
|
// zh,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const languageNativeNames = {
|
export const languageNativeNames = {
|
||||||
ar: 'العربية',
|
// ar: 'العربية',
|
||||||
cs: 'Čeština',
|
// cs: 'Čeština',
|
||||||
de: 'Deutsch',
|
// de: 'Deutsch',
|
||||||
dk: 'Dansk',
|
// dk: 'Dansk',
|
||||||
en: 'English',
|
en: 'English',
|
||||||
es: 'Español',
|
// es: 'Español',
|
||||||
fr: 'Français',
|
fr: 'Français',
|
||||||
he: 'עברית',
|
// he: 'עברית',
|
||||||
id: 'Indonesian',
|
// id: 'Indonesian',
|
||||||
it: 'Italiano',
|
// it: 'Italiano',
|
||||||
ja: '日本語',
|
// ja: '日本語',
|
||||||
ko: '한국어',
|
// ko: '한국어',
|
||||||
ms: 'Melayu',
|
// ms: 'Melayu',
|
||||||
nl: 'Nederlands',
|
// nl: 'Nederlands',
|
||||||
pl: 'Polski',
|
// pl: 'Polski',
|
||||||
'pt-BR': 'Português (Brasil)',
|
// 'pt-BR': 'Português (Brasil)',
|
||||||
pt: 'Português (Portugal)',
|
// pt: 'Português (Portugal)',
|
||||||
ru: 'Русский',
|
// ru: 'Русский',
|
||||||
sk: 'Slovenčina',
|
// sk: 'Slovenčina',
|
||||||
th: 'ไทย',
|
// th: 'ไทย',
|
||||||
tr: 'Türkçe',
|
// tr: 'Türkçe',
|
||||||
uk: 'Українська',
|
// uk: 'Українська',
|
||||||
vi: 'Tiếng Việt',
|
// vi: 'Tiếng Việt',
|
||||||
'zh-Hans': '中文 (简体)',
|
// 'zh-Hans': '中文 (简体)',
|
||||||
zh: '中文 (繁體)',
|
// zh: '中文 (繁體)',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default trads;
|
export default trads;
|
||||||
|
|||||||
@ -0,0 +1,41 @@
|
|||||||
|
/* eslint-disable indent */
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { Checkbox } from '@buffetjs/core';
|
||||||
|
|
||||||
|
const CheckboxWithCondition = styled(Checkbox)`
|
||||||
|
min-width: 10rem;
|
||||||
|
max-width: 12rem;
|
||||||
|
flex: 1;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
input[type='checkbox'] {
|
||||||
|
z-index: 10;
|
||||||
|
&:after {
|
||||||
|
color: ${({ theme }) => theme.main.colors.mediumBlue};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${({ hasConditions, disabled, theme }) =>
|
||||||
|
hasConditions &&
|
||||||
|
`
|
||||||
|
&:before {
|
||||||
|
content: '•';
|
||||||
|
font-size: 11px;
|
||||||
|
position: absolute;
|
||||||
|
top: -6px;
|
||||||
|
left: -7px;
|
||||||
|
color: ${disabled ? theme.main.colors.grey : theme.main.colors.mediumBlue};
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
${({ disabled, theme }) =>
|
||||||
|
disabled &&
|
||||||
|
`
|
||||||
|
input[type='checkbox'] {
|
||||||
|
cursor: not-allowed;
|
||||||
|
&:after {
|
||||||
|
color: ${theme.main.colors.grey};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default CheckboxWithCondition;
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
|
||||||
|
const Chevron = styled(FontAwesomeIcon)`
|
||||||
|
width: 18px !important;
|
||||||
|
display: none;
|
||||||
|
padding-left: 1rem;
|
||||||
|
font-size: 2.1rem;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default Chevron;
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
import { Flex } from '@buffetjs/core';
|
||||||
|
|
||||||
|
const CollapseLabel = styled(Flex)`
|
||||||
|
padding-right: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
flex: 1;
|
||||||
|
${({ isCollapsable }) => isCollapsable && 'cursor: pointer;'}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default CollapseLabel;
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
/* eslint-disable indent */
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
color: ${({ theme }) => theme.main.colors.mediumBlue};
|
||||||
|
${({ isRight }) =>
|
||||||
|
isRight &&
|
||||||
|
`
|
||||||
|
position: absolute;
|
||||||
|
right: 5rem;
|
||||||
|
`}
|
||||||
|
${({ hasConditions, disabled, theme }) =>
|
||||||
|
hasConditions &&
|
||||||
|
`
|
||||||
|
&:before {
|
||||||
|
content: '•';
|
||||||
|
position: absolute;
|
||||||
|
top: -4px;
|
||||||
|
left: -15px;
|
||||||
|
font-size: 18px;
|
||||||
|
color: ${disabled ? theme.main.colors.grey : theme.main.colors.mediumBlue};
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default Wrapper;
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { useIntl } from 'react-intl';
|
||||||
|
import { Flex, Text, Padded } from '@buffetjs/core';
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
|
||||||
|
import Wrapper from './Wrapper';
|
||||||
|
|
||||||
|
const ConditionsButton = ({ onClick, className, hasConditions, isRight }) => {
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Wrapper
|
||||||
|
isRight={isRight}
|
||||||
|
hasConditions={hasConditions}
|
||||||
|
className={className}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
<Padded right size="smd">
|
||||||
|
<Flex alignItems="center">
|
||||||
|
<Text color="mediumBlue">
|
||||||
|
{formatMessage({ id: 'app.components.LeftMenuLinkContainer.settings' })}
|
||||||
|
</Text>
|
||||||
|
<Padded style={{ height: '18px', lineHeight: 'normal' }} left size="xs">
|
||||||
|
<FontAwesomeIcon style={{ fontSize: '11px' }} icon="cog" />
|
||||||
|
</Padded>
|
||||||
|
</Flex>
|
||||||
|
</Padded>
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ConditionsButton.defaultProps = {
|
||||||
|
className: null,
|
||||||
|
hasConditions: false,
|
||||||
|
isRight: false,
|
||||||
|
};
|
||||||
|
ConditionsButton.propTypes = {
|
||||||
|
onClick: PropTypes.func.isRequired,
|
||||||
|
className: PropTypes.string,
|
||||||
|
hasConditions: PropTypes.bool,
|
||||||
|
isRight: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is a styled component advanced usage :
|
||||||
|
// Used to make a ref to a non styled component.
|
||||||
|
// https://styled-components.com/docs/advanced#caveat
|
||||||
|
export default styled(ConditionsButton)``;
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
import { Text } from '@buffetjs/core';
|
||||||
|
import ConditionsButton from '../../ConditionsButton';
|
||||||
|
import Chevron from '../../Chevron';
|
||||||
|
|
||||||
|
const activeRowStyle = (theme, isActive) => `
|
||||||
|
border: 1px solid ${theme.main.colors.darkBlue};
|
||||||
|
background-color: ${theme.main.colors.lightBlue};
|
||||||
|
color: ${theme.main.colors.mediumBlue};
|
||||||
|
border-radius: ${isActive ? '2px 2px 0 0' : '2px'};
|
||||||
|
${Text} {
|
||||||
|
color: ${theme.main.colors.mediumBlue};
|
||||||
|
}
|
||||||
|
${Chevron} {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
${ConditionsButton} {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledRow = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 36px;
|
||||||
|
background-color: ${({ isGrey, theme }) =>
|
||||||
|
isGrey ? theme.main.colors.content.background : theme.main.colors.white};
|
||||||
|
border: 1px solid transparent;
|
||||||
|
${ConditionsButton} {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
${({ isActive, theme }) => isActive && activeRowStyle(theme, isActive)}
|
||||||
|
&:hover {
|
||||||
|
${({ theme, isActive }) => activeRowStyle(theme, isActive)}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default StyledRow;
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Flex, Padded } from '@buffetjs/core';
|
||||||
|
import CheckboxWithCondition from '../../CheckboxWithCondition';
|
||||||
|
import Chevron from '../../Chevron';
|
||||||
|
import ConditionsButton from '../../ConditionsButton';
|
||||||
|
import HiddenAction from '../../HiddenAction';
|
||||||
|
import Wrapper from './Wrapper';
|
||||||
|
import RowLabel from '../../RowLabel';
|
||||||
|
|
||||||
|
const Collapse = ({ availableActions, isActive, isGrey, name, onClickToggle }) => {
|
||||||
|
return (
|
||||||
|
<Wrapper isActive={isActive} isGrey={isGrey}>
|
||||||
|
<Flex style={{ flex: 1 }}>
|
||||||
|
<Padded left size="sm" />
|
||||||
|
<RowLabel label={name} onClick={onClickToggle} isCollapsable>
|
||||||
|
<Chevron icon={isActive ? 'chevron-up' : 'chevron-down'} />
|
||||||
|
</RowLabel>
|
||||||
|
|
||||||
|
<Flex style={{ flex: 1 }}>
|
||||||
|
{availableActions.map(action => {
|
||||||
|
if (!action.isDisplayed) {
|
||||||
|
return <HiddenAction key={action.actionId} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <CheckboxWithCondition key={action.actionId} name={action.actionId} />;
|
||||||
|
})}
|
||||||
|
</Flex>
|
||||||
|
<ConditionsButton isRight onClick={() => console.log('todo')} />
|
||||||
|
</Flex>
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Collapse.propTypes = {
|
||||||
|
availableActions: PropTypes.array.isRequired,
|
||||||
|
isActive: PropTypes.bool.isRequired,
|
||||||
|
isGrey: PropTypes.bool.isRequired,
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
onClickToggle: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Collapse;
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
/* eslint-disable indent */
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { Flex } from '@buffetjs/core';
|
||||||
|
import { activeStyle } from '../../utils';
|
||||||
|
import Chevron from '../../../Chevron';
|
||||||
|
|
||||||
|
const RowWrapper = styled(Flex)`
|
||||||
|
height: 36px;
|
||||||
|
padding: 1rem 0;
|
||||||
|
flex: 1;
|
||||||
|
${Chevron} {
|
||||||
|
width: 13px;
|
||||||
|
}
|
||||||
|
${({ isCollapsable, theme }) =>
|
||||||
|
isCollapsable &&
|
||||||
|
`
|
||||||
|
${Chevron} {
|
||||||
|
display: block;
|
||||||
|
color: ${theme.main.colors.grey};
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
${activeStyle(theme)}
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
${({ isActive, theme }) => isActive && activeStyle(theme)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default RowWrapper;
|
||||||
@ -0,0 +1,91 @@
|
|||||||
|
import React, { memo, useState, useMemo, useCallback } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Padded, Flex } from '@buffetjs/core';
|
||||||
|
import CheckboxWithCondition from '../../../CheckboxWithCondition';
|
||||||
|
import Chevron from '../../../Chevron';
|
||||||
|
import HiddenAction from '../../../HiddenAction';
|
||||||
|
import RequiredSign from '../../../RequiredSign';
|
||||||
|
import RowLabel from '../../../RowLabel';
|
||||||
|
import SubActionRow from '../SubActionRow';
|
||||||
|
import Wrapper from './Wrapper';
|
||||||
|
|
||||||
|
const ActionRow = ({ childrenForm, label, name, required, propertyActions }) => {
|
||||||
|
const [rowToOpen, setRowToOpen] = useState(null);
|
||||||
|
|
||||||
|
const isActive = rowToOpen === name;
|
||||||
|
|
||||||
|
const recursiveValues = useMemo(() => {
|
||||||
|
if (!Array.isArray(childrenForm)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return childrenForm;
|
||||||
|
}, [childrenForm]);
|
||||||
|
|
||||||
|
const isCollapsable = recursiveValues.length > 0;
|
||||||
|
|
||||||
|
const handleClick = useCallback(() => {
|
||||||
|
if (isCollapsable) {
|
||||||
|
setRowToOpen(prev => {
|
||||||
|
if (prev === name) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return name;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [isCollapsable, name]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Wrapper alignItems="center" isCollapsable={isCollapsable}>
|
||||||
|
<Flex style={{ flex: 1 }}>
|
||||||
|
<Padded left size="sm" />
|
||||||
|
<RowLabel
|
||||||
|
width="15rem"
|
||||||
|
onClick={handleClick}
|
||||||
|
isCollapsable={isCollapsable}
|
||||||
|
label={label}
|
||||||
|
// TODO
|
||||||
|
textColor="grey"
|
||||||
|
>
|
||||||
|
{required && <RequiredSign />}
|
||||||
|
<Chevron icon={isActive ? 'caret-up' : 'caret-down'} />
|
||||||
|
</RowLabel>
|
||||||
|
<Flex style={{ flex: 1 }}>
|
||||||
|
{propertyActions.map(action => {
|
||||||
|
if (!action.isActionRelatedToCurrentProperty) {
|
||||||
|
return <HiddenAction key={action.label} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <CheckboxWithCondition key={action.label} name="todo" />;
|
||||||
|
})}
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</Wrapper>
|
||||||
|
{isActive && (
|
||||||
|
<SubActionRow
|
||||||
|
// label={label}
|
||||||
|
// name={name}
|
||||||
|
propertyActions={propertyActions}
|
||||||
|
childrenForm={recursiveValues}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ActionRow.defaultProps = {
|
||||||
|
childrenForm: [],
|
||||||
|
required: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
ActionRow.propTypes = {
|
||||||
|
childrenForm: PropTypes.array,
|
||||||
|
label: PropTypes.string.isRequired,
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
propertyActions: PropTypes.array.isRequired,
|
||||||
|
required: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(ActionRow);
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { useIntl } from 'react-intl';
|
||||||
|
import { Flex, Text } from '@buffetjs/core';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
// Those styles are very specific.
|
||||||
|
// so it is not a big problem to use custom paddings and widths.
|
||||||
|
const HeaderLabel = styled.div`
|
||||||
|
width: 12rem;
|
||||||
|
padding-top: 1rem;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
`;
|
||||||
|
const PropertyLabelWrapper = styled.div`
|
||||||
|
width: 18rem;
|
||||||
|
padding-top: 1rem;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
padding-left: 3.5rem;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Header = ({ headers, label }) => {
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
const translatedLabel = formatMessage(
|
||||||
|
{
|
||||||
|
id: 'Settings.roles.form.permission.property-label',
|
||||||
|
defaultMessage: '{label} permissions',
|
||||||
|
},
|
||||||
|
{ label }
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex>
|
||||||
|
<PropertyLabelWrapper>
|
||||||
|
<Text fontWeight="bold">{translatedLabel}</Text>
|
||||||
|
</PropertyLabelWrapper>
|
||||||
|
{headers.map(header => {
|
||||||
|
if (!header.isActionRelatedToCurrentProperty) {
|
||||||
|
return <HeaderLabel key={header.label} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<HeaderLabel key={header.label}>
|
||||||
|
<Text textTransform="capitalize" fontWeight="bold">
|
||||||
|
{header.label}
|
||||||
|
</Text>
|
||||||
|
</HeaderLabel>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Header.propTypes = {
|
||||||
|
headers: PropTypes.arrayOf(
|
||||||
|
PropTypes.shape({
|
||||||
|
label: PropTypes.string.isRequired,
|
||||||
|
isActionRelatedToCurrentProperty: PropTypes.bool.isRequired,
|
||||||
|
})
|
||||||
|
).isRequired,
|
||||||
|
label: PropTypes.string.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Header;
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
/* eslint-disable indent */
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
padding-left: 15px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default Wrapper;
|
||||||
@ -0,0 +1,117 @@
|
|||||||
|
import React, { memo, useCallback, useMemo, useState } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Flex, Text } from '@buffetjs/core';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import CheckboxWithCondition from '../../../CheckboxWithCondition';
|
||||||
|
import Chevron from '../../../Chevron';
|
||||||
|
import CollapseLabel from '../../../CollapseLabel';
|
||||||
|
import Curve from '../../../Curve';
|
||||||
|
import HiddenAction from '../../../HiddenAction';
|
||||||
|
import RequiredSign from '../../../RequiredSign';
|
||||||
|
import { RowStyle, RowWrapper } from './row';
|
||||||
|
import { LeftBorderTimeline, TopTimeline } from './timeline';
|
||||||
|
import Wrapper from './Wrapper';
|
||||||
|
|
||||||
|
const SubLevelWrapper = styled.div`
|
||||||
|
padding-bottom: 8px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const SubActionRow = ({ childrenForm, recursiveLevel, propertyActions }) => {
|
||||||
|
const [rowToOpen, setRowToOpen] = useState(null);
|
||||||
|
const handleClickToggleSubLevel = useCallback(name => {
|
||||||
|
setRowToOpen(prev => {
|
||||||
|
if (prev === name) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return name;
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const displayedRecursiveValue = useMemo(() => {
|
||||||
|
if (!rowToOpen) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return childrenForm.find(({ value }) => value === rowToOpen);
|
||||||
|
}, [rowToOpen, childrenForm]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Wrapper>
|
||||||
|
<TopTimeline />
|
||||||
|
{childrenForm.map(({ label, value, required, children: subChildrenForm }, index) => {
|
||||||
|
const isVisible = index + 1 < childrenForm.length;
|
||||||
|
const isArrayType = Array.isArray(subChildrenForm);
|
||||||
|
const isSmall = isArrayType || index + 1 === childrenForm.length;
|
||||||
|
const isActive = rowToOpen === value;
|
||||||
|
console.log({ label, isSmall });
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LeftBorderTimeline key={value} isVisible={isVisible}>
|
||||||
|
<RowWrapper isSmall={isSmall}>
|
||||||
|
<Curve fill="#a5d5ff" />
|
||||||
|
<Flex style={{ flex: 1 }}>
|
||||||
|
<RowStyle level={recursiveLevel} isActive={isActive} isCollapsable={isArrayType}>
|
||||||
|
<CollapseLabel
|
||||||
|
alignItems="center"
|
||||||
|
isCollapsable={isArrayType}
|
||||||
|
onClick={() => {
|
||||||
|
if (isArrayType) {
|
||||||
|
handleClickToggleSubLevel(value);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
title={label}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
color={isActive ? 'mediumBlue' : 'grey'}
|
||||||
|
ellipsis
|
||||||
|
fontSize="xs"
|
||||||
|
fontWeight="bold"
|
||||||
|
lineHeight="20px"
|
||||||
|
textTransform="uppercase"
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</Text>
|
||||||
|
{required && <RequiredSign />}
|
||||||
|
<Chevron icon={isActive ? 'caret-up' : 'caret-down'} />
|
||||||
|
</CollapseLabel>
|
||||||
|
</RowStyle>
|
||||||
|
<Flex style={{ flex: 1 }}>
|
||||||
|
{propertyActions.map(action => {
|
||||||
|
if (!action.isActionRelatedToCurrentProperty) {
|
||||||
|
return <HiddenAction key={action.label} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <CheckboxWithCondition key={action.label} name="todo" />;
|
||||||
|
})}
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</RowWrapper>
|
||||||
|
{displayedRecursiveValue && isActive && (
|
||||||
|
<SubLevelWrapper>
|
||||||
|
<SubActionRow
|
||||||
|
// name={displayedRecursiveValue.key}
|
||||||
|
propertyActions={propertyActions}
|
||||||
|
recursiveLevel={recursiveLevel + 1}
|
||||||
|
childrenForm={displayedRecursiveValue.children}
|
||||||
|
/>
|
||||||
|
</SubLevelWrapper>
|
||||||
|
)}
|
||||||
|
</LeftBorderTimeline>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
SubActionRow.defaultProps = {
|
||||||
|
recursiveLevel: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
SubActionRow.propTypes = {
|
||||||
|
childrenForm: PropTypes.array.isRequired,
|
||||||
|
propertyActions: PropTypes.array.isRequired,
|
||||||
|
recursiveLevel: PropTypes.number,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(SubActionRow);
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
import { Flex } from '@buffetjs/core';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import Chevron from '../../../Chevron';
|
||||||
|
import { activeStyle } from '../../utils';
|
||||||
|
|
||||||
|
const RowStyle = styled.div`
|
||||||
|
padding-left: ${({ theme }) => theme.main.sizes.paddings.xs};
|
||||||
|
width: ${({ level }) => 128 - level * 18}px;
|
||||||
|
${Chevron} {
|
||||||
|
width: 13px;
|
||||||
|
}
|
||||||
|
${({ isCollapsable, theme }) =>
|
||||||
|
isCollapsable &&
|
||||||
|
`
|
||||||
|
${Chevron} {
|
||||||
|
display: block;
|
||||||
|
color: ${theme.main.colors.grey};
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
${activeStyle(theme)}
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
${({ isActive, theme }) => isActive && activeStyle(theme)}}
|
||||||
|
`;
|
||||||
|
|
||||||
|
RowStyle.propTypes = {
|
||||||
|
isActive: PropTypes.bool.isRequired,
|
||||||
|
isCollapsable: PropTypes.bool.isRequired,
|
||||||
|
level: PropTypes.number.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
const RowWrapper = styled(Flex)`
|
||||||
|
height: ${({ isSmall }) => (isSmall ? '28px' : '36px')};
|
||||||
|
`;
|
||||||
|
|
||||||
|
export { RowStyle, RowWrapper };
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const LeftBorderTimeline = styled.div`
|
||||||
|
border-left: ${({ isVisible }) => (isVisible ? '3px solid #a5d5ff' : '3px solid transparent')};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const TopTimeline = styled.div`
|
||||||
|
padding-top: 8px;
|
||||||
|
width: 3px;
|
||||||
|
background-color: #a5d5ff;
|
||||||
|
border-top-left-radius: 2px;
|
||||||
|
border-top-right-radius: 2px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export { LeftBorderTimeline, TopTimeline };
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
padding-top: ${({ withPadding }) => (withPadding ? '9px' : '0px')};
|
||||||
|
border: 1px solid ${({ theme }) => theme.main.colors.darkBlue};
|
||||||
|
border-top: none;
|
||||||
|
border-bottom: ${({ isLast, theme }) => {
|
||||||
|
if (isLast) {
|
||||||
|
return `1px solid ${theme.main.colors.darkBlue}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `none`;
|
||||||
|
}};
|
||||||
|
border-radius: 0px 0px 2px 2px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
Wrapper.defaultProps = {
|
||||||
|
isLast: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Wrapper;
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
import React, { useMemo } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Padded } from '@buffetjs/core';
|
||||||
|
import generateHeadersFromActions from './utils/generateHeadersFromActions';
|
||||||
|
import Header from './Header';
|
||||||
|
import ActionRow from './ActionRow';
|
||||||
|
import Wrapper from './Wrapper';
|
||||||
|
|
||||||
|
const CollapsePropertyMatrix = ({
|
||||||
|
availableActions,
|
||||||
|
childrenForm,
|
||||||
|
isLast,
|
||||||
|
isOdd,
|
||||||
|
label,
|
||||||
|
propertyName,
|
||||||
|
}) => {
|
||||||
|
const propertyActions = useMemo(
|
||||||
|
() => generateHeadersFromActions(availableActions, propertyName),
|
||||||
|
[availableActions, propertyName]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Wrapper withPadding={isOdd} isLast={isLast}>
|
||||||
|
<Header label={label} headers={propertyActions} />
|
||||||
|
<Padded left size="md">
|
||||||
|
{childrenForm.map(({ children: childrenForm, label, value, required }) => (
|
||||||
|
<ActionRow
|
||||||
|
childrenForm={childrenForm}
|
||||||
|
key={value}
|
||||||
|
label={label}
|
||||||
|
name={value}
|
||||||
|
required={required}
|
||||||
|
propertyActions={propertyActions}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Padded>
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
CollapsePropertyMatrix.propTypes = {
|
||||||
|
childrenForm: PropTypes.array.isRequired,
|
||||||
|
availableActions: PropTypes.array.isRequired,
|
||||||
|
isOdd: PropTypes.bool.isRequired,
|
||||||
|
isLast: PropTypes.bool.isRequired,
|
||||||
|
label: PropTypes.string.isRequired,
|
||||||
|
propertyName: PropTypes.string.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CollapsePropertyMatrix;
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
const generateHeadersFromActions = (actions, propertyName) => {
|
||||||
|
return actions.map(action => {
|
||||||
|
const isActionRelatedToCurrentProperty =
|
||||||
|
Array.isArray(action.applyToProperties) &&
|
||||||
|
action.applyToProperties.indexOf(propertyName) !== -1 &&
|
||||||
|
action.isDisplayed;
|
||||||
|
|
||||||
|
return { label: action.label, isActionRelatedToCurrentProperty };
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default generateHeadersFromActions;
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
import generateHeadersFromActions from '../generateHeadersFromActions';
|
||||||
|
|
||||||
|
describe('ADMIN | COMPONENTS | Permissions | CollapsePropertyMatrix | utils', () => {
|
||||||
|
it('should set isActionRelatedToCurrentProperty key to false if the applyToProperties is not an array', () => {
|
||||||
|
const data = [{ label: 'test' }, { label: 'test1' }];
|
||||||
|
const expected = [
|
||||||
|
{ label: 'test', isActionRelatedToCurrentProperty: false },
|
||||||
|
{ label: 'test1', isActionRelatedToCurrentProperty: false },
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(generateHeadersFromActions(data, 'test')).toEqual(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set isActionRelatedToCurrentProperty key to false if the propertyName is not in the applyToProperties array', () => {
|
||||||
|
const data = [
|
||||||
|
{ label: 'test', applyToProperties: ['foo'] },
|
||||||
|
{ label: 'test1', applyToProperties: ['foo'] },
|
||||||
|
];
|
||||||
|
const expected = [
|
||||||
|
{ label: 'test', isActionRelatedToCurrentProperty: false },
|
||||||
|
{ label: 'test1', isActionRelatedToCurrentProperty: false },
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(generateHeadersFromActions(data, 'test')).toEqual(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set isActionRelatedToCurrentProperty key to false if the isDisplayed key is falsy', () => {
|
||||||
|
const data = [
|
||||||
|
{ label: 'test', isDisplayed: false },
|
||||||
|
{ label: 'test1', isDisplayed: false },
|
||||||
|
];
|
||||||
|
const expected = [
|
||||||
|
{ label: 'test', isActionRelatedToCurrentProperty: false },
|
||||||
|
{ label: 'test1', isActionRelatedToCurrentProperty: false },
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(generateHeadersFromActions(data, 'test')).toEqual(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set the isActionRelatedToCurrentProperty key to true if isDisplayed is truthy and the propertyName is in the applyToProperties array', () => {
|
||||||
|
const data = [
|
||||||
|
{ label: 'test', isDisplayed: true, applyToProperties: ['test'] },
|
||||||
|
{ label: 'test1', isDisplayed: false, applyToProperties: ['test'] },
|
||||||
|
];
|
||||||
|
const expected = [
|
||||||
|
{ label: 'test', isActionRelatedToCurrentProperty: true },
|
||||||
|
{ label: 'test1', isActionRelatedToCurrentProperty: false },
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(generateHeadersFromActions(data, 'test')).toEqual(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
/* eslint-disable indent */
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const RowWrapper = styled.div`
|
||||||
|
${({ withMargin }) =>
|
||||||
|
withMargin &&
|
||||||
|
`
|
||||||
|
margin: 9px 0;
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default RowWrapper;
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
import React, { useCallback, useMemo } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import Collapse from './Collapse';
|
||||||
|
import CollapsePropertyMatrix from './CollapsePropertyMatrix';
|
||||||
|
import { getAvailableActions } from './utils';
|
||||||
|
import Wrapper from './Wrapper';
|
||||||
|
|
||||||
|
const ContentTypeCollapse = ({
|
||||||
|
allActions,
|
||||||
|
contentTypeName,
|
||||||
|
index,
|
||||||
|
isActive,
|
||||||
|
onClickToggleCollapse,
|
||||||
|
properties,
|
||||||
|
}) => {
|
||||||
|
const handleClickToggleCollapse = useCallback(() => {
|
||||||
|
onClickToggleCollapse(contentTypeName);
|
||||||
|
}, [contentTypeName, onClickToggleCollapse]);
|
||||||
|
|
||||||
|
const availableActions = useMemo(() => {
|
||||||
|
return getAvailableActions(allActions, contentTypeName);
|
||||||
|
}, [allActions, contentTypeName]);
|
||||||
|
|
||||||
|
const isOdd = useMemo(() => index % 2 !== 0, [index]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Wrapper withMargin={isOdd}>
|
||||||
|
<Collapse
|
||||||
|
availableActions={availableActions}
|
||||||
|
isActive={isActive}
|
||||||
|
isGrey={index % 2 === 0}
|
||||||
|
name={contentTypeName}
|
||||||
|
onClickToggle={handleClickToggleCollapse}
|
||||||
|
/>
|
||||||
|
{isActive &&
|
||||||
|
properties.map(({ label, value, children: childrenForm }, i) => {
|
||||||
|
return (
|
||||||
|
<CollapsePropertyMatrix
|
||||||
|
availableActions={availableActions}
|
||||||
|
childrenForm={childrenForm}
|
||||||
|
label={label}
|
||||||
|
propertyName={value}
|
||||||
|
key={value}
|
||||||
|
isLast={i === properties.length - 1}
|
||||||
|
isOdd={isOdd}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ContentTypeCollapse.propTypes = {
|
||||||
|
allActions: PropTypes.array.isRequired,
|
||||||
|
contentTypeName: PropTypes.string.isRequired,
|
||||||
|
index: PropTypes.number.isRequired,
|
||||||
|
isActive: PropTypes.bool.isRequired,
|
||||||
|
onClickToggleCollapse: PropTypes.func.isRequired,
|
||||||
|
properties: PropTypes.array.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ContentTypeCollapse;
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
// import styled from 'styled-components';
|
||||||
|
import { Text } from '@buffetjs/core';
|
||||||
|
import Chevron from '../../Chevron';
|
||||||
|
|
||||||
|
const activeStyle = theme => `
|
||||||
|
color: ${theme.main.colors.mediumBlue};
|
||||||
|
${Text} {
|
||||||
|
color: ${theme.main.colors.mediumBlue};
|
||||||
|
}
|
||||||
|
${Chevron} {
|
||||||
|
display: block;
|
||||||
|
color: ${theme.main.colors.mediumBlue};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default activeStyle;
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
const getAvailableActions = (actions, targetSubject) => {
|
||||||
|
return actions.map(action => {
|
||||||
|
const isDisplayed =
|
||||||
|
Array.isArray(action.subjects) && action.subjects.indexOf(targetSubject) !== -1;
|
||||||
|
|
||||||
|
return { ...action, isDisplayed };
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getAvailableActions;
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
export { default as activeStyle } from './activeStyle';
|
||||||
|
export { default as getAvailableActions } from './getAvailableActions';
|
||||||
@ -0,0 +1,110 @@
|
|||||||
|
import getAvailableActions from '../getAvailableActions';
|
||||||
|
|
||||||
|
describe('ADMIN | COMPONENTS | Permissions | ContentTypeCollapse | utils | getAvailableActions', () => {
|
||||||
|
it('should return an empty array', () => {
|
||||||
|
expect(getAvailableActions([])).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set the isDisplayed key to false to all actions if the subject value does not match the target', () => {
|
||||||
|
const actions = [
|
||||||
|
{
|
||||||
|
label: 'Create',
|
||||||
|
actionId: 'content-manager.explorer.create',
|
||||||
|
subjects: ['restaurant', 'address'],
|
||||||
|
applyToProperties: ['fields', 'locales'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Read',
|
||||||
|
actionId: 'content-manager.explorer.read',
|
||||||
|
subjects: ['restaurant', 'address'],
|
||||||
|
applyToProperties: ['fields', 'locales'],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const expected = [
|
||||||
|
{
|
||||||
|
label: 'Create',
|
||||||
|
actionId: 'content-manager.explorer.create',
|
||||||
|
subjects: ['restaurant', 'address'],
|
||||||
|
applyToProperties: ['fields', 'locales'],
|
||||||
|
isDisplayed: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Read',
|
||||||
|
actionId: 'content-manager.explorer.read',
|
||||||
|
subjects: ['restaurant', 'address'],
|
||||||
|
applyToProperties: ['fields', 'locales'],
|
||||||
|
isDisplayed: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(getAvailableActions(actions, 'test')).toEqual(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set the isDisplayed key to false for the actions which the type of the subjects value is not an array', () => {
|
||||||
|
const actions = [
|
||||||
|
{
|
||||||
|
label: 'Create',
|
||||||
|
actionId: 'content-manager.explorer.create',
|
||||||
|
subjects: 'test',
|
||||||
|
applyToProperties: ['fields', 'locales'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Read',
|
||||||
|
actionId: 'content-manager.explorer.read',
|
||||||
|
applyToProperties: ['fields', 'locales'],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const expected = [
|
||||||
|
{
|
||||||
|
label: 'Create',
|
||||||
|
actionId: 'content-manager.explorer.create',
|
||||||
|
subjects: 'test',
|
||||||
|
applyToProperties: ['fields', 'locales'],
|
||||||
|
isDisplayed: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Read',
|
||||||
|
actionId: 'content-manager.explorer.read',
|
||||||
|
applyToProperties: ['fields', 'locales'],
|
||||||
|
isDisplayed: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(getAvailableActions(actions, 'test')).toEqual(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set the isDisplayed key to false for the action which include the targetSubject in the subjects value array', () => {
|
||||||
|
const actions = [
|
||||||
|
{
|
||||||
|
label: 'Create',
|
||||||
|
actionId: 'content-manager.explorer.create',
|
||||||
|
subjects: ['restaurant', 'address'],
|
||||||
|
applyToProperties: ['fields', 'locales'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Read',
|
||||||
|
actionId: 'content-manager.explorer.read',
|
||||||
|
subjects: [],
|
||||||
|
applyToProperties: ['fields', 'locales'],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const expected = [
|
||||||
|
{
|
||||||
|
label: 'Create',
|
||||||
|
actionId: 'content-manager.explorer.create',
|
||||||
|
subjects: ['restaurant', 'address'],
|
||||||
|
applyToProperties: ['fields', 'locales'],
|
||||||
|
isDisplayed: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Read',
|
||||||
|
actionId: 'content-manager.explorer.read',
|
||||||
|
subjects: [],
|
||||||
|
applyToProperties: ['fields', 'locales'],
|
||||||
|
isDisplayed: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(getAvailableActions(actions, 'address')).toEqual(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
import React, { memo, useCallback, useState } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import ContentTypeCollapse from '../ContentTypeCollapse';
|
||||||
|
|
||||||
|
const ContentTypeCollapses = ({ actions, subjects }) => {
|
||||||
|
const [collapseToOpen, setCollapseToOpen] = useState(null);
|
||||||
|
|
||||||
|
const handleClickToggleCollapse = useCallback(
|
||||||
|
collapseName => {
|
||||||
|
const nextCollapseToOpen = collapseToOpen === collapseName ? null : collapseName;
|
||||||
|
|
||||||
|
setCollapseToOpen(nextCollapseToOpen);
|
||||||
|
},
|
||||||
|
[collapseToOpen]
|
||||||
|
);
|
||||||
|
|
||||||
|
return Object.keys(subjects).map((subject, index) => {
|
||||||
|
return (
|
||||||
|
<ContentTypeCollapse
|
||||||
|
allActions={actions}
|
||||||
|
key={subject}
|
||||||
|
contentTypeName={subject}
|
||||||
|
isActive={collapseToOpen === subject}
|
||||||
|
index={index}
|
||||||
|
onClickToggleCollapse={handleClickToggleCollapse}
|
||||||
|
properties={subjects[subject].properties}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
ContentTypeCollapses.defaultProps = {
|
||||||
|
actions: [],
|
||||||
|
subjects: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
ContentTypeCollapses.propTypes = {
|
||||||
|
actions: PropTypes.array.isRequired,
|
||||||
|
subjects: PropTypes.object,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(ContentTypeCollapses);
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
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;
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
import React, { memo } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Padded } from '@buffetjs/core';
|
||||||
|
import ContentTypeCollapses from '../ContentTypeCollapses';
|
||||||
|
import GlobalActions from '../GlobalActions';
|
||||||
|
import Wrapper from './Wrapper';
|
||||||
|
|
||||||
|
const ContentTypes = ({ layout: { actions, subjects } }) => {
|
||||||
|
return (
|
||||||
|
<Wrapper>
|
||||||
|
<Padded left right bottom size="md">
|
||||||
|
<GlobalActions actions={actions} />
|
||||||
|
<ContentTypeCollapses subjects={subjects} actions={actions} />
|
||||||
|
</Padded>
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ContentTypes.propTypes = {
|
||||||
|
layout: PropTypes.shape({
|
||||||
|
actions: PropTypes.array,
|
||||||
|
subjects: PropTypes.object,
|
||||||
|
}).isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(ContentTypes);
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
import React, { memo } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
const Curve = props => (
|
||||||
|
<svg
|
||||||
|
style={{
|
||||||
|
height: '14px',
|
||||||
|
transform: 'translate(-3.2px, -1px)',
|
||||||
|
position: 'relative',
|
||||||
|
}}
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 21.08 21"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<path
|
||||||
|
d="M2.58 2.5q-1.2 16 16 16"
|
||||||
|
fill="none"
|
||||||
|
stroke={props.fill}
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="5"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
Curve.defaultProps = {
|
||||||
|
fill: '#f3f4f4',
|
||||||
|
};
|
||||||
|
Curve.propTypes = {
|
||||||
|
fill: PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(Curve);
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
padding-left: 165px;
|
||||||
|
padding-bottom: 25px;
|
||||||
|
padding-top: 26px;
|
||||||
|
${({ disabled, theme }) =>
|
||||||
|
`
|
||||||
|
input[type='checkbox'] {
|
||||||
|
&:after {
|
||||||
|
color: ${!disabled ? theme.main.colors.mediumBlue : theme.main.colors.grey};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cursor: initial;
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default Wrapper;
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
import React, { memo, useMemo } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Flex } from '@buffetjs/core';
|
||||||
|
import { useIntl } from 'react-intl';
|
||||||
|
import CheckboxWithCondition from '../CheckboxWithCondition';
|
||||||
|
import Wrapper from './Wrapper';
|
||||||
|
|
||||||
|
const GlobalActions = ({ actions }) => {
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
|
const displayedActions = useMemo(() => {
|
||||||
|
return actions.filter(({ subjects }) => {
|
||||||
|
return subjects.length;
|
||||||
|
});
|
||||||
|
}, [actions]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Wrapper>
|
||||||
|
<Flex>
|
||||||
|
{displayedActions.map(({ label, actionId }) => {
|
||||||
|
return (
|
||||||
|
<CheckboxWithCondition
|
||||||
|
key={actionId}
|
||||||
|
message={formatMessage({
|
||||||
|
id: `Settings.roles.form.permissions.${label.toLowerCase()}`,
|
||||||
|
defaultMessage: label,
|
||||||
|
})}
|
||||||
|
name={actionId}
|
||||||
|
value={false}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Flex>
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
GlobalActions.defaultProps = {
|
||||||
|
actions: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
GlobalActions.propTypes = {
|
||||||
|
actions: PropTypes.arrayOf(
|
||||||
|
PropTypes.shape({
|
||||||
|
label: PropTypes.string.isRequired,
|
||||||
|
actionId: PropTypes.string.isRequired,
|
||||||
|
subjects: PropTypes.array.isRequired,
|
||||||
|
})
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(GlobalActions);
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
const findDisplayedActions = actions =>
|
||||||
|
actions.filter(({ subjects }) => subjects && subjects.length);
|
||||||
|
|
||||||
|
export default findDisplayedActions;
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
import findDisplayedActions from '../findDisplayedActions';
|
||||||
|
|
||||||
|
describe('ADMIN | COMPONENTS | Permissions | GlobalActions | utils', () => {
|
||||||
|
describe('findDisplayedActions', () => {
|
||||||
|
it('should return an empty array', () => {
|
||||||
|
expect(findDisplayedActions([])).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should filter the actions that have no subjects', () => {
|
||||||
|
const actions = [
|
||||||
|
{ label: 'Create' },
|
||||||
|
{ label: 'Read', subject: [] },
|
||||||
|
{ label: 'Update', subjects: ['test'] },
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(findDisplayedActions(actions)).toHaveLength(1);
|
||||||
|
expect(findDisplayedActions(actions)[0]).toEqual({ label: 'Update', subjects: ['test'] });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const HiddenAction = styled.div`
|
||||||
|
min-width: 10rem;
|
||||||
|
max-width: 12rem;
|
||||||
|
flex: 1;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default HiddenAction;
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
import React, { forwardRef, memo, useImperativeHandle } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Tabs } from '../../../../../../admin/src/components/Roles';
|
||||||
|
import { roleTabsLabel as TAB_LABELS } from '../../../../../../admin/src/utils';
|
||||||
|
import ContentTypes from '../ContentTypes';
|
||||||
|
import layout from '../temp/fakeData';
|
||||||
|
|
||||||
|
const Permissions = forwardRef(({ layout }, ref) => {
|
||||||
|
useImperativeHandle(ref, () => {
|
||||||
|
return {
|
||||||
|
getPermissions: () => {
|
||||||
|
console.log('todo');
|
||||||
|
},
|
||||||
|
resetForm: () => {
|
||||||
|
console.log('todo');
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tabs tabsLabel={TAB_LABELS}>
|
||||||
|
<ContentTypes layout={layout.sections.collectionTypes} />
|
||||||
|
<ContentTypes layout={layout.sections.singleTypes} />
|
||||||
|
<div>Plugins</div>
|
||||||
|
<div>Settings</div>
|
||||||
|
</Tabs>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Permissions.defaultProps = {
|
||||||
|
layout,
|
||||||
|
};
|
||||||
|
Permissions.propTypes = {
|
||||||
|
// Todo
|
||||||
|
layout: PropTypes.object,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(Permissions);
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const Required = styled.span`
|
||||||
|
color: ${({ theme }) => theme.main.colors.red};
|
||||||
|
padding-left: 2px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const RequiredSign = () => <Required>*</Required>;
|
||||||
|
|
||||||
|
export default RequiredSign;
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: ${({ width }) => width};
|
||||||
|
`;
|
||||||
|
|
||||||
|
Wrapper.defaultProps = {
|
||||||
|
width: '18rem',
|
||||||
|
};
|
||||||
|
|
||||||
|
Wrapper.propTypes = {
|
||||||
|
width: PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Wrapper;
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
import React, { memo } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Checkbox, Text } from '@buffetjs/core';
|
||||||
|
import CollapseLabel from '../CollapseLabel';
|
||||||
|
import Wrapper from './Wrapper';
|
||||||
|
|
||||||
|
const RowLabel = ({ isCollapsable, label, children, onClick, textColor, width }) => {
|
||||||
|
return (
|
||||||
|
<Wrapper width={width}>
|
||||||
|
<Checkbox name="todo" value={false} />
|
||||||
|
<CollapseLabel
|
||||||
|
title={label}
|
||||||
|
alignItems="center"
|
||||||
|
isCollapsable={isCollapsable}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
color={textColor}
|
||||||
|
ellipsis
|
||||||
|
fontSize="xs"
|
||||||
|
fontWeight="bold"
|
||||||
|
lineHeight="20px"
|
||||||
|
textTransform="uppercase"
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</Text>
|
||||||
|
{children}
|
||||||
|
</CollapseLabel>
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
RowLabel.defaultProps = {
|
||||||
|
children: null,
|
||||||
|
isCollapsable: false,
|
||||||
|
textColor: 'grey',
|
||||||
|
width: '18rem',
|
||||||
|
};
|
||||||
|
|
||||||
|
RowLabel.propTypes = {
|
||||||
|
children: PropTypes.node,
|
||||||
|
isCollapsable: PropTypes.bool,
|
||||||
|
label: PropTypes.string.isRequired,
|
||||||
|
onClick: PropTypes.func.isRequired,
|
||||||
|
textColor: PropTypes.string,
|
||||||
|
width: PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(RowLabel);
|
||||||
@ -17,9 +17,10 @@ import { useFetchPermissionsLayout, useFetchRole } from '../../../../../admin/sr
|
|||||||
import PageTitle from '../../../../../admin/src/components/SettingsPageTitle';
|
import PageTitle from '../../../../../admin/src/components/SettingsPageTitle';
|
||||||
import ContainerFluid from '../../../../../admin/src/components/ContainerFluid';
|
import ContainerFluid from '../../../../../admin/src/components/ContainerFluid';
|
||||||
import FormCard from '../../../../../admin/src/components/FormBloc';
|
import FormCard from '../../../../../admin/src/components/FormBloc';
|
||||||
import { ButtonWithNumber, Permissions } from '../../../../../admin/src/components/Roles';
|
import { ButtonWithNumber } from '../../../../../admin/src/components/Roles';
|
||||||
import SizedInput from '../../../../../admin/src/components/SizedInput';
|
import SizedInput from '../../../../../admin/src/components/SizedInput';
|
||||||
import { formatPermissionsToApi } from '../../../../../admin/src/utils';
|
import { formatPermissionsToApi } from '../../../../../admin/src/utils';
|
||||||
|
import Permissions from './Permissions';
|
||||||
|
|
||||||
import schema from './utils/schema';
|
import schema from './utils/schema';
|
||||||
|
|
||||||
@ -31,8 +32,15 @@ const CreatePage = () => {
|
|||||||
const { emitEvent, settingsBaseURL } = useGlobalContext();
|
const { emitEvent, settingsBaseURL } = useGlobalContext();
|
||||||
const params = useRouteMatch(`${settingsBaseURL}/roles/duplicate/:id`);
|
const params = useRouteMatch(`${settingsBaseURL}/roles/duplicate/:id`);
|
||||||
const id = get(params, 'params.id', null);
|
const id = get(params, 'params.id', null);
|
||||||
const { isLoading: isLayoutLoading, data: permissionsLayout } = useFetchPermissionsLayout();
|
const {
|
||||||
const { role, permissions: rolePermissions, isLoading: isRoleLoading } = useFetchRole(id);
|
isLoading: isLayoutLoading,
|
||||||
|
// data: permissionsLayout
|
||||||
|
} = useFetchPermissionsLayout();
|
||||||
|
const {
|
||||||
|
// role, permissions: rolePermissions,
|
||||||
|
isLoading: isRoleLoading,
|
||||||
|
} = useFetchRole(id);
|
||||||
|
// console.log({ role, rolePermissions });
|
||||||
|
|
||||||
const headerActions = (handleSubmit, handleReset) => [
|
const headerActions = (handleSubmit, handleReset) => [
|
||||||
{
|
{
|
||||||
@ -191,10 +199,10 @@ const CreatePage = () => {
|
|||||||
{!isLayoutLoading && !isRoleLoading && (
|
{!isLayoutLoading && !isRoleLoading && (
|
||||||
<Padded top bottom size="md">
|
<Padded top bottom size="md">
|
||||||
<Permissions
|
<Permissions
|
||||||
permissionsLayout={permissionsLayout}
|
// permissionsLayout={permissionsLayout}
|
||||||
ref={permissionsRef}
|
ref={permissionsRef}
|
||||||
rolePermissions={rolePermissions}
|
// rolePermissions={rolePermissions}
|
||||||
role={role}
|
// role={role}
|
||||||
/>
|
/>
|
||||||
</Padded>
|
</Padded>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -0,0 +1,425 @@
|
|||||||
|
const data = {
|
||||||
|
conditions: [
|
||||||
|
{
|
||||||
|
id: 'admin::is-creator',
|
||||||
|
displayName: 'Is creator',
|
||||||
|
category: 'default',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'admin::has-same-role-as-creator',
|
||||||
|
displayName: 'Has same role as creator',
|
||||||
|
category: 'default',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
sections: {
|
||||||
|
plugins: [
|
||||||
|
{
|
||||||
|
displayName: 'Read',
|
||||||
|
action: 'plugins::content-type-builder.read',
|
||||||
|
subCategory: 'general',
|
||||||
|
plugin: 'plugin::content-type-builder',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Access the Documentation',
|
||||||
|
action: 'plugins::documentation.read',
|
||||||
|
subCategory: 'general',
|
||||||
|
plugin: 'plugin::documentation',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Update and delete',
|
||||||
|
action: 'plugins::documentation.settings.update',
|
||||||
|
subCategory: 'settings',
|
||||||
|
plugin: 'plugin::documentation',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Regenerate',
|
||||||
|
action: 'plugins::documentation.settings.regenerate',
|
||||||
|
subCategory: 'settings',
|
||||||
|
plugin: 'plugin::documentation',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Access the Media Library',
|
||||||
|
action: 'plugins::upload.read',
|
||||||
|
subCategory: 'general',
|
||||||
|
plugin: 'plugin::upload',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Create (upload)',
|
||||||
|
action: 'plugins::upload.assets.create',
|
||||||
|
subCategory: 'assets',
|
||||||
|
plugin: 'plugin::upload',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Update (crop, details, replace) + delete',
|
||||||
|
action: 'plugins::upload.assets.update',
|
||||||
|
subCategory: 'assets',
|
||||||
|
plugin: 'plugin::upload',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Download',
|
||||||
|
action: 'plugins::upload.assets.download',
|
||||||
|
subCategory: 'assets',
|
||||||
|
plugin: 'plugin::upload',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Copy link',
|
||||||
|
action: 'plugins::upload.assets.copy-link',
|
||||||
|
subCategory: 'assets',
|
||||||
|
plugin: 'plugin::upload',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Create',
|
||||||
|
action: 'plugins::users-permissions.roles.create',
|
||||||
|
subCategory: 'roles',
|
||||||
|
plugin: 'plugin::users-permissions',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Read',
|
||||||
|
action: 'plugins::users-permissions.roles.read',
|
||||||
|
subCategory: 'roles',
|
||||||
|
plugin: 'plugin::users-permissions',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Update',
|
||||||
|
action: 'plugins::users-permissions.roles.update',
|
||||||
|
subCategory: 'roles',
|
||||||
|
plugin: 'plugin::users-permissions',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Delete',
|
||||||
|
action: 'plugins::users-permissions.roles.delete',
|
||||||
|
subCategory: 'roles',
|
||||||
|
plugin: 'plugin::users-permissions',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Read',
|
||||||
|
action: 'plugins::users-permissions.providers.read',
|
||||||
|
subCategory: 'providers',
|
||||||
|
plugin: 'plugin::users-permissions',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Edit',
|
||||||
|
action: 'plugins::users-permissions.providers.update',
|
||||||
|
subCategory: 'providers',
|
||||||
|
plugin: 'plugin::users-permissions',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Read',
|
||||||
|
action: 'plugins::users-permissions.email-templates.read',
|
||||||
|
subCategory: 'emailTemplates',
|
||||||
|
plugin: 'plugin::users-permissions',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Edit',
|
||||||
|
action: 'plugins::users-permissions.email-templates.update',
|
||||||
|
subCategory: 'emailTemplates',
|
||||||
|
plugin: 'plugin::users-permissions',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Read',
|
||||||
|
action: 'plugins::users-permissions.advanced-settings.read',
|
||||||
|
subCategory: 'advancedSettings',
|
||||||
|
plugin: 'plugin::users-permissions',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Edit',
|
||||||
|
action: 'plugins::users-permissions.advanced-settings.update',
|
||||||
|
subCategory: 'advancedSettings',
|
||||||
|
plugin: 'plugin::users-permissions',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Configure view',
|
||||||
|
action: 'plugins::content-manager.single-types.configure-view',
|
||||||
|
subCategory: 'single types',
|
||||||
|
plugin: 'plugin::content-manager',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Configure view',
|
||||||
|
action: 'plugins::content-manager.collection-types.configure-view',
|
||||||
|
subCategory: 'collection types',
|
||||||
|
plugin: 'plugin::content-manager',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Configure Layout',
|
||||||
|
action: 'plugins::content-manager.components.configure-layout',
|
||||||
|
subCategory: 'components',
|
||||||
|
plugin: 'plugin::content-manager',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
settings: [
|
||||||
|
{
|
||||||
|
displayName: 'Create',
|
||||||
|
action: 'plugins::i18n.locale.create',
|
||||||
|
category: 'Internationalization',
|
||||||
|
subCategory: 'Locales',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Read',
|
||||||
|
action: 'plugins::i18n.locale.read',
|
||||||
|
category: 'Internationalization',
|
||||||
|
subCategory: 'Locales',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Update',
|
||||||
|
action: 'plugins::i18n.locale.update',
|
||||||
|
category: 'Internationalization',
|
||||||
|
subCategory: 'Locales',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Delete',
|
||||||
|
action: 'plugins::i18n.locale.delete',
|
||||||
|
category: 'Internationalization',
|
||||||
|
subCategory: 'Locales',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Access the Media Library settings page',
|
||||||
|
action: 'plugins::upload.settings.read',
|
||||||
|
category: 'media library',
|
||||||
|
subCategory: 'general',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Read',
|
||||||
|
action: 'admin::provider-login.read',
|
||||||
|
category: 'single sign on',
|
||||||
|
subCategory: 'options',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Update',
|
||||||
|
action: 'admin::provider-login.update',
|
||||||
|
category: 'single sign on',
|
||||||
|
subCategory: 'options',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Access the marketplace',
|
||||||
|
action: 'admin::marketplace.read',
|
||||||
|
category: 'plugins and marketplace',
|
||||||
|
subCategory: 'marketplace',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Install (only for dev env)',
|
||||||
|
action: 'admin::marketplace.plugins.install',
|
||||||
|
category: 'plugins and marketplace',
|
||||||
|
subCategory: 'plugins',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Uninstall (only for dev env)',
|
||||||
|
action: 'admin::marketplace.plugins.uninstall',
|
||||||
|
category: 'plugins and marketplace',
|
||||||
|
subCategory: 'plugins',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Create',
|
||||||
|
action: 'admin::webhooks.create',
|
||||||
|
category: 'webhooks',
|
||||||
|
subCategory: 'general',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Read',
|
||||||
|
action: 'admin::webhooks.read',
|
||||||
|
category: 'webhooks',
|
||||||
|
subCategory: 'general',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Update',
|
||||||
|
action: 'admin::webhooks.update',
|
||||||
|
category: 'webhooks',
|
||||||
|
subCategory: 'general',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Delete',
|
||||||
|
action: 'admin::webhooks.delete',
|
||||||
|
category: 'webhooks',
|
||||||
|
subCategory: 'general',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Create (invite)',
|
||||||
|
action: 'admin::users.create',
|
||||||
|
category: 'users and roles',
|
||||||
|
subCategory: 'users',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Read',
|
||||||
|
action: 'admin::users.read',
|
||||||
|
category: 'users and roles',
|
||||||
|
subCategory: 'users',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Update',
|
||||||
|
action: 'admin::users.update',
|
||||||
|
category: 'users and roles',
|
||||||
|
subCategory: 'users',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Delete',
|
||||||
|
action: 'admin::users.delete',
|
||||||
|
category: 'users and roles',
|
||||||
|
subCategory: 'users',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Create',
|
||||||
|
action: 'admin::roles.create',
|
||||||
|
category: 'users and roles',
|
||||||
|
subCategory: 'roles',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Read',
|
||||||
|
action: 'admin::roles.read',
|
||||||
|
category: 'users and roles',
|
||||||
|
subCategory: 'roles',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Update',
|
||||||
|
action: 'admin::roles.update',
|
||||||
|
category: 'users and roles',
|
||||||
|
subCategory: 'roles',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Delete',
|
||||||
|
action: 'admin::roles.delete',
|
||||||
|
category: 'users and roles',
|
||||||
|
subCategory: 'roles',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
singleTypes: {}, // same format as under,
|
||||||
|
collectionTypes: {
|
||||||
|
subjects: {
|
||||||
|
address: {
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
label: 'Fields',
|
||||||
|
value: 'fields',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
label: 'POST',
|
||||||
|
value: 'postal_coder',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'categories',
|
||||||
|
value: 'categories',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'cover',
|
||||||
|
value: 'cover',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'images',
|
||||||
|
value: 'images',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'city',
|
||||||
|
value: 'city',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
restaurant: {
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
label: 'Fields',
|
||||||
|
value: 'fields',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
label: 'f1',
|
||||||
|
value: 'f1',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'f2',
|
||||||
|
value: 'f2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// nested compo
|
||||||
|
label: 'services',
|
||||||
|
value: 'services',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
label: 'name',
|
||||||
|
value: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'media',
|
||||||
|
value: 'media',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'closing',
|
||||||
|
value: 'closing',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
label: 'name',
|
||||||
|
value: 'name',
|
||||||
|
children: [{ label: 'test', value: 'test' }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'dz',
|
||||||
|
value: 'dz',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'relation',
|
||||||
|
value: 'relation',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Locales',
|
||||||
|
value: 'locales',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
label: 'French',
|
||||||
|
value: 'fr',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'English',
|
||||||
|
required: true,
|
||||||
|
value: 'en',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
// test: {
|
||||||
|
// properties: [],
|
||||||
|
// },
|
||||||
|
},
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
label: 'Create',
|
||||||
|
actionId: 'content-manager.explorer.create',
|
||||||
|
subjects: ['restaurant', 'address'],
|
||||||
|
applyToProperties: ['fields', 'locales'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Read',
|
||||||
|
actionId: 'content-manager.explorer.read',
|
||||||
|
subjects: ['restaurant', 'addresse'],
|
||||||
|
applyToProperties: ['fields', 'locales'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Update',
|
||||||
|
actionId: 'content-manager.explorer.update',
|
||||||
|
subjects: ['addresse', 'restaurant'],
|
||||||
|
applyToProperties: ['fields'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Delete',
|
||||||
|
actionId: 'content-manager.explorer.delete',
|
||||||
|
subjects: ['restaurant', 'address'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Publish',
|
||||||
|
actionId: 'content-manager.explorer.publish',
|
||||||
|
subjects: ['restaurant'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default data;
|
||||||
Loading…
x
Reference in New Issue
Block a user