mirror of
https://github.com/strapi/strapi.git
synced 2025-09-20 14:00:48 +00:00
Merge pull request #11384 from strapi/edit-settings/modal-submit
Edit settings/modal submit
This commit is contained in:
commit
e381ff23f2
@ -0,0 +1,48 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import Date from '@strapi/icons/Date';
|
||||||
|
import Boolean from '@strapi/icons/Boolean';
|
||||||
|
import Email from '@strapi/icons/Email';
|
||||||
|
import Enumeration from '@strapi/icons/Enumeration';
|
||||||
|
import Media from '@strapi/icons/Media';
|
||||||
|
import Relation from '@strapi/icons/Relation';
|
||||||
|
import Text from '@strapi/icons/Text';
|
||||||
|
import Uid from '@strapi/icons/Uid';
|
||||||
|
import Number from '@strapi/icons/Number';
|
||||||
|
import Json from '@strapi/icons/Json';
|
||||||
|
import Component from '@strapi/icons/Component';
|
||||||
|
import DynamicZone from '@strapi/icons/DynamicZone';
|
||||||
|
|
||||||
|
const iconByTypes = {
|
||||||
|
biginteger: <Number />,
|
||||||
|
boolean: <Boolean />,
|
||||||
|
date: <Date />,
|
||||||
|
datetime: <Date />,
|
||||||
|
decimal: <Number />,
|
||||||
|
email: <Email />,
|
||||||
|
enum: <Enumeration />,
|
||||||
|
enumeration: <Enumeration />,
|
||||||
|
file: <Media />,
|
||||||
|
files: <Media />,
|
||||||
|
float: <Number />,
|
||||||
|
integer: <Number />,
|
||||||
|
media: <Media />,
|
||||||
|
number: <Number />,
|
||||||
|
relation: <Relation />,
|
||||||
|
string: <Text />,
|
||||||
|
text: <Text />,
|
||||||
|
time: <Date />,
|
||||||
|
timestamp: <Date />,
|
||||||
|
json: <Json />,
|
||||||
|
uid: <Uid />,
|
||||||
|
component: <Component />,
|
||||||
|
dynamiczone: <DynamicZone />,
|
||||||
|
};
|
||||||
|
|
||||||
|
const FieldTypeIcon = ({ type }) => iconByTypes[type];
|
||||||
|
|
||||||
|
FieldTypeIcon.propTypes = {
|
||||||
|
type: PropTypes.string.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FieldTypeIcon;
|
@ -16,15 +16,9 @@ import { useLayoutDnd } from '../../../hooks';
|
|||||||
import FieldButton from './FieldButton';
|
import FieldButton from './FieldButton';
|
||||||
import LinkToCTB from './LinkToCTB';
|
import LinkToCTB from './LinkToCTB';
|
||||||
|
|
||||||
const DisplayedFields = ({
|
const DisplayedFields = ({ editLayout, editLayoutRemainingFields, onRemoveField, onAddField }) => {
|
||||||
attributes,
|
|
||||||
editLayout,
|
|
||||||
editLayoutRemainingFields,
|
|
||||||
onRemoveField,
|
|
||||||
onAddField,
|
|
||||||
}) => {
|
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
const { setEditFieldToSelect } = useLayoutDnd();
|
const { setEditFieldToSelect, attributes, modifiedData } = useLayoutDnd();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack size={4}>
|
<Stack size={4}>
|
||||||
@ -56,6 +50,11 @@ const DisplayedFields = ({
|
|||||||
<Grid gap={4} key={row.rowId}>
|
<Grid gap={4} key={row.rowId}>
|
||||||
{row.rowContent.map((rowItem, index) => {
|
{row.rowContent.map((rowItem, index) => {
|
||||||
const attribute = get(attributes, [rowItem.name], {});
|
const attribute = get(attributes, [rowItem.name], {});
|
||||||
|
const attributeLabel = get(
|
||||||
|
modifiedData,
|
||||||
|
['metadatas', rowItem.name, 'edit', 'label'],
|
||||||
|
''
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GridItem key={rowItem.name} col={rowItem.size}>
|
<GridItem key={rowItem.name} col={rowItem.size}>
|
||||||
@ -65,7 +64,7 @@ const DisplayedFields = ({
|
|||||||
onDeleteField={() => onRemoveField(row.rowId, index)}
|
onDeleteField={() => onRemoveField(row.rowId, index)}
|
||||||
attribute={attribute}
|
attribute={attribute}
|
||||||
>
|
>
|
||||||
{rowItem.name}
|
{attributeLabel || rowItem.name}
|
||||||
</FieldButton>
|
</FieldButton>
|
||||||
) : (
|
) : (
|
||||||
<VisuallyHidden />
|
<VisuallyHidden />
|
||||||
@ -82,6 +81,7 @@ const DisplayedFields = ({
|
|||||||
defaultMessage: 'Insert another field',
|
defaultMessage: 'Insert another field',
|
||||||
})}
|
})}
|
||||||
as={Button}
|
as={Button}
|
||||||
|
data-testid="add-field"
|
||||||
fullWidth
|
fullWidth
|
||||||
startIcon={<Plus />}
|
startIcon={<Plus />}
|
||||||
endIcon={null}
|
endIcon={null}
|
||||||
@ -103,7 +103,6 @@ const DisplayedFields = ({
|
|||||||
DisplayedFields.propTypes = {
|
DisplayedFields.propTypes = {
|
||||||
editLayout: PropTypes.array.isRequired,
|
editLayout: PropTypes.array.isRequired,
|
||||||
editLayoutRemainingFields: PropTypes.array.isRequired,
|
editLayoutRemainingFields: PropTypes.array.isRequired,
|
||||||
attributes: PropTypes.object.isRequired,
|
|
||||||
onAddField: PropTypes.func.isRequired,
|
onAddField: PropTypes.func.isRequired,
|
||||||
onRemoveField: PropTypes.func.isRequired,
|
onRemoveField: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
@ -15,6 +15,9 @@ import getTrad from '../../../utils/getTrad';
|
|||||||
|
|
||||||
const CustomIconButton = styled(IconButton)`
|
const CustomIconButton = styled(IconButton)`
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
path {
|
||||||
|
fill: ${({ theme }) => theme.colors.neutral600};
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
const CustomDragIcon = styled(Drag)`
|
const CustomDragIcon = styled(Drag)`
|
||||||
height: ${12 / 16}rem;
|
height: ${12 / 16}rem;
|
||||||
@ -81,6 +84,7 @@ const FieldButton = ({ attribute, onEditField, onDeleteField, children }) => {
|
|||||||
target: children,
|
target: children,
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
|
data-testid="delete-field"
|
||||||
onClick={onDeleteField}
|
onClick={onDeleteField}
|
||||||
icon={<Trash />}
|
icon={<Trash />}
|
||||||
noBorder
|
noBorder
|
||||||
|
@ -0,0 +1,91 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { useIntl } from 'react-intl';
|
||||||
|
import upperFirst from 'lodash/upperFirst';
|
||||||
|
import {
|
||||||
|
ModalLayout,
|
||||||
|
ModalHeader,
|
||||||
|
ModalFooter,
|
||||||
|
ModalBody,
|
||||||
|
} from '@strapi/design-system/ModalLayout';
|
||||||
|
import { ButtonText } from '@strapi/design-system/Text';
|
||||||
|
import { Button } from '@strapi/design-system/Button';
|
||||||
|
import { Flex } from '@strapi/design-system/Flex';
|
||||||
|
import { Grid } from '@strapi/design-system/Grid';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { getTrad } from '../../../utils';
|
||||||
|
import { useLayoutDnd } from '../../../hooks';
|
||||||
|
import FieldTypeIcon from '../../../components/FieldTypeIcon';
|
||||||
|
import ModalForm from './ModalForm';
|
||||||
|
|
||||||
|
const HeaderContainer = styled(Flex)`
|
||||||
|
svg {
|
||||||
|
width: ${32 / 16}rem;
|
||||||
|
height: ${24 / 16}rem;
|
||||||
|
margin-right: ${({ theme }) => theme.spaces[3]};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const FormModal = ({ onToggle, onChange, onSubmit, type }) => {
|
||||||
|
const { selectedField } = useLayoutDnd();
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
|
const getAttrType = () => {
|
||||||
|
if (type === 'timestamp') {
|
||||||
|
return 'date';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (['decimal', 'float', 'integer', 'biginter'].includes(type)) {
|
||||||
|
return 'number';
|
||||||
|
}
|
||||||
|
|
||||||
|
return type;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalLayout onClose={onToggle} labelledBy="title">
|
||||||
|
<form onSubmit={onSubmit}>
|
||||||
|
<ModalHeader>
|
||||||
|
<HeaderContainer>
|
||||||
|
<FieldTypeIcon type={getAttrType(type)} />
|
||||||
|
<ButtonText textColor="neutral800" as="h2" id="title">
|
||||||
|
{formatMessage(
|
||||||
|
{
|
||||||
|
id: getTrad('containers.ListSettingsView.modal-form.edit-label'),
|
||||||
|
defaultMessage: 'Edit {fieldName}',
|
||||||
|
},
|
||||||
|
{ fieldName: upperFirst(selectedField) }
|
||||||
|
)}
|
||||||
|
</ButtonText>
|
||||||
|
</HeaderContainer>
|
||||||
|
</ModalHeader>
|
||||||
|
<ModalBody>
|
||||||
|
<Grid gap={4}>
|
||||||
|
<ModalForm onChange={onChange} />
|
||||||
|
</Grid>
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter
|
||||||
|
startActions={
|
||||||
|
<Button onClick={onToggle} variant="tertiary">
|
||||||
|
{formatMessage({ id: 'app.components.Button.cancel', defaultMessage: 'Cancel' })}
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
endActions={
|
||||||
|
<Button type="submit">
|
||||||
|
{formatMessage({ id: 'form.button.finish', defaultMessage: 'Finish' })}
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</ModalLayout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
FormModal.propTypes = {
|
||||||
|
onSubmit: PropTypes.func.isRequired,
|
||||||
|
onToggle: PropTypes.func.isRequired,
|
||||||
|
onChange: PropTypes.func.isRequired,
|
||||||
|
type: PropTypes.string.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FormModal;
|
@ -0,0 +1,68 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { TextInput } from '@strapi/design-system/TextInput';
|
||||||
|
import { ToggleInput } from '@strapi/design-system/ToggleInput';
|
||||||
|
import { Select, Option } from '@strapi/design-system/Select';
|
||||||
|
import { useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
const GenericInput = ({ type, options, onChange, value, name, ...inputProps }) => {
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 'text': {
|
||||||
|
return <TextInput onChange={onChange} value={value} name={name} {...inputProps} />;
|
||||||
|
}
|
||||||
|
case 'bool': {
|
||||||
|
return (
|
||||||
|
<ToggleInput
|
||||||
|
onChange={e => {
|
||||||
|
onChange({ target: { name, value: e.target.checked } });
|
||||||
|
}}
|
||||||
|
checked={value}
|
||||||
|
name={name}
|
||||||
|
onLabel={formatMessage({
|
||||||
|
id: 'app.components.ToggleCheckbox.on-label',
|
||||||
|
defaultMessage: 'On',
|
||||||
|
})}
|
||||||
|
offLabel={formatMessage({
|
||||||
|
id: 'app.components.ToggleCheckbox.off-label',
|
||||||
|
defaultMessage: 'Off',
|
||||||
|
})}
|
||||||
|
{...inputProps}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case 'select': {
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
value={value}
|
||||||
|
name={name}
|
||||||
|
onChange={value => onChange({ target: { name, value } })}
|
||||||
|
{...inputProps}
|
||||||
|
>
|
||||||
|
{options.map(option => (
|
||||||
|
<Option key={option} value={option}>
|
||||||
|
{option}
|
||||||
|
</Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
GenericInput.defaultProps = {
|
||||||
|
options: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
GenericInput.propTypes = {
|
||||||
|
type: PropTypes.string.isRequired,
|
||||||
|
options: PropTypes.arrayOf(PropTypes.string),
|
||||||
|
onChange: PropTypes.func.isRequired,
|
||||||
|
value: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]).isRequired,
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GenericInput;
|
@ -1,129 +1,90 @@
|
|||||||
import React from 'react';
|
import React, { useMemo, useCallback } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import get from 'lodash/get';
|
||||||
|
import { GridItem } from '@strapi/design-system/Grid';
|
||||||
|
import { useSelector, shallowEqual } from 'react-redux';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import upperFirst from 'lodash/upperFirst';
|
|
||||||
import {
|
|
||||||
ModalLayout,
|
|
||||||
ModalHeader,
|
|
||||||
ModalFooter,
|
|
||||||
ModalBody,
|
|
||||||
} from '@strapi/design-system/ModalLayout';
|
|
||||||
import { ButtonText } from '@strapi/design-system/Text';
|
|
||||||
import { Button } from '@strapi/design-system/Button';
|
|
||||||
import { Flex } from '@strapi/design-system/Flex';
|
|
||||||
import { Grid, GridItem } from '@strapi/design-system/Grid';
|
|
||||||
import Date from '@strapi/icons/Date';
|
|
||||||
import Boolean from '@strapi/icons/Boolean';
|
|
||||||
import Email from '@strapi/icons/Email';
|
|
||||||
import Enumeration from '@strapi/icons/Enumeration';
|
|
||||||
import Media from '@strapi/icons/Media';
|
|
||||||
import Relation from '@strapi/icons/Relation';
|
|
||||||
import Text from '@strapi/icons/Text';
|
|
||||||
import Uid from '@strapi/icons/Uid';
|
|
||||||
import Number from '@strapi/icons/Number';
|
|
||||||
import Json from '@strapi/icons/Json';
|
|
||||||
import Component from '@strapi/icons/Component';
|
|
||||||
import DynamicZone from '@strapi/icons/DynamicZone';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
import { getTrad } from '../../../utils';
|
|
||||||
import { useLayoutDnd } from '../../../hooks';
|
import { useLayoutDnd } from '../../../hooks';
|
||||||
|
import { createPossibleMainFieldsForModelsAndComponents, getInputProps } from '../utils';
|
||||||
|
import { makeSelectModelAndComponentSchemas } from '../../App/selectors';
|
||||||
|
import getTrad from '../../../utils/getTrad';
|
||||||
|
import GenericInput from './GenericInput';
|
||||||
|
|
||||||
// Create a file
|
const ModalForm = ({ onChange }) => {
|
||||||
const iconByTypes = {
|
|
||||||
biginteger: <Number />,
|
|
||||||
boolean: <Boolean />,
|
|
||||||
date: <Date />,
|
|
||||||
datetime: <Date />,
|
|
||||||
decimal: <Number />,
|
|
||||||
email: <Email />,
|
|
||||||
enum: <Enumeration />,
|
|
||||||
enumeration: <Enumeration />,
|
|
||||||
file: <Media />,
|
|
||||||
files: <Media />,
|
|
||||||
float: <Number />,
|
|
||||||
integer: <Number />,
|
|
||||||
media: <Media />,
|
|
||||||
number: <Number />,
|
|
||||||
relation: <Relation />,
|
|
||||||
string: <Text />,
|
|
||||||
text: <Text />,
|
|
||||||
time: <Date />,
|
|
||||||
timestamp: <Date />,
|
|
||||||
json: <Json />,
|
|
||||||
uid: <Uid />,
|
|
||||||
component: <Component />,
|
|
||||||
dynamiczone: <DynamicZone />,
|
|
||||||
};
|
|
||||||
|
|
||||||
const HeaderContainer = styled(Flex)`
|
|
||||||
svg {
|
|
||||||
width: ${32 / 16}rem;
|
|
||||||
height: ${24 / 16}rem;
|
|
||||||
margin-right: ${({ theme }) => theme.spaces[3]};
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const ModalForm = ({ onToggle, onSubmit, type }) => {
|
|
||||||
const { selectedField } = useLayoutDnd();
|
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
|
const { modifiedData, selectedField, attributes, fieldForm } = useLayoutDnd();
|
||||||
|
const schemasSelector = useMemo(makeSelectModelAndComponentSchemas, []);
|
||||||
|
const { schemas } = useSelector(state => schemasSelector(state), shallowEqual);
|
||||||
|
|
||||||
const getAttrType = () => {
|
const formToDisplay = useMemo(() => {
|
||||||
if (type === 'timestamp') {
|
if (!selectedField) {
|
||||||
return 'date';
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (['decimal', 'float', 'integer', 'biginter'].includes(type)) {
|
const associatedMetas = get(modifiedData, ['metadatas', selectedField, 'edit'], {});
|
||||||
return 'number';
|
|
||||||
|
return Object.keys(associatedMetas).filter(meta => meta !== 'visible');
|
||||||
|
}, [selectedField, modifiedData]);
|
||||||
|
|
||||||
|
const componentsAndModelsPossibleMainFields = useMemo(() => {
|
||||||
|
return createPossibleMainFieldsForModelsAndComponents(schemas);
|
||||||
|
}, [schemas]);
|
||||||
|
|
||||||
|
const getSelectedItemSelectOptions = useCallback(
|
||||||
|
formType => {
|
||||||
|
if (formType !== 'relation' && formType !== 'component') {
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return type;
|
const targetKey = formType === 'component' ? 'component' : 'targetModel';
|
||||||
};
|
const key = get(modifiedData, ['attributes', selectedField, targetKey], '');
|
||||||
|
|
||||||
|
return get(componentsAndModelsPossibleMainFields, [key], []);
|
||||||
|
},
|
||||||
|
|
||||||
|
[selectedField, componentsAndModelsPossibleMainFields, modifiedData]
|
||||||
|
);
|
||||||
|
|
||||||
|
return formToDisplay.map(meta => {
|
||||||
|
const formType = get(attributes, [selectedField, 'type']);
|
||||||
|
|
||||||
|
if (formType === 'dynamiczone' && !['label', 'description'].includes(meta)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((formType === 'component' || formType === 'media') && meta !== 'label') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((formType === 'json' || formType === 'boolean') && meta === 'placeholder') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formType === 'richtext' && meta === 'editable') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalLayout onClose={onToggle} labelledBy="title">
|
<GridItem col={6} key={meta}>
|
||||||
<form onSubmit={onSubmit}>
|
<GenericInput
|
||||||
<ModalHeader>
|
type={getInputProps(meta).type}
|
||||||
<HeaderContainer>
|
hint={
|
||||||
{iconByTypes[getAttrType(type)]}
|
meta === 'mainField'
|
||||||
<ButtonText textColor="neutral800" as="h2" id="title">
|
? formatMessage({
|
||||||
{formatMessage(
|
id: getTrad('containers.SettingPage.editSettings.relation-field.description'),
|
||||||
{
|
})
|
||||||
id: getTrad('containers.ListSettingsView.modal-form.edit-label'),
|
: ''
|
||||||
defaultMessage: 'Edit {fieldName}',
|
|
||||||
},
|
|
||||||
{ fieldName: upperFirst(selectedField) }
|
|
||||||
)}
|
|
||||||
</ButtonText>
|
|
||||||
</HeaderContainer>
|
|
||||||
</ModalHeader>
|
|
||||||
<ModalBody>
|
|
||||||
<Grid gap={4}>
|
|
||||||
<GridItem s={12} col={6}>
|
|
||||||
TO DO
|
|
||||||
</GridItem>
|
|
||||||
</Grid>
|
|
||||||
</ModalBody>
|
|
||||||
<ModalFooter
|
|
||||||
startActions={
|
|
||||||
<Button onClick={onToggle} variant="tertiary">
|
|
||||||
{formatMessage({ id: 'app.components.Button.cancel', defaultMessage: 'Cancel' })}
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
endActions={
|
|
||||||
<Button type="submit">
|
|
||||||
{formatMessage({ id: 'form.button.finish', defaultMessage: 'Finish' })}
|
|
||||||
</Button>
|
|
||||||
}
|
}
|
||||||
|
label={formatMessage({
|
||||||
|
id: get(getInputProps(meta), 'label.id', 'app.utils.defaultMessage'),
|
||||||
|
})}
|
||||||
|
name={meta}
|
||||||
|
onChange={onChange}
|
||||||
|
value={get(fieldForm, meta, '')}
|
||||||
|
options={getSelectedItemSelectOptions(formType)}
|
||||||
/>
|
/>
|
||||||
</form>
|
</GridItem>
|
||||||
</ModalLayout>
|
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
ModalForm.propTypes = {
|
|
||||||
onSubmit: PropTypes.func.isRequired,
|
|
||||||
onToggle: PropTypes.func.isRequired,
|
|
||||||
type: PropTypes.string.isRequired,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ModalForm;
|
export default ModalForm;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import get from 'lodash/get';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { Button } from '@strapi/design-system/Button';
|
import { Button } from '@strapi/design-system/Button';
|
||||||
import { Box } from '@strapi/design-system/Box';
|
import { Box } from '@strapi/design-system/Box';
|
||||||
@ -9,6 +10,7 @@ import { SimpleMenu, MenuItem } from '@strapi/design-system/SimpleMenu';
|
|||||||
import Plus from '@strapi/icons/Plus';
|
import Plus from '@strapi/icons/Plus';
|
||||||
import { getTrad } from '../../../utils';
|
import { getTrad } from '../../../utils';
|
||||||
import FieldButton from './FieldButton';
|
import FieldButton from './FieldButton';
|
||||||
|
import { useLayoutDnd } from '../../../hooks';
|
||||||
|
|
||||||
const RelationalFields = ({
|
const RelationalFields = ({
|
||||||
relationsLayout,
|
relationsLayout,
|
||||||
@ -17,6 +19,7 @@ const RelationalFields = ({
|
|||||||
onAddField,
|
onAddField,
|
||||||
}) => {
|
}) => {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
|
const { setEditFieldToSelect, modifiedData } = useLayoutDnd();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack size={4}>
|
<Stack size={4}>
|
||||||
@ -41,21 +44,30 @@ const RelationalFields = ({
|
|||||||
</div>
|
</div>
|
||||||
<Box padding={4} hasRadius borderStyle="dashed" borderWidth="1px" borderColor="neutral300">
|
<Box padding={4} hasRadius borderStyle="dashed" borderWidth="1px" borderColor="neutral300">
|
||||||
<Stack size={2}>
|
<Stack size={2}>
|
||||||
{relationsLayout.map((relationName, index) => (
|
{relationsLayout.map((relationName, index) => {
|
||||||
|
const relationLabel = get(
|
||||||
|
modifiedData,
|
||||||
|
['metadatas', relationName, 'edit', 'label'],
|
||||||
|
''
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
<FieldButton
|
<FieldButton
|
||||||
onEditField={() => console.log(relationName)}
|
onEditField={() => setEditFieldToSelect(relationName)}
|
||||||
onDeleteField={() => onRemoveField(index)}
|
onDeleteField={() => onRemoveField(index)}
|
||||||
key={relationName}
|
key={relationName}
|
||||||
>
|
>
|
||||||
{relationName}
|
{relationLabel || relationName}
|
||||||
</FieldButton>
|
</FieldButton>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
<SimpleMenu
|
<SimpleMenu
|
||||||
id="label"
|
id="label"
|
||||||
label={formatMessage({
|
label={formatMessage({
|
||||||
id: 'containers.SettingPage.add.relational-field',
|
id: 'containers.SettingPage.add.relational-field',
|
||||||
defaultMessage: 'Insert another relational field',
|
defaultMessage: 'Insert another relational field',
|
||||||
})}
|
})}
|
||||||
|
data-testid="add-relation"
|
||||||
as={Button}
|
as={Button}
|
||||||
fullWidth
|
fullWidth
|
||||||
startIcon={<Plus />}
|
startIcon={<Plus />}
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
import React, { useReducer, useState } from 'react';
|
import React, { useReducer, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
|
import { useMutation } from 'react-query';
|
||||||
import upperFirst from 'lodash/upperFirst';
|
import upperFirst from 'lodash/upperFirst';
|
||||||
|
import pick from 'lodash/pick';
|
||||||
|
import cloneDeep from 'lodash/cloneDeep';
|
||||||
import flatMap from 'lodash/flatMap';
|
import flatMap from 'lodash/flatMap';
|
||||||
import isEqual from 'lodash/isEqual';
|
import isEqual from 'lodash/isEqual';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
|
import set from 'lodash/set';
|
||||||
|
import { useNotification, useTracking, ConfirmDialog } from '@strapi/helper-plugin';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { Main } from '@strapi/design-system/Main';
|
import { Main } from '@strapi/design-system/Main';
|
||||||
import { HeaderLayout, ContentLayout } from '@strapi/design-system/Layout';
|
import { HeaderLayout, ContentLayout } from '@strapi/design-system/Layout';
|
||||||
@ -24,15 +29,20 @@ import reducer, { initialState } from './reducer';
|
|||||||
import init from './init';
|
import init from './init';
|
||||||
import DisplayedFields from './components/DisplayedFields';
|
import DisplayedFields from './components/DisplayedFields';
|
||||||
import RelationalFields from './components/RelationalFields';
|
import RelationalFields from './components/RelationalFields';
|
||||||
import ModalForm from './components/ModalForm';
|
import ModalForm from './components/FormModal';
|
||||||
import LayoutDndProvider from '../../components/LayoutDndProvider';
|
import LayoutDndProvider from '../../components/LayoutDndProvider';
|
||||||
|
import { unformatLayout } from './utils/layout';
|
||||||
|
import putCMSettingsEV from './utils/api';
|
||||||
|
|
||||||
const EditSettingsView = ({ mainLayout, components, isContentTypeView, slug }) => {
|
const EditSettingsView = ({ mainLayout, components, isContentTypeView, slug, updateLayout }) => {
|
||||||
const [reducerState, dispatch] = useReducer(reducer, initialState, () =>
|
const [reducerState, dispatch] = useReducer(reducer, initialState, () =>
|
||||||
init(initialState, mainLayout, components)
|
init(initialState, mainLayout, components)
|
||||||
);
|
);
|
||||||
|
const { trackUsage } = useTracking();
|
||||||
|
const toggleNotification = useNotification();
|
||||||
const { goBack } = useHistory();
|
const { goBack } = useHistory();
|
||||||
const [isModalFormOpen, setIsModalFormOpen] = useState(false);
|
const [isModalFormOpen, setIsModalFormOpen] = useState(false);
|
||||||
|
const [isConfirmDialogOpen, setIsConfirmDialogOpen] = useState(false);
|
||||||
const { componentLayouts, initialData, modifiedData, metaToEdit, metaForm } = reducerState;
|
const { componentLayouts, initialData, modifiedData, metaToEdit, metaForm } = reducerState;
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
const modelName = get(mainLayout, ['info', isContentTypeView ? 'displayName' : 'name'], '');
|
const modelName = get(mainLayout, ['info', isContentTypeView ? 'displayName' : 'name'], '');
|
||||||
@ -85,7 +95,60 @@ const EditSettingsView = ({ mainLayout, components, isContentTypeView, slug }) =
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleToggleModal = () => {
|
const handleToggleModal = () => {
|
||||||
setIsModalFormOpen(prevState => !prevState);
|
setIsModalFormOpen(prev => !prev);
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleConfirmDialog = () => {
|
||||||
|
setIsConfirmDialogOpen(prev => !prev);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMetaChange = ({ target: { name, value } }) => {
|
||||||
|
dispatch({
|
||||||
|
type: 'ON_CHANGE_META',
|
||||||
|
keys: name.split('.'),
|
||||||
|
value,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMetaSubmit = e => {
|
||||||
|
e.preventDefault();
|
||||||
|
dispatch({
|
||||||
|
type: 'SUBMIT_META_FORM',
|
||||||
|
});
|
||||||
|
handleToggleModal();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = e => {
|
||||||
|
e.preventDefault();
|
||||||
|
toggleConfirmDialog();
|
||||||
|
};
|
||||||
|
|
||||||
|
const submitMutation = useMutation(
|
||||||
|
body => {
|
||||||
|
return putCMSettingsEV(body, slug, isContentTypeView);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onSuccess: ({ data }) => {
|
||||||
|
if (updateLayout) {
|
||||||
|
updateLayout(data.data);
|
||||||
|
}
|
||||||
|
dispatch({
|
||||||
|
type: 'SUBMIT_SUCCEEDED',
|
||||||
|
});
|
||||||
|
toggleConfirmDialog();
|
||||||
|
trackUsage('didEditEditSettings');
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
toggleNotification({ type: 'warning', message: { id: 'notification.error' } });
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const { isLoading: isSubmittingForm } = submitMutation;
|
||||||
|
|
||||||
|
const handleConfirm = () => {
|
||||||
|
const body = pick(cloneDeep(modifiedData), ['layouts', 'metadatas', 'settings']);
|
||||||
|
set(body, 'layouts.edit', unformatLayout(body.layouts.edit));
|
||||||
|
submitMutation.mutate(body);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -115,6 +178,7 @@ const EditSettingsView = ({ mainLayout, components, isContentTypeView, slug }) =
|
|||||||
{ name: upperFirst(modelName) }
|
{ name: upperFirst(modelName) }
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
<HeaderLayout
|
<HeaderLayout
|
||||||
title={formatMessage(
|
title={formatMessage(
|
||||||
{
|
{
|
||||||
@ -182,7 +246,10 @@ const EditSettingsView = ({ mainLayout, components, isContentTypeView, slug }) =
|
|||||||
})}
|
})}
|
||||||
onChange={value => {
|
onChange={value => {
|
||||||
handleChange({
|
handleChange({
|
||||||
target: { name: 'settings.mainField', value: value === '' ? null : value },
|
target: {
|
||||||
|
name: 'settings.mainField',
|
||||||
|
value: value === '' ? null : value,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
value={modifiedData.settings.mainField}
|
value={modifiedData.settings.mainField}
|
||||||
@ -239,11 +306,25 @@ const EditSettingsView = ({ mainLayout, components, isContentTypeView, slug }) =
|
|||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
</ContentLayout>
|
</ContentLayout>
|
||||||
|
<ConfirmDialog
|
||||||
|
bodyText={{
|
||||||
|
id: getTrad('popUpWarning.warning.updateAllSettings'),
|
||||||
|
defaultMessage: 'This will modify all your settings',
|
||||||
|
}}
|
||||||
|
iconRightButton={<Check />}
|
||||||
|
isConfirmButtonLoading={isSubmittingForm}
|
||||||
|
isOpen={isConfirmDialogOpen}
|
||||||
|
onToggleDialog={toggleConfirmDialog}
|
||||||
|
onConfirm={handleConfirm}
|
||||||
|
variantRightButton="success-light"
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
{isModalFormOpen && (
|
{isModalFormOpen && (
|
||||||
<ModalForm
|
<ModalForm
|
||||||
onSubmit={e => console.log(e)}
|
onSubmit={handleMetaSubmit}
|
||||||
onToggle={handleToggleModal}
|
onToggle={handleToggleModal}
|
||||||
type={get(attributes, [metaToEdit, 'type'], '')}
|
type={get(attributes, [metaToEdit, 'type'], '')}
|
||||||
|
onChange={handleMetaChange}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Main>
|
</Main>
|
||||||
@ -253,6 +334,7 @@ const EditSettingsView = ({ mainLayout, components, isContentTypeView, slug }) =
|
|||||||
|
|
||||||
EditSettingsView.defaultProps = {
|
EditSettingsView.defaultProps = {
|
||||||
isContentTypeView: false,
|
isContentTypeView: false,
|
||||||
|
updateLayout: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
EditSettingsView.propTypes = {
|
EditSettingsView.propTypes = {
|
||||||
@ -270,6 +352,7 @@ EditSettingsView.propTypes = {
|
|||||||
options: PropTypes.object.isRequired,
|
options: PropTypes.object.isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
slug: PropTypes.string.isRequired,
|
slug: PropTypes.string.isRequired,
|
||||||
|
updateLayout: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default EditSettingsView;
|
export default EditSettingsView;
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,13 @@
|
|||||||
|
import { axiosInstance } from '../../../../core/utils';
|
||||||
|
import { getRequestUrl } from '../../../utils';
|
||||||
|
|
||||||
|
const putCMSettingsEV = (body, slug, isContentTypeView) => {
|
||||||
|
return axiosInstance.put(
|
||||||
|
getRequestUrl(
|
||||||
|
isContentTypeView ? `content-types/${slug}/configuration` : `components/${slug}/configuration`
|
||||||
|
),
|
||||||
|
body
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default putCMSettingsEV;
|
@ -15,39 +15,8 @@ import { Flex } from '@strapi/design-system/Flex';
|
|||||||
import { Grid, GridItem } from '@strapi/design-system/Grid';
|
import { Grid, GridItem } from '@strapi/design-system/Grid';
|
||||||
import { TextInput } from '@strapi/design-system/TextInput';
|
import { TextInput } from '@strapi/design-system/TextInput';
|
||||||
import { ToggleInput } from '@strapi/design-system/ToggleInput';
|
import { ToggleInput } from '@strapi/design-system/ToggleInput';
|
||||||
import Date from '@strapi/icons/Date';
|
|
||||||
import Boolean from '@strapi/icons/Boolean';
|
|
||||||
import Email from '@strapi/icons/Email';
|
|
||||||
import Enumeration from '@strapi/icons/Enumeration';
|
|
||||||
import Media from '@strapi/icons/Media';
|
|
||||||
import Relation from '@strapi/icons/Relation';
|
|
||||||
import Text from '@strapi/icons/Text';
|
|
||||||
import Uid from '@strapi/icons/Uid';
|
|
||||||
import Number from '@strapi/icons/Number';
|
|
||||||
import { getTrad } from '../../../utils';
|
import { getTrad } from '../../../utils';
|
||||||
|
import FieldTypeIcon from '../../../components/FieldTypeIcon';
|
||||||
const iconByTypes = {
|
|
||||||
biginteger: <Number />,
|
|
||||||
boolean: <Boolean />,
|
|
||||||
date: <Date />,
|
|
||||||
datetime: <Date />,
|
|
||||||
decimal: <Number />,
|
|
||||||
email: <Email />,
|
|
||||||
enum: <Enumeration />,
|
|
||||||
enumeration: <Enumeration />,
|
|
||||||
file: <Media />,
|
|
||||||
files: <Media />,
|
|
||||||
float: <Number />,
|
|
||||||
integer: <Number />,
|
|
||||||
media: <Media />,
|
|
||||||
number: <Number />,
|
|
||||||
relation: <Relation />,
|
|
||||||
string: <Text />,
|
|
||||||
text: <Text />,
|
|
||||||
time: <Date />,
|
|
||||||
timestamp: <Date />,
|
|
||||||
uid: <Uid />,
|
|
||||||
};
|
|
||||||
|
|
||||||
const HeaderContainer = styled(Flex)`
|
const HeaderContainer = styled(Flex)`
|
||||||
svg {
|
svg {
|
||||||
@ -81,7 +50,7 @@ const EditFieldForm = ({
|
|||||||
<form onSubmit={onSubmit}>
|
<form onSubmit={onSubmit}>
|
||||||
<ModalHeader>
|
<ModalHeader>
|
||||||
<HeaderContainer>
|
<HeaderContainer>
|
||||||
{iconByTypes[type]}
|
<FieldTypeIcon type={type} />
|
||||||
<ButtonText textColor="neutral800" as="h2" id="title">
|
<ButtonText textColor="neutral800" as="h2" id="title">
|
||||||
{formatMessage(
|
{formatMessage(
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user