mirror of
https://github.com/strapi/strapi.git
synced 2025-09-26 17:00:55 +00:00
Created conditions modal
Signed-off-by: soupette <cyril.lpz@gmail.com>
This commit is contained in:
parent
5a53675b8f
commit
95eeed0dec
@ -0,0 +1,11 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
height: 36px;
|
||||||
|
border-radius: 2px;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
background-color: ${({ theme, isGrey }) => (isGrey ? '#fafafb' : theme.main.colors.white)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default Wrapper;
|
@ -0,0 +1,83 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Text, Padded, Flex } from '@buffetjs/core';
|
||||||
|
import { useIntl } from 'react-intl';
|
||||||
|
// import { usePermissionsDataManager } from '../contexts/PermissionsDataManagerContext';
|
||||||
|
|
||||||
|
import ConditionsSelect from '../ConditionsSelect';
|
||||||
|
import Wrapper from './Wrapper';
|
||||||
|
|
||||||
|
const ActionRow = ({
|
||||||
|
arrayOfOptionsGroupedByCategory,
|
||||||
|
isGrey,
|
||||||
|
label,
|
||||||
|
name,
|
||||||
|
onCategoryChange,
|
||||||
|
onChange,
|
||||||
|
value,
|
||||||
|
}) => {
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Wrapper isGrey={isGrey}>
|
||||||
|
<Padded style={{ width: 200 }} top left right bottom size="sm">
|
||||||
|
<Flex>
|
||||||
|
<Text
|
||||||
|
lineHeight="19px"
|
||||||
|
color="grey"
|
||||||
|
fontSize="xs"
|
||||||
|
fontWeight="bold"
|
||||||
|
textTransform="uppercase"
|
||||||
|
>
|
||||||
|
{formatMessage({
|
||||||
|
id: 'Settings.permissions.conditions.can',
|
||||||
|
})}
|
||||||
|
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
title={label}
|
||||||
|
lineHeight="19px"
|
||||||
|
fontWeight="bold"
|
||||||
|
fontSize="xs"
|
||||||
|
textTransform="uppercase"
|
||||||
|
color="mediumBlue"
|
||||||
|
style={{ maxWidth: '60%' }}
|
||||||
|
ellipsis
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
lineHeight="19px"
|
||||||
|
color="grey"
|
||||||
|
fontSize="xs"
|
||||||
|
fontWeight="bold"
|
||||||
|
textTransform="uppercase"
|
||||||
|
>
|
||||||
|
|
||||||
|
{formatMessage({
|
||||||
|
id: 'Settings.permissions.conditions.when',
|
||||||
|
})}
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
</Padded>
|
||||||
|
<ConditionsSelect
|
||||||
|
arrayOfOptionsGroupedByCategory={arrayOfOptionsGroupedByCategory}
|
||||||
|
name={name}
|
||||||
|
onCategoryChange={onCategoryChange}
|
||||||
|
onChange={onChange}
|
||||||
|
value={value}
|
||||||
|
/>
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ActionRow.propTypes = {
|
||||||
|
arrayOfOptionsGroupedByCategory: PropTypes.array.isRequired,
|
||||||
|
isGrey: PropTypes.bool.isRequired,
|
||||||
|
label: PropTypes.string.isRequired,
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
value: PropTypes.object.isRequired,
|
||||||
|
onCategoryChange: PropTypes.func.isRequired,
|
||||||
|
onChange: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
export default ActionRow;
|
@ -0,0 +1,15 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Remove } from '@buffetjs/icons';
|
||||||
|
import { components } from 'react-select';
|
||||||
|
|
||||||
|
const ClearIndicator = props => {
|
||||||
|
const Component = components.ClearIndicator;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Component {...props}>
|
||||||
|
<Remove width="11px" height="11px" fill="#9EA7B8" />
|
||||||
|
</Component>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ClearIndicator;
|
@ -0,0 +1,39 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
import { Flex } from '@buffetjs/core';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
const Wrapper = styled(Flex)`
|
||||||
|
height: 100%;
|
||||||
|
width: 32px;
|
||||||
|
background: #fafafb;
|
||||||
|
> svg {
|
||||||
|
align-self: center;
|
||||||
|
font-size: 11px;
|
||||||
|
color: #b3b5b9;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const DropdownIndicator = ({ selectProps: { menuIsOpen } }) => {
|
||||||
|
const icon = menuIsOpen ? 'caret-up' : 'caret-down';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Wrapper>
|
||||||
|
<FontAwesomeIcon icon={icon} />
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
DropdownIndicator.propTypes = {
|
||||||
|
selectProps: PropTypes.shape({
|
||||||
|
menuIsOpen: PropTypes.bool.isRequired,
|
||||||
|
}).isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
Wrapper.defaultProps = {
|
||||||
|
flexDirection: 'column',
|
||||||
|
justifyContent: 'center',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DropdownIndicator;
|
@ -0,0 +1,3 @@
|
|||||||
|
const IndicatorSeparator = () => null;
|
||||||
|
|
||||||
|
export default IndicatorSeparator;
|
@ -0,0 +1,48 @@
|
|||||||
|
/* eslint-disable jsx-a11y/click-events-have-key-events */
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { Collapse } from 'reactstrap';
|
||||||
|
|
||||||
|
const ToggleUl = styled(Collapse)`
|
||||||
|
font-size: 13px;
|
||||||
|
padding: 12px 15px 0 15px;
|
||||||
|
list-style: none;
|
||||||
|
background-color: #fff;
|
||||||
|
> li {
|
||||||
|
padding-top: 5px;
|
||||||
|
label {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-wrapper {
|
||||||
|
z-index: 9;
|
||||||
|
> input {
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
> li:not(:last-child) {
|
||||||
|
padding-bottom: 12px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const SubUl = ({ children, isOpen }) => {
|
||||||
|
return (
|
||||||
|
<ToggleUl tag="ul" isOpen={isOpen}>
|
||||||
|
{children}
|
||||||
|
</ToggleUl>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
SubUl.defaultProps = {
|
||||||
|
children: null,
|
||||||
|
isOpen: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
SubUl.propTypes = {
|
||||||
|
children: PropTypes.node,
|
||||||
|
isOpen: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SubUl;
|
@ -0,0 +1,80 @@
|
|||||||
|
/* eslint-disable indent */
|
||||||
|
/* eslint-disable jsx-a11y/click-events-have-key-events */
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const Ul = styled.ul`
|
||||||
|
max-height: 150px;
|
||||||
|
font-size: 13px;
|
||||||
|
padding: 0 15px;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
list-style: none;
|
||||||
|
background-color: #fff;
|
||||||
|
> li {
|
||||||
|
label {
|
||||||
|
flex-shrink: 1;
|
||||||
|
width: fit-content !important;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-wrapper {
|
||||||
|
z-index: 9;
|
||||||
|
> input {
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.chevron {
|
||||||
|
margin: auto;
|
||||||
|
|
||||||
|
font-size: 11px;
|
||||||
|
color: #919bae;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.li-multi-menu {
|
||||||
|
margin-bottom: -3px;
|
||||||
|
}
|
||||||
|
.li {
|
||||||
|
line-height: 27px;
|
||||||
|
position: relative;
|
||||||
|
> p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
> p::after {
|
||||||
|
content: attr(datadescr);
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
color: #007eff;
|
||||||
|
font-weight: 700;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
top: 0;
|
||||||
|
left: -30px;
|
||||||
|
right: -30px;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: #e6f0fb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${({ disabled, theme }) =>
|
||||||
|
disabled &&
|
||||||
|
`
|
||||||
|
label {
|
||||||
|
cursor: not-allowed !important;
|
||||||
|
}
|
||||||
|
input[type='checkbox'] {
|
||||||
|
cursor: not-allowed;
|
||||||
|
&:after {
|
||||||
|
cursor: not-allowed;
|
||||||
|
color: ${theme.main.colors.grey};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default Ul;
|
@ -0,0 +1,6 @@
|
|||||||
|
/* eslint-disable jsx-a11y/click-events-have-key-events */
|
||||||
|
import { upperFirst } from 'lodash';
|
||||||
|
|
||||||
|
const UpperFirst = ({ content }) => upperFirst(content);
|
||||||
|
|
||||||
|
export default UpperFirst;
|
@ -0,0 +1,139 @@
|
|||||||
|
/* eslint-disable jsx-a11y/click-events-have-key-events */
|
||||||
|
import React, { useState, useMemo } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { components } from 'react-select';
|
||||||
|
import { get } from 'lodash';
|
||||||
|
import { Checkbox, Flex } from '@buffetjs/core';
|
||||||
|
import { Label } from '@buffetjs/styles';
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
import SubUl from './SubUl';
|
||||||
|
import Ul from './Ul';
|
||||||
|
import UpperFirst from './UpperFirst';
|
||||||
|
import { getCheckboxState } from '../../../utils';
|
||||||
|
import createCollapsesObject from './utils/createCollapsesObject';
|
||||||
|
|
||||||
|
/* eslint-disable jsx-a11y/no-static-element-interactions */
|
||||||
|
|
||||||
|
const MenuList = ({ selectProps, ...rest }) => {
|
||||||
|
const Component = components.MenuList;
|
||||||
|
const { arrayOfOptionsGroupedByCategory } = selectProps;
|
||||||
|
|
||||||
|
const initCollapses = useMemo(() => createCollapsesObject(arrayOfOptionsGroupedByCategory), [
|
||||||
|
arrayOfOptionsGroupedByCategory,
|
||||||
|
]);
|
||||||
|
const [collapses, setCollapses] = useState(initCollapses);
|
||||||
|
|
||||||
|
const toggleCollapse = collapseName => {
|
||||||
|
setCollapses(prevState => ({ ...prevState, [collapseName]: !collapses[collapseName] }));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Component {...rest}>
|
||||||
|
<Ul
|
||||||
|
// TODO
|
||||||
|
disabled={false}
|
||||||
|
>
|
||||||
|
{arrayOfOptionsGroupedByCategory.map((category, index) => {
|
||||||
|
const [categoryName, conditions] = category;
|
||||||
|
const checkboxName = `${selectProps.name}..${categoryName}`;
|
||||||
|
|
||||||
|
const {
|
||||||
|
hasAllActionsSelected: hasAllConditionsSelected,
|
||||||
|
hasSomeActionsSelected: hasSomeConditionsSelected,
|
||||||
|
} = getCheckboxState(selectProps.value);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li key={categoryName}>
|
||||||
|
<div>
|
||||||
|
<Flex justifyContent="space-between">
|
||||||
|
<Label
|
||||||
|
htmlFor="overrideReactSelectBehavior"
|
||||||
|
onClick={() =>
|
||||||
|
selectProps.onCategoryChange({
|
||||||
|
keys: [selectProps.name, categoryName],
|
||||||
|
value: !hasAllConditionsSelected,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Flex>
|
||||||
|
<Checkbox
|
||||||
|
// TODO
|
||||||
|
disabled={false}
|
||||||
|
id="checkCategory"
|
||||||
|
// TODO
|
||||||
|
name={checkboxName}
|
||||||
|
onChange={() => {}}
|
||||||
|
someChecked={hasSomeConditionsSelected}
|
||||||
|
value={hasAllConditionsSelected}
|
||||||
|
/>
|
||||||
|
<UpperFirst content={categoryName} />
|
||||||
|
</Flex>
|
||||||
|
</Label>
|
||||||
|
<div
|
||||||
|
style={{ flex: 1, textAlign: 'end', cursor: 'pointer' }}
|
||||||
|
onClick={() => toggleCollapse(categoryName)}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
style={{
|
||||||
|
margin: 'auto',
|
||||||
|
fontSize: '11px',
|
||||||
|
color: '#919bae',
|
||||||
|
}}
|
||||||
|
icon={collapses[categoryName] ? 'chevron-up' : 'chevron-down'}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Flex>
|
||||||
|
</div>
|
||||||
|
<SubUl tag="ul" isOpen={collapses[categoryName]}>
|
||||||
|
{conditions.map(condition => {
|
||||||
|
const checkboxValue = get(selectProps.value, [categoryName, condition.id], false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li key={condition.id}>
|
||||||
|
<Flex>
|
||||||
|
<Label
|
||||||
|
htmlFor={condition.id}
|
||||||
|
message={condition.displayName}
|
||||||
|
onClick={() => {
|
||||||
|
selectProps.onChange({
|
||||||
|
keys: [selectProps.name, categoryName, condition.id],
|
||||||
|
value: !checkboxValue,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Flex>
|
||||||
|
<Checkbox
|
||||||
|
id="check"
|
||||||
|
name={condition.id}
|
||||||
|
// Remove the handler
|
||||||
|
onChange={() => {}}
|
||||||
|
value={checkboxValue}
|
||||||
|
/>
|
||||||
|
{condition.displayName}
|
||||||
|
</Flex>
|
||||||
|
</Label>
|
||||||
|
</Flex>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</SubUl>
|
||||||
|
{index + 1 < arrayOfOptionsGroupedByCategory.length.length && (
|
||||||
|
<div style={{ paddingTop: '17px' }} />
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Ul>
|
||||||
|
</Component>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
MenuList.propTypes = {
|
||||||
|
selectProps: PropTypes.shape({
|
||||||
|
arrayOfOptionsGroupedByCategory: PropTypes.array.isRequired,
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
onCategoryChange: PropTypes.func.isRequired,
|
||||||
|
onChange: PropTypes.func.isRequired,
|
||||||
|
value: PropTypes.object.isRequired,
|
||||||
|
}).isRequired,
|
||||||
|
};
|
||||||
|
export default MenuList;
|
@ -0,0 +1,8 @@
|
|||||||
|
const createCollapsesObject = arrayOfCategories =>
|
||||||
|
arrayOfCategories.reduce((acc, current, index) => {
|
||||||
|
acc[current[0]] = index === 0;
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
export default createCollapsesObject;
|
@ -0,0 +1,46 @@
|
|||||||
|
/* eslint-disable indent */
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { components } from 'react-select';
|
||||||
|
import { Text } from '@buffetjs/core';
|
||||||
|
import { useIntl } from 'react-intl';
|
||||||
|
import { createArrayOfValues } from '../../utils';
|
||||||
|
|
||||||
|
const Value = ({ children, selectProps, ...props }) => {
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
const SingleValue = components.SingleValue;
|
||||||
|
const valuesArray = createArrayOfValues(selectProps.value).filter(val => val);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SingleValue {...props}>
|
||||||
|
<Text style={{ paddingTop: 1 }}>
|
||||||
|
{valuesArray.length === 0
|
||||||
|
? 'Anytime'
|
||||||
|
: formatMessage(
|
||||||
|
{
|
||||||
|
id: `Settings.permissions.conditions.selected.${
|
||||||
|
selectProps.value.length > 1 ? 'plural' : 'singular'
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
{ number: valuesArray.length }
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
</SingleValue>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Value.defaultProps = {
|
||||||
|
children: null,
|
||||||
|
selectProps: {
|
||||||
|
value: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Value.propTypes = {
|
||||||
|
children: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
|
||||||
|
selectProps: PropTypes.shape({
|
||||||
|
value: PropTypes.object,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Value;
|
@ -0,0 +1,11 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
import { Option } from '@buffetjs/core';
|
||||||
|
|
||||||
|
const StyledOption = styled(Option)`
|
||||||
|
> span {
|
||||||
|
display: block !important;
|
||||||
|
color: #007eff !important;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default StyledOption;
|
@ -0,0 +1,69 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import Select from 'react-select';
|
||||||
|
import { useIntl } from 'react-intl';
|
||||||
|
import MenuList from './MenuList';
|
||||||
|
import ClearIndicator from './ClearIndicator';
|
||||||
|
import DropdownIndicator from './CustomDropdownIndicator';
|
||||||
|
import IndicatorSeparator from './IndicatorSeparator';
|
||||||
|
import SingleValue from './SingleValue';
|
||||||
|
import selectStyle from '../utils/selectStyle';
|
||||||
|
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
padding-left: 30px;
|
||||||
|
width: 60%;
|
||||||
|
cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'default')};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ConditionsSelect = ({
|
||||||
|
arrayOfOptionsGroupedByCategory,
|
||||||
|
name,
|
||||||
|
onCategoryChange,
|
||||||
|
onChange,
|
||||||
|
value,
|
||||||
|
}) => {
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Wrapper
|
||||||
|
// TODO
|
||||||
|
disabled={false}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
components={{
|
||||||
|
ClearIndicator,
|
||||||
|
DropdownIndicator,
|
||||||
|
IndicatorSeparator,
|
||||||
|
SingleValue,
|
||||||
|
MenuList,
|
||||||
|
}}
|
||||||
|
arrayOfOptionsGroupedByCategory={arrayOfOptionsGroupedByCategory}
|
||||||
|
// TODO
|
||||||
|
isDisabled={false}
|
||||||
|
name={name}
|
||||||
|
onChange={onChange}
|
||||||
|
isClearable={false}
|
||||||
|
isLoading={false}
|
||||||
|
closeMenuOnSelect={false}
|
||||||
|
isSearchable={false}
|
||||||
|
hideSelectedOptions={false}
|
||||||
|
placeholder={formatMessage({ id: 'Settings.permissions.conditions.anytime' })}
|
||||||
|
onCategoryChange={onCategoryChange}
|
||||||
|
options={[]}
|
||||||
|
styles={selectStyle}
|
||||||
|
value={value}
|
||||||
|
/>
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ConditionsSelect.propTypes = {
|
||||||
|
arrayOfOptionsGroupedByCategory: PropTypes.array.isRequired,
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
onCategoryChange: PropTypes.func.isRequired,
|
||||||
|
onChange: PropTypes.func.isRequired,
|
||||||
|
value: PropTypes.object.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ConditionsSelect;
|
@ -1,12 +1,59 @@
|
|||||||
import React from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import { cloneDeep, get, groupBy, set } from 'lodash';
|
||||||
import { Modal, ModalHeader, ModalFooter } from 'strapi-helper-plugin';
|
import { Modal, ModalHeader, ModalFooter } from 'strapi-helper-plugin';
|
||||||
import { Button, Text, Padded } from '@buffetjs/core';
|
import { Button, Text, Padded } from '@buffetjs/core';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
|
import createDefaultConditionsForm from './utils/createDefaultConditionsForm';
|
||||||
|
import ActionRow from './ActionRow';
|
||||||
import Separator from './Separator';
|
import Separator from './Separator';
|
||||||
|
import { usePermissionsDataManager } from '../contexts/PermissionsDataManagerContext';
|
||||||
|
import updateValues from '../Permissions/utils/updateValues';
|
||||||
|
|
||||||
const ConditionsModal = ({ headerBreadCrumbs, isOpen, onClosed, onToggle }) => {
|
const ConditionsModal = ({ actions, headerBreadCrumbs, isOpen, onClosed, onToggle }) => {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
|
const { availableConditions, modifiedData } = usePermissionsDataManager();
|
||||||
|
|
||||||
|
const arrayOfOptionsGroupedByCategory = useMemo(() => {
|
||||||
|
return Object.entries(groupBy(availableConditions, 'category'));
|
||||||
|
}, [availableConditions]);
|
||||||
|
|
||||||
|
const actionsToDisplay = actions.filter(
|
||||||
|
({ isDisplayed, hasSomeActionsSelected, hasAllActionsSelected }) =>
|
||||||
|
isDisplayed && (hasSomeActionsSelected || hasAllActionsSelected)
|
||||||
|
);
|
||||||
|
|
||||||
|
const initState = useMemo(() => {
|
||||||
|
return createDefaultConditionsForm(
|
||||||
|
actionsToDisplay,
|
||||||
|
modifiedData,
|
||||||
|
arrayOfOptionsGroupedByCategory
|
||||||
|
);
|
||||||
|
}, [actionsToDisplay, modifiedData, arrayOfOptionsGroupedByCategory]);
|
||||||
|
|
||||||
|
const [state, setState] = useState(initState);
|
||||||
|
|
||||||
|
const handleCategoryChange = ({ keys, value }) => {
|
||||||
|
setState(prevState => {
|
||||||
|
const updatedState = cloneDeep(prevState);
|
||||||
|
const objToUpdate = get(prevState, keys, {});
|
||||||
|
const updatedValues = updateValues(objToUpdate, value);
|
||||||
|
|
||||||
|
set(updatedState, keys, updatedValues);
|
||||||
|
|
||||||
|
return updatedState;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChange = ({ keys, value }) => {
|
||||||
|
setState(prevState => {
|
||||||
|
const updatedState = cloneDeep(prevState);
|
||||||
|
|
||||||
|
set(updatedState, keys, value);
|
||||||
|
|
||||||
|
return updatedState;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal withoverflow="true" onClosed={onClosed} isOpen={isOpen} onToggle={onToggle}>
|
<Modal withoverflow="true" onClosed={onClosed} isOpen={isOpen} onToggle={onToggle}>
|
||||||
@ -18,20 +65,27 @@ const ConditionsModal = ({ headerBreadCrumbs, isOpen, onClosed, onToggle }) => {
|
|||||||
})}
|
})}
|
||||||
</Text>
|
</Text>
|
||||||
<Separator />
|
<Separator />
|
||||||
{/* {actions.length === 0 && (
|
{actionsToDisplay.length === 0 && (
|
||||||
<Text fontSize="md" color="grey">
|
<Text fontSize="md" color="grey">
|
||||||
{formatMessage({ id: 'Settings.permissions.conditions.no-actions' })}
|
{formatMessage({ id: 'Settings.permissions.conditions.no-actions' })}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
{actions.map((action, index) => (
|
{actionsToDisplay.map(({ actionId, label, pathToConditionsObject }, index) => {
|
||||||
|
const name = pathToConditionsObject.join('..');
|
||||||
|
|
||||||
|
return (
|
||||||
<ActionRow
|
<ActionRow
|
||||||
key={action.id}
|
key={actionId}
|
||||||
action={action}
|
arrayOfOptionsGroupedByCategory={arrayOfOptionsGroupedByCategory}
|
||||||
|
label={label}
|
||||||
isGrey={index % 2 === 0}
|
isGrey={index % 2 === 0}
|
||||||
value={conditions[action.id]}
|
name={name}
|
||||||
onChange={val => handleSelectChange(action.id, val)}
|
onCategoryChange={handleCategoryChange}
|
||||||
|
onChange={handleChange}
|
||||||
|
value={get(state, name, {})}
|
||||||
/>
|
/>
|
||||||
))} */}
|
);
|
||||||
|
})}
|
||||||
</Padded>
|
</Padded>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<section>
|
<section>
|
||||||
@ -51,6 +105,16 @@ const ConditionsModal = ({ headerBreadCrumbs, isOpen, onClosed, onToggle }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
ConditionsModal.propTypes = {
|
ConditionsModal.propTypes = {
|
||||||
|
actions: PropTypes.arrayOf(
|
||||||
|
PropTypes.shape({
|
||||||
|
actionId: PropTypes.string.isRequired,
|
||||||
|
checkboxName: PropTypes.string,
|
||||||
|
hasSomeActionsSelected: PropTypes.bool.isRequired,
|
||||||
|
hasAllActionsSelected: PropTypes.bool,
|
||||||
|
isDisplayed: PropTypes.bool.isRequired,
|
||||||
|
label: PropTypes.string,
|
||||||
|
})
|
||||||
|
).isRequired,
|
||||||
headerBreadCrumbs: PropTypes.arrayOf(PropTypes.string).isRequired,
|
headerBreadCrumbs: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||||
isOpen: PropTypes.bool.isRequired,
|
isOpen: PropTypes.bool.isRequired,
|
||||||
onClosed: PropTypes.func.isRequired,
|
onClosed: PropTypes.func.isRequired,
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
import { get } from 'lodash';
|
||||||
|
|
||||||
|
const createConditionsForm = (conditions, valueObject) => {
|
||||||
|
return conditions.reduce((acc, current) => {
|
||||||
|
acc[current.id] = get(valueObject, current.id, true);
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
};
|
||||||
|
|
||||||
|
const createCategoryForm = (arrayOfOptions, valueObject) => {
|
||||||
|
return arrayOfOptions.reduce((acc, current) => {
|
||||||
|
const [categoryName, relatedConditions] = current;
|
||||||
|
|
||||||
|
const conditionsForm = createConditionsForm(relatedConditions, valueObject);
|
||||||
|
|
||||||
|
acc[categoryName] = conditionsForm;
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
};
|
||||||
|
|
||||||
|
const createDefaultConditionsForm = (
|
||||||
|
actionsToDisplay,
|
||||||
|
modifiedData,
|
||||||
|
arrayOfOptionsGroupedByCategory
|
||||||
|
) => {
|
||||||
|
return actionsToDisplay.reduce((acc, current) => {
|
||||||
|
const valueFromModifiedData = get(
|
||||||
|
modifiedData,
|
||||||
|
[...current.pathToConditionsObject, 'conditions'],
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
const categoryDefaultForm = createCategoryForm(
|
||||||
|
arrayOfOptionsGroupedByCategory,
|
||||||
|
valueFromModifiedData
|
||||||
|
);
|
||||||
|
|
||||||
|
acc[current.pathToConditionsObject.join('..')] = categoryDefaultForm;
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default createDefaultConditionsForm;
|
@ -0,0 +1,95 @@
|
|||||||
|
/* eslint-disable indent */
|
||||||
|
/* eslint-disable no-nested-ternary */
|
||||||
|
|
||||||
|
const selectStyle = {
|
||||||
|
container: base => ({
|
||||||
|
...base,
|
||||||
|
width: '70%',
|
||||||
|
alignItems: 'center',
|
||||||
|
height: '36px',
|
||||||
|
}),
|
||||||
|
menu: base => ({
|
||||||
|
...base,
|
||||||
|
margin: '0',
|
||||||
|
paddingTop: 0,
|
||||||
|
borderRadius: '2px !important',
|
||||||
|
borderTopLeftRadius: '0 !important',
|
||||||
|
borderTopRightRadius: '0 !important',
|
||||||
|
border: '1px solid #78caff !important',
|
||||||
|
boxShadow: 0,
|
||||||
|
borderTop: '0 !important',
|
||||||
|
fontSize: '13px',
|
||||||
|
}),
|
||||||
|
menuList: base => ({
|
||||||
|
...base,
|
||||||
|
paddingBottom: 9,
|
||||||
|
paddingTop: 10,
|
||||||
|
}),
|
||||||
|
multiValue: base => ({
|
||||||
|
...base,
|
||||||
|
backgroundColor: 'none',
|
||||||
|
color: '#333740',
|
||||||
|
}),
|
||||||
|
multiValueLabel: base => ({
|
||||||
|
...base,
|
||||||
|
fontSize: '13px',
|
||||||
|
}),
|
||||||
|
multiValueRemove: base => ({
|
||||||
|
...base,
|
||||||
|
display: 'none',
|
||||||
|
}),
|
||||||
|
|
||||||
|
control: (base, state) => {
|
||||||
|
const borderRadiusStyle = state.selectProps.menuIsOpen
|
||||||
|
? {
|
||||||
|
borderBottomLeftRadius: '0 !important',
|
||||||
|
borderBottomRightRadius: '0 !important',
|
||||||
|
}
|
||||||
|
: {};
|
||||||
|
|
||||||
|
const {
|
||||||
|
selectProps: { error, value },
|
||||||
|
} = state;
|
||||||
|
|
||||||
|
let border;
|
||||||
|
let borderBottom;
|
||||||
|
|
||||||
|
if (state.isFocused) {
|
||||||
|
border = '1px solid #78caff !important';
|
||||||
|
} else if (error && !value.length) {
|
||||||
|
border = '1px solid #f64d0a !important';
|
||||||
|
} else {
|
||||||
|
border = '1px solid #e3e9f3 !important';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.menuIsOpen === true) {
|
||||||
|
borderBottom = '1px solid #e3e9f3 !important';
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...base,
|
||||||
|
fontSize: 13,
|
||||||
|
minHeight: 34,
|
||||||
|
top: '1px',
|
||||||
|
border,
|
||||||
|
outline: 0,
|
||||||
|
boxShadow: 0,
|
||||||
|
borderRadius: '2px !important',
|
||||||
|
...borderRadiusStyle,
|
||||||
|
borderBottom,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
valueContainer: base => ({
|
||||||
|
...base,
|
||||||
|
padding: '2px 4px 4px 10px',
|
||||||
|
lineHeight: '18px',
|
||||||
|
minWidth: 200,
|
||||||
|
}),
|
||||||
|
placeholder: base => ({
|
||||||
|
...base,
|
||||||
|
paddingTop: 1,
|
||||||
|
color: 'black',
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default selectStyle;
|
@ -108,6 +108,7 @@ const Collapse = ({ availableActions, isActive, isGrey, label, onClickToggle, pa
|
|||||||
{modalState.isMounted && (
|
{modalState.isMounted && (
|
||||||
<ConditionsModal
|
<ConditionsModal
|
||||||
headerBreadCrumbs={[label, 'app.components.LeftMenuLinkContainer.settings']}
|
headerBreadCrumbs={[label, 'app.components.LeftMenuLinkContainer.settings']}
|
||||||
|
actions={checkboxesActions}
|
||||||
isOpen={modalState.isOpen}
|
isOpen={modalState.isOpen}
|
||||||
onClosed={handleModalClose}
|
onClosed={handleModalClose}
|
||||||
onToggle={handleToggleModalIsOpen}
|
onToggle={handleToggleModalIsOpen}
|
||||||
|
@ -2,7 +2,7 @@ import { get, isEmpty } from 'lodash';
|
|||||||
import { createArrayOfValues, getCheckboxState } from '../../../utils';
|
import { createArrayOfValues, getCheckboxState } from '../../../utils';
|
||||||
|
|
||||||
const generateCheckboxesActions = (availableActions, modifiedData, pathToData) => {
|
const generateCheckboxesActions = (availableActions, modifiedData, pathToData) => {
|
||||||
return availableActions.map(({ actionId, isDisplayed, applyToProperties }) => {
|
return availableActions.map(({ actionId, isDisplayed, applyToProperties, label }) => {
|
||||||
if (!isDisplayed) {
|
if (!isDisplayed) {
|
||||||
return { actionId, hasSomeActionsSelected: false, isDisplayed };
|
return { actionId, hasSomeActionsSelected: false, isDisplayed };
|
||||||
}
|
}
|
||||||
@ -29,6 +29,8 @@ const generateCheckboxesActions = (availableActions, modifiedData, pathToData) =
|
|||||||
hasSomeActionsSelected: value,
|
hasSomeActionsSelected: value,
|
||||||
isDisplayed,
|
isDisplayed,
|
||||||
isParentCheckbox: false,
|
isParentCheckbox: false,
|
||||||
|
label,
|
||||||
|
pathToConditionsObject: baseCheckboxNameArray,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,6 +46,8 @@ const generateCheckboxesActions = (availableActions, modifiedData, pathToData) =
|
|||||||
hasSomeActionsSelected,
|
hasSomeActionsSelected,
|
||||||
isDisplayed,
|
isDisplayed,
|
||||||
isParentCheckbox: true,
|
isParentCheckbox: true,
|
||||||
|
label,
|
||||||
|
pathToConditionsObject: baseCheckboxNameArray,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -68,6 +68,7 @@ const Permissions = forwardRef(({ layout }, ref) => {
|
|||||||
return (
|
return (
|
||||||
<PermissionsDataManagerProvider
|
<PermissionsDataManagerProvider
|
||||||
value={{
|
value={{
|
||||||
|
availableConditions: layout.conditions,
|
||||||
modifiedData,
|
modifiedData,
|
||||||
onChangeSimpleCheckbox: handleChangeSimpleCheckbox,
|
onChangeSimpleCheckbox: handleChangeSimpleCheckbox,
|
||||||
onChangeParentCheckbox: handleChangeParentCheckbox,
|
onChangeParentCheckbox: handleChangeParentCheckbox,
|
||||||
|
@ -16,6 +16,7 @@ const usePermissionsDataManager = () => useContext(PermissionsDataManagerContext
|
|||||||
PermissionsDataManagerProvider.propTypes = {
|
PermissionsDataManagerProvider.propTypes = {
|
||||||
children: PropTypes.node.isRequired,
|
children: PropTypes.node.isRequired,
|
||||||
value: PropTypes.exact({
|
value: PropTypes.exact({
|
||||||
|
availableConditions: PropTypes.array.isRequired,
|
||||||
modifiedData: PropTypes.object.isRequired,
|
modifiedData: PropTypes.object.isRequired,
|
||||||
onChangeCollectionTypeLeftActionRowCheckbox: PropTypes.func.isRequired,
|
onChangeCollectionTypeLeftActionRowCheckbox: PropTypes.func.isRequired,
|
||||||
onChangeSimpleCheckbox: PropTypes.func.isRequired,
|
onChangeSimpleCheckbox: PropTypes.func.isRequired,
|
||||||
|
@ -424,6 +424,7 @@ const data = {
|
|||||||
label: 'Publish',
|
label: 'Publish',
|
||||||
actionId: 'content-manager.explorer.publish',
|
actionId: 'content-manager.explorer.publish',
|
||||||
subjects: ['restaurant'],
|
subjects: ['restaurant'],
|
||||||
|
applyToProperties: ['locales'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user