mirror of
				https://github.com/strapi/strapi.git
				synced 2025-11-04 03:43:34 +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 { formatMessage } = useIntl();
 | 
			
		||||
  const [selectedTabIndex, setSelectedTabIndex] = useState(0);
 | 
			
		||||
  const selectedChild = React.Children.toArray(children)[selectedTabIndex];
 | 
			
		||||
 | 
			
		||||
  const handleSelectedTab = index => {
 | 
			
		||||
    if (index !== selectedTabIndex) {
 | 
			
		||||
@ -38,7 +39,7 @@ const Tabs = ({ children, isLoading, tabsLabel }) => {
 | 
			
		||||
              </Tab>
 | 
			
		||||
            ))}
 | 
			
		||||
          </Flex>
 | 
			
		||||
          {children[selectedTabIndex]}
 | 
			
		||||
          {selectedChild}
 | 
			
		||||
        </>
 | 
			
		||||
      )}
 | 
			
		||||
    </TabsWrapper>
 | 
			
		||||
 | 
			
		||||
@ -23,15 +23,15 @@ window.strapi = Object.assign(window.strapi || {}, {
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
  'strapi-plugin-documentation': require('../../../strapi-plugin-documentation/admin/src').default,
 | 
			
		||||
  'strapi-plugin-users-permissions': require('../../../strapi-plugin-users-permissions/admin/src')
 | 
			
		||||
    .default,
 | 
			
		||||
  'strapi-plugin-content-manager': require('../../../strapi-plugin-content-manager/admin/src')
 | 
			
		||||
    .default,
 | 
			
		||||
  'strapi-plugin-content-type-builder': require('../../../strapi-plugin-content-type-builder/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-graphql': require('../../../strapi-plugin-graphql/admin/src').default,
 | 
			
		||||
  'strapi-plugin-i18n': require('../../../strapi-plugin-i18n/admin/src').default,
 | 
			
		||||
  // 'strapi-plugin-documentation': require('../../../strapi-plugin-documentation/admin/src').default,
 | 
			
		||||
  // 'strapi-plugin-users-permissions': require('../../../strapi-plugin-users-permissions/admin/src')
 | 
			
		||||
  //   .default,
 | 
			
		||||
  // 'strapi-plugin-content-manager': require('../../../strapi-plugin-content-manager/admin/src')
 | 
			
		||||
  //   .default,
 | 
			
		||||
  // 'strapi-plugin-content-type-builder': require('../../../strapi-plugin-content-type-builder/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-graphql': require('../../../strapi-plugin-graphql/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.input.description": "Description",
 | 
			
		||||
  "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.create": "Create",
 | 
			
		||||
  "Settings.roles.form.permissions.delete": "Delete",
 | 
			
		||||
 | 
			
		||||
@ -1,83 +1,83 @@
 | 
			
		||||
import ar from './ar.json';
 | 
			
		||||
import cs from './cs.json';
 | 
			
		||||
import de from './de.json';
 | 
			
		||||
import dk from './dk.json';
 | 
			
		||||
// import ar from './ar.json';
 | 
			
		||||
// import cs from './cs.json';
 | 
			
		||||
// import de from './de.json';
 | 
			
		||||
// import dk from './dk.json';
 | 
			
		||||
import en from './en.json';
 | 
			
		||||
import es from './es.json';
 | 
			
		||||
// import es from './es.json';
 | 
			
		||||
import fr from './fr.json';
 | 
			
		||||
import he from './he.json';
 | 
			
		||||
import id from './id.json';
 | 
			
		||||
import it from './it.json';
 | 
			
		||||
import ja from './ja.json';
 | 
			
		||||
import ko from './ko.json';
 | 
			
		||||
import ms from './ms.json';
 | 
			
		||||
import nl from './nl.json';
 | 
			
		||||
import pl from './pl.json';
 | 
			
		||||
import ptBR from './pt-BR.json';
 | 
			
		||||
import pt from './pt.json';
 | 
			
		||||
import ru from './ru.json';
 | 
			
		||||
import th from './th.json';
 | 
			
		||||
import tr from './tr.json';
 | 
			
		||||
import vi from './vi.json';
 | 
			
		||||
import zhHans from './zh-Hans.json';
 | 
			
		||||
import zh from './zh.json';
 | 
			
		||||
import sk from './sk.json';
 | 
			
		||||
import uk from './uk.json';
 | 
			
		||||
// import he from './he.json';
 | 
			
		||||
// import id from './id.json';
 | 
			
		||||
// import it from './it.json';
 | 
			
		||||
// import ja from './ja.json';
 | 
			
		||||
// import ko from './ko.json';
 | 
			
		||||
// import ms from './ms.json';
 | 
			
		||||
// import nl from './nl.json';
 | 
			
		||||
// import pl from './pl.json';
 | 
			
		||||
// import ptBR from './pt-BR.json';
 | 
			
		||||
// import pt from './pt.json';
 | 
			
		||||
// import ru from './ru.json';
 | 
			
		||||
// import th from './th.json';
 | 
			
		||||
// import tr from './tr.json';
 | 
			
		||||
// import vi from './vi.json';
 | 
			
		||||
// import zhHans from './zh-Hans.json';
 | 
			
		||||
// import zh from './zh.json';
 | 
			
		||||
// import sk from './sk.json';
 | 
			
		||||
// import uk from './uk.json';
 | 
			
		||||
 | 
			
		||||
const trads = {
 | 
			
		||||
  ar,
 | 
			
		||||
  cs,
 | 
			
		||||
  de,
 | 
			
		||||
  dk,
 | 
			
		||||
  // ar,
 | 
			
		||||
  // cs,
 | 
			
		||||
  // de,
 | 
			
		||||
  // dk,
 | 
			
		||||
  en,
 | 
			
		||||
  es,
 | 
			
		||||
  // es,
 | 
			
		||||
  fr,
 | 
			
		||||
  he,
 | 
			
		||||
  id,
 | 
			
		||||
  it,
 | 
			
		||||
  ja,
 | 
			
		||||
  ko,
 | 
			
		||||
  ms,
 | 
			
		||||
  nl,
 | 
			
		||||
  pl,
 | 
			
		||||
  'pt-BR': ptBR,
 | 
			
		||||
  pt,
 | 
			
		||||
  ru,
 | 
			
		||||
  sk,
 | 
			
		||||
  th,
 | 
			
		||||
  tr,
 | 
			
		||||
  uk,
 | 
			
		||||
  vi,
 | 
			
		||||
  'zh-Hans': zhHans,
 | 
			
		||||
  zh,
 | 
			
		||||
  // he,
 | 
			
		||||
  // id,
 | 
			
		||||
  // it,
 | 
			
		||||
  // ja,
 | 
			
		||||
  // ko,
 | 
			
		||||
  // ms,
 | 
			
		||||
  // nl,
 | 
			
		||||
  // pl,
 | 
			
		||||
  // 'pt-BR': ptBR,
 | 
			
		||||
  // pt,
 | 
			
		||||
  // ru,
 | 
			
		||||
  // sk,
 | 
			
		||||
  // th,
 | 
			
		||||
  // tr,
 | 
			
		||||
  // uk,
 | 
			
		||||
  // vi,
 | 
			
		||||
  // 'zh-Hans': zhHans,
 | 
			
		||||
  // zh,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const languageNativeNames = {
 | 
			
		||||
  ar: 'العربية',
 | 
			
		||||
  cs: 'Čeština',
 | 
			
		||||
  de: 'Deutsch',
 | 
			
		||||
  dk: 'Dansk',
 | 
			
		||||
  // ar: 'العربية',
 | 
			
		||||
  // cs: 'Čeština',
 | 
			
		||||
  // de: 'Deutsch',
 | 
			
		||||
  // dk: 'Dansk',
 | 
			
		||||
  en: 'English',
 | 
			
		||||
  es: 'Español',
 | 
			
		||||
  // es: 'Español',
 | 
			
		||||
  fr: 'Français',
 | 
			
		||||
  he: 'עברית',
 | 
			
		||||
  id: 'Indonesian',
 | 
			
		||||
  it: 'Italiano',
 | 
			
		||||
  ja: '日本語',
 | 
			
		||||
  ko: '한국어',
 | 
			
		||||
  ms: 'Melayu',
 | 
			
		||||
  nl: 'Nederlands',
 | 
			
		||||
  pl: 'Polski',
 | 
			
		||||
  'pt-BR': 'Português (Brasil)',
 | 
			
		||||
  pt: 'Português (Portugal)',
 | 
			
		||||
  ru: 'Русский',
 | 
			
		||||
  sk: 'Slovenčina',
 | 
			
		||||
  th: 'ไทย',
 | 
			
		||||
  tr: 'Türkçe',
 | 
			
		||||
  uk: 'Українська',
 | 
			
		||||
  vi: 'Tiếng Việt',
 | 
			
		||||
  'zh-Hans': '中文 (简体)',
 | 
			
		||||
  zh: '中文 (繁體)',
 | 
			
		||||
  // he: 'עברית',
 | 
			
		||||
  // id: 'Indonesian',
 | 
			
		||||
  // it: 'Italiano',
 | 
			
		||||
  // ja: '日本語',
 | 
			
		||||
  // ko: '한국어',
 | 
			
		||||
  // ms: 'Melayu',
 | 
			
		||||
  // nl: 'Nederlands',
 | 
			
		||||
  // pl: 'Polski',
 | 
			
		||||
  // 'pt-BR': 'Português (Brasil)',
 | 
			
		||||
  // pt: 'Português (Portugal)',
 | 
			
		||||
  // ru: 'Русский',
 | 
			
		||||
  // sk: 'Slovenčina',
 | 
			
		||||
  // th: 'ไทย',
 | 
			
		||||
  // tr: 'Türkçe',
 | 
			
		||||
  // uk: 'Українська',
 | 
			
		||||
  // vi: 'Tiếng Việt',
 | 
			
		||||
  // 'zh-Hans': '中文 (简体)',
 | 
			
		||||
  // zh: '中文 (繁體)',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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 ContainerFluid from '../../../../../admin/src/components/ContainerFluid';
 | 
			
		||||
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 { formatPermissionsToApi } from '../../../../../admin/src/utils';
 | 
			
		||||
import Permissions from './Permissions';
 | 
			
		||||
 | 
			
		||||
import schema from './utils/schema';
 | 
			
		||||
 | 
			
		||||
@ -31,8 +32,15 @@ const CreatePage = () => {
 | 
			
		||||
  const { emitEvent, settingsBaseURL } = useGlobalContext();
 | 
			
		||||
  const params = useRouteMatch(`${settingsBaseURL}/roles/duplicate/:id`);
 | 
			
		||||
  const id = get(params, 'params.id', null);
 | 
			
		||||
  const { isLoading: isLayoutLoading, data: permissionsLayout } = useFetchPermissionsLayout();
 | 
			
		||||
  const { role, permissions: rolePermissions, isLoading: isRoleLoading } = useFetchRole(id);
 | 
			
		||||
  const {
 | 
			
		||||
    isLoading: isLayoutLoading,
 | 
			
		||||
    // data: permissionsLayout
 | 
			
		||||
  } = useFetchPermissionsLayout();
 | 
			
		||||
  const {
 | 
			
		||||
    // role, permissions: rolePermissions,
 | 
			
		||||
    isLoading: isRoleLoading,
 | 
			
		||||
  } = useFetchRole(id);
 | 
			
		||||
  // console.log({ role, rolePermissions });
 | 
			
		||||
 | 
			
		||||
  const headerActions = (handleSubmit, handleReset) => [
 | 
			
		||||
    {
 | 
			
		||||
@ -191,10 +199,10 @@ const CreatePage = () => {
 | 
			
		||||
              {!isLayoutLoading && !isRoleLoading && (
 | 
			
		||||
                <Padded top bottom size="md">
 | 
			
		||||
                  <Permissions
 | 
			
		||||
                    permissionsLayout={permissionsLayout}
 | 
			
		||||
                    // permissionsLayout={permissionsLayout}
 | 
			
		||||
                    ref={permissionsRef}
 | 
			
		||||
                    rolePermissions={rolePermissions}
 | 
			
		||||
                    role={role}
 | 
			
		||||
                    // rolePermissions={rolePermissions}
 | 
			
		||||
                    // role={role}
 | 
			
		||||
                  />
 | 
			
		||||
                </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