mirror of
https://github.com/strapi/strapi.git
synced 2025-09-19 21:38:05 +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 LinkToCTB from './LinkToCTB';
|
||||
|
||||
const DisplayedFields = ({
|
||||
attributes,
|
||||
editLayout,
|
||||
editLayoutRemainingFields,
|
||||
onRemoveField,
|
||||
onAddField,
|
||||
}) => {
|
||||
const DisplayedFields = ({ editLayout, editLayoutRemainingFields, onRemoveField, onAddField }) => {
|
||||
const { formatMessage } = useIntl();
|
||||
const { setEditFieldToSelect } = useLayoutDnd();
|
||||
const { setEditFieldToSelect, attributes, modifiedData } = useLayoutDnd();
|
||||
|
||||
return (
|
||||
<Stack size={4}>
|
||||
@ -56,6 +50,11 @@ const DisplayedFields = ({
|
||||
<Grid gap={4} key={row.rowId}>
|
||||
{row.rowContent.map((rowItem, index) => {
|
||||
const attribute = get(attributes, [rowItem.name], {});
|
||||
const attributeLabel = get(
|
||||
modifiedData,
|
||||
['metadatas', rowItem.name, 'edit', 'label'],
|
||||
''
|
||||
);
|
||||
|
||||
return (
|
||||
<GridItem key={rowItem.name} col={rowItem.size}>
|
||||
@ -65,7 +64,7 @@ const DisplayedFields = ({
|
||||
onDeleteField={() => onRemoveField(row.rowId, index)}
|
||||
attribute={attribute}
|
||||
>
|
||||
{rowItem.name}
|
||||
{attributeLabel || rowItem.name}
|
||||
</FieldButton>
|
||||
) : (
|
||||
<VisuallyHidden />
|
||||
@ -82,6 +81,7 @@ const DisplayedFields = ({
|
||||
defaultMessage: 'Insert another field',
|
||||
})}
|
||||
as={Button}
|
||||
data-testid="add-field"
|
||||
fullWidth
|
||||
startIcon={<Plus />}
|
||||
endIcon={null}
|
||||
@ -103,7 +103,6 @@ const DisplayedFields = ({
|
||||
DisplayedFields.propTypes = {
|
||||
editLayout: PropTypes.array.isRequired,
|
||||
editLayoutRemainingFields: PropTypes.array.isRequired,
|
||||
attributes: PropTypes.object.isRequired,
|
||||
onAddField: PropTypes.func.isRequired,
|
||||
onRemoveField: PropTypes.func.isRequired,
|
||||
};
|
||||
|
@ -15,6 +15,9 @@ import getTrad from '../../../utils/getTrad';
|
||||
|
||||
const CustomIconButton = styled(IconButton)`
|
||||
background-color: transparent;
|
||||
path {
|
||||
fill: ${({ theme }) => theme.colors.neutral600};
|
||||
}
|
||||
`;
|
||||
const CustomDragIcon = styled(Drag)`
|
||||
height: ${12 / 16}rem;
|
||||
@ -81,6 +84,7 @@ const FieldButton = ({ attribute, onEditField, onDeleteField, children }) => {
|
||||
target: children,
|
||||
}
|
||||
)}
|
||||
data-testid="delete-field"
|
||||
onClick={onDeleteField}
|
||||
icon={<Trash />}
|
||||
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 PropTypes from 'prop-types';
|
||||
import React, { useMemo, useCallback } from 'react';
|
||||
import get from 'lodash/get';
|
||||
import { GridItem } from '@strapi/design-system/Grid';
|
||||
import { useSelector, shallowEqual } from 'react-redux';
|
||||
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 { createPossibleMainFieldsForModelsAndComponents, getInputProps } from '../utils';
|
||||
import { makeSelectModelAndComponentSchemas } from '../../App/selectors';
|
||||
import getTrad from '../../../utils/getTrad';
|
||||
import GenericInput from './GenericInput';
|
||||
|
||||
// Create a file
|
||||
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 ModalForm = ({ onChange }) => {
|
||||
const { formatMessage } = useIntl();
|
||||
const { modifiedData, selectedField, attributes, fieldForm } = useLayoutDnd();
|
||||
const schemasSelector = useMemo(makeSelectModelAndComponentSchemas, []);
|
||||
const { schemas } = useSelector(state => schemasSelector(state), shallowEqual);
|
||||
|
||||
const getAttrType = () => {
|
||||
if (type === 'timestamp') {
|
||||
return 'date';
|
||||
const formToDisplay = useMemo(() => {
|
||||
if (!selectedField) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (['decimal', 'float', 'integer', 'biginter'].includes(type)) {
|
||||
return 'number';
|
||||
}
|
||||
const associatedMetas = get(modifiedData, ['metadatas', selectedField, 'edit'], {});
|
||||
|
||||
return type;
|
||||
};
|
||||
return Object.keys(associatedMetas).filter(meta => meta !== 'visible');
|
||||
}, [selectedField, modifiedData]);
|
||||
|
||||
return (
|
||||
<ModalLayout onClose={onToggle} labelledBy="title">
|
||||
<form onSubmit={onSubmit}>
|
||||
<ModalHeader>
|
||||
<HeaderContainer>
|
||||
{iconByTypes[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}>
|
||||
<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>
|
||||
}
|
||||
/>
|
||||
</form>
|
||||
</ModalLayout>
|
||||
const componentsAndModelsPossibleMainFields = useMemo(() => {
|
||||
return createPossibleMainFieldsForModelsAndComponents(schemas);
|
||||
}, [schemas]);
|
||||
|
||||
const getSelectedItemSelectOptions = useCallback(
|
||||
formType => {
|
||||
if (formType !== 'relation' && formType !== 'component') {
|
||||
return [];
|
||||
}
|
||||
|
||||
const targetKey = formType === 'component' ? 'component' : 'targetModel';
|
||||
const key = get(modifiedData, ['attributes', selectedField, targetKey], '');
|
||||
|
||||
return get(componentsAndModelsPossibleMainFields, [key], []);
|
||||
},
|
||||
|
||||
[selectedField, componentsAndModelsPossibleMainFields, modifiedData]
|
||||
);
|
||||
};
|
||||
|
||||
ModalForm.propTypes = {
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
onToggle: PropTypes.func.isRequired,
|
||||
type: PropTypes.string.isRequired,
|
||||
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 (
|
||||
<GridItem col={6} key={meta}>
|
||||
<GenericInput
|
||||
type={getInputProps(meta).type}
|
||||
hint={
|
||||
meta === 'mainField'
|
||||
? formatMessage({
|
||||
id: getTrad('containers.SettingPage.editSettings.relation-field.description'),
|
||||
})
|
||||
: ''
|
||||
}
|
||||
label={formatMessage({
|
||||
id: get(getInputProps(meta), 'label.id', 'app.utils.defaultMessage'),
|
||||
})}
|
||||
name={meta}
|
||||
onChange={onChange}
|
||||
value={get(fieldForm, meta, '')}
|
||||
options={getSelectedItemSelectOptions(formType)}
|
||||
/>
|
||||
</GridItem>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export default ModalForm;
|
||||
|
@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import get from 'lodash/get';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { Button } from '@strapi/design-system/Button';
|
||||
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 { getTrad } from '../../../utils';
|
||||
import FieldButton from './FieldButton';
|
||||
import { useLayoutDnd } from '../../../hooks';
|
||||
|
||||
const RelationalFields = ({
|
||||
relationsLayout,
|
||||
@ -17,6 +19,7 @@ const RelationalFields = ({
|
||||
onAddField,
|
||||
}) => {
|
||||
const { formatMessage } = useIntl();
|
||||
const { setEditFieldToSelect, modifiedData } = useLayoutDnd();
|
||||
|
||||
return (
|
||||
<Stack size={4}>
|
||||
@ -41,21 +44,30 @@ const RelationalFields = ({
|
||||
</div>
|
||||
<Box padding={4} hasRadius borderStyle="dashed" borderWidth="1px" borderColor="neutral300">
|
||||
<Stack size={2}>
|
||||
{relationsLayout.map((relationName, index) => (
|
||||
<FieldButton
|
||||
onEditField={() => console.log(relationName)}
|
||||
onDeleteField={() => onRemoveField(index)}
|
||||
key={relationName}
|
||||
>
|
||||
{relationName}
|
||||
</FieldButton>
|
||||
))}
|
||||
{relationsLayout.map((relationName, index) => {
|
||||
const relationLabel = get(
|
||||
modifiedData,
|
||||
['metadatas', relationName, 'edit', 'label'],
|
||||
''
|
||||
);
|
||||
|
||||
return (
|
||||
<FieldButton
|
||||
onEditField={() => setEditFieldToSelect(relationName)}
|
||||
onDeleteField={() => onRemoveField(index)}
|
||||
key={relationName}
|
||||
>
|
||||
{relationLabel || relationName}
|
||||
</FieldButton>
|
||||
);
|
||||
})}
|
||||
<SimpleMenu
|
||||
id="label"
|
||||
label={formatMessage({
|
||||
id: 'containers.SettingPage.add.relational-field',
|
||||
defaultMessage: 'Insert another relational field',
|
||||
})}
|
||||
data-testid="add-relation"
|
||||
as={Button}
|
||||
fullWidth
|
||||
startIcon={<Plus />}
|
||||
|
@ -1,10 +1,15 @@
|
||||
import React, { useReducer, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { useMutation } from 'react-query';
|
||||
import upperFirst from 'lodash/upperFirst';
|
||||
import pick from 'lodash/pick';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import flatMap from 'lodash/flatMap';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
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 { Main } from '@strapi/design-system/Main';
|
||||
import { HeaderLayout, ContentLayout } from '@strapi/design-system/Layout';
|
||||
@ -24,15 +29,20 @@ import reducer, { initialState } from './reducer';
|
||||
import init from './init';
|
||||
import DisplayedFields from './components/DisplayedFields';
|
||||
import RelationalFields from './components/RelationalFields';
|
||||
import ModalForm from './components/ModalForm';
|
||||
import ModalForm from './components/FormModal';
|
||||
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, () =>
|
||||
init(initialState, mainLayout, components)
|
||||
);
|
||||
const { trackUsage } = useTracking();
|
||||
const toggleNotification = useNotification();
|
||||
const { goBack } = useHistory();
|
||||
const [isModalFormOpen, setIsModalFormOpen] = useState(false);
|
||||
const [isConfirmDialogOpen, setIsConfirmDialogOpen] = useState(false);
|
||||
const { componentLayouts, initialData, modifiedData, metaToEdit, metaForm } = reducerState;
|
||||
const { formatMessage } = useIntl();
|
||||
const modelName = get(mainLayout, ['info', isContentTypeView ? 'displayName' : 'name'], '');
|
||||
@ -85,7 +95,60 @@ const EditSettingsView = ({ mainLayout, components, isContentTypeView, slug }) =
|
||||
};
|
||||
|
||||
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 (
|
||||
@ -115,135 +178,153 @@ const EditSettingsView = ({ mainLayout, components, isContentTypeView, slug }) =
|
||||
{ name: upperFirst(modelName) }
|
||||
)}
|
||||
/>
|
||||
<HeaderLayout
|
||||
title={formatMessage(
|
||||
{
|
||||
id: getTrad('components.SettingsViewWrapper.pluginHeader.title'),
|
||||
defaultMessage: `Configure the view - ${upperFirst(modelName)}`,
|
||||
},
|
||||
{ name: upperFirst(modelName) }
|
||||
)}
|
||||
subtitle={formatMessage({
|
||||
id: getTrad('components.SettingsViewWrapper.pluginHeader.description.edit-settings'),
|
||||
defaultMessage: 'Customize how the edit view will look like.',
|
||||
})}
|
||||
navigationAction={
|
||||
<Link
|
||||
startIcon={<ArrowLeft />}
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
goBack();
|
||||
}}
|
||||
to="/"
|
||||
>
|
||||
{formatMessage({
|
||||
id: 'app.components.go-back',
|
||||
defaultMessage: 'Go back',
|
||||
})}
|
||||
</Link>
|
||||
}
|
||||
primaryAction={
|
||||
<Button
|
||||
disabled={isEqual(initialData, modifiedData)}
|
||||
startIcon={<Check />}
|
||||
type="submit"
|
||||
>
|
||||
{formatMessage({ id: 'form.button.save', defaultMessage: 'Save' })}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<ContentLayout>
|
||||
<Box
|
||||
background="neutral0"
|
||||
hasRadius
|
||||
shadow="filterShadow"
|
||||
paddingTop={6}
|
||||
paddingBottom={6}
|
||||
paddingLeft={7}
|
||||
paddingRight={7}
|
||||
>
|
||||
<Stack size={4}>
|
||||
<H3 as="h2">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<HeaderLayout
|
||||
title={formatMessage(
|
||||
{
|
||||
id: getTrad('components.SettingsViewWrapper.pluginHeader.title'),
|
||||
defaultMessage: `Configure the view - ${upperFirst(modelName)}`,
|
||||
},
|
||||
{ name: upperFirst(modelName) }
|
||||
)}
|
||||
subtitle={formatMessage({
|
||||
id: getTrad('components.SettingsViewWrapper.pluginHeader.description.edit-settings'),
|
||||
defaultMessage: 'Customize how the edit view will look like.',
|
||||
})}
|
||||
navigationAction={
|
||||
<Link
|
||||
startIcon={<ArrowLeft />}
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
goBack();
|
||||
}}
|
||||
to="/"
|
||||
>
|
||||
{formatMessage({
|
||||
id: getTrad('containers.SettingPage.settings'),
|
||||
defaultMessage: 'Settings',
|
||||
id: 'app.components.go-back',
|
||||
defaultMessage: 'Go back',
|
||||
})}
|
||||
</H3>
|
||||
<Grid>
|
||||
<GridItem col={6} s={12}>
|
||||
<Select
|
||||
label={formatMessage({
|
||||
id: getTrad('containers.SettingPage.editSettings.entry.title'),
|
||||
defaultMessage: 'Entry title',
|
||||
})}
|
||||
hint={formatMessage({
|
||||
id: getTrad('containers.SettingPage.editSettings.entry.title.description'),
|
||||
defaultMessage: 'Set the display field of your entry',
|
||||
})}
|
||||
onChange={value => {
|
||||
handleChange({
|
||||
target: { name: 'settings.mainField', value: value === '' ? null : value },
|
||||
});
|
||||
}}
|
||||
value={modifiedData.settings.mainField}
|
||||
>
|
||||
{entryTitleOptions.map(attribute => (
|
||||
<Option key={attribute} value={attribute}>
|
||||
{attribute}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</GridItem>
|
||||
</Grid>
|
||||
<Box paddingTop={2} paddingBottom={2}>
|
||||
<Divider />
|
||||
</Box>
|
||||
<H3>
|
||||
{formatMessage({
|
||||
id: getTrad('containers.SettingPage.view'),
|
||||
defaultMessage: 'View',
|
||||
})}
|
||||
</H3>
|
||||
<Grid gap={4}>
|
||||
<GridItem col={isContentTypeView ? 8 : 12} s={12}>
|
||||
<DisplayedFields
|
||||
attributes={attributes}
|
||||
editLayout={editLayout}
|
||||
editLayoutRemainingFields={editLayoutRemainingFields}
|
||||
onAddField={field => {
|
||||
dispatch({
|
||||
type: 'ON_ADD_FIELD',
|
||||
name: field,
|
||||
});
|
||||
}}
|
||||
onRemoveField={(rowId, index) => {
|
||||
dispatch({
|
||||
type: 'REMOVE_FIELD',
|
||||
rowIndex: rowId,
|
||||
fieldIndex: index,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</GridItem>
|
||||
{isContentTypeView && (
|
||||
<GridItem col={4} s={12}>
|
||||
<RelationalFields
|
||||
editRelationsLayoutRemainingFields={editRelationsLayoutRemainingFields}
|
||||
relationsLayout={relationsLayout}
|
||||
onAddField={name => dispatch({ type: 'ADD_RELATION', name })}
|
||||
onRemoveField={index => dispatch({ type: 'REMOVE_RELATION', index })}
|
||||
</Link>
|
||||
}
|
||||
primaryAction={
|
||||
<Button
|
||||
disabled={isEqual(initialData, modifiedData)}
|
||||
startIcon={<Check />}
|
||||
type="submit"
|
||||
>
|
||||
{formatMessage({ id: 'form.button.save', defaultMessage: 'Save' })}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<ContentLayout>
|
||||
<Box
|
||||
background="neutral0"
|
||||
hasRadius
|
||||
shadow="filterShadow"
|
||||
paddingTop={6}
|
||||
paddingBottom={6}
|
||||
paddingLeft={7}
|
||||
paddingRight={7}
|
||||
>
|
||||
<Stack size={4}>
|
||||
<H3 as="h2">
|
||||
{formatMessage({
|
||||
id: getTrad('containers.SettingPage.settings'),
|
||||
defaultMessage: 'Settings',
|
||||
})}
|
||||
</H3>
|
||||
<Grid>
|
||||
<GridItem col={6} s={12}>
|
||||
<Select
|
||||
label={formatMessage({
|
||||
id: getTrad('containers.SettingPage.editSettings.entry.title'),
|
||||
defaultMessage: 'Entry title',
|
||||
})}
|
||||
hint={formatMessage({
|
||||
id: getTrad('containers.SettingPage.editSettings.entry.title.description'),
|
||||
defaultMessage: 'Set the display field of your entry',
|
||||
})}
|
||||
onChange={value => {
|
||||
handleChange({
|
||||
target: {
|
||||
name: 'settings.mainField',
|
||||
value: value === '' ? null : value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
value={modifiedData.settings.mainField}
|
||||
>
|
||||
{entryTitleOptions.map(attribute => (
|
||||
<Option key={attribute} value={attribute}>
|
||||
{attribute}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</GridItem>
|
||||
</Grid>
|
||||
<Box paddingTop={2} paddingBottom={2}>
|
||||
<Divider />
|
||||
</Box>
|
||||
<H3>
|
||||
{formatMessage({
|
||||
id: getTrad('containers.SettingPage.view'),
|
||||
defaultMessage: 'View',
|
||||
})}
|
||||
</H3>
|
||||
<Grid gap={4}>
|
||||
<GridItem col={isContentTypeView ? 8 : 12} s={12}>
|
||||
<DisplayedFields
|
||||
attributes={attributes}
|
||||
editLayout={editLayout}
|
||||
editLayoutRemainingFields={editLayoutRemainingFields}
|
||||
onAddField={field => {
|
||||
dispatch({
|
||||
type: 'ON_ADD_FIELD',
|
||||
name: field,
|
||||
});
|
||||
}}
|
||||
onRemoveField={(rowId, index) => {
|
||||
dispatch({
|
||||
type: 'REMOVE_FIELD',
|
||||
rowIndex: rowId,
|
||||
fieldIndex: index,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</GridItem>
|
||||
)}
|
||||
</Grid>
|
||||
</Stack>
|
||||
</Box>
|
||||
</ContentLayout>
|
||||
{isContentTypeView && (
|
||||
<GridItem col={4} s={12}>
|
||||
<RelationalFields
|
||||
editRelationsLayoutRemainingFields={editRelationsLayoutRemainingFields}
|
||||
relationsLayout={relationsLayout}
|
||||
onAddField={name => dispatch({ type: 'ADD_RELATION', name })}
|
||||
onRemoveField={index => dispatch({ type: 'REMOVE_RELATION', index })}
|
||||
/>
|
||||
</GridItem>
|
||||
)}
|
||||
</Grid>
|
||||
</Stack>
|
||||
</Box>
|
||||
</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 && (
|
||||
<ModalForm
|
||||
onSubmit={e => console.log(e)}
|
||||
onSubmit={handleMetaSubmit}
|
||||
onToggle={handleToggleModal}
|
||||
type={get(attributes, [metaToEdit, 'type'], '')}
|
||||
onChange={handleMetaChange}
|
||||
/>
|
||||
)}
|
||||
</Main>
|
||||
@ -253,6 +334,7 @@ const EditSettingsView = ({ mainLayout, components, isContentTypeView, slug }) =
|
||||
|
||||
EditSettingsView.defaultProps = {
|
||||
isContentTypeView: false,
|
||||
updateLayout: null,
|
||||
};
|
||||
|
||||
EditSettingsView.propTypes = {
|
||||
@ -270,6 +352,7 @@ EditSettingsView.propTypes = {
|
||||
options: PropTypes.object.isRequired,
|
||||
}).isRequired,
|
||||
slug: PropTypes.string.isRequired,
|
||||
updateLayout: PropTypes.func,
|
||||
};
|
||||
|
||||
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 { TextInput } from '@strapi/design-system/TextInput';
|
||||
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';
|
||||
|
||||
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 />,
|
||||
};
|
||||
import FieldTypeIcon from '../../../components/FieldTypeIcon';
|
||||
|
||||
const HeaderContainer = styled(Flex)`
|
||||
svg {
|
||||
@ -81,7 +50,7 @@ const EditFieldForm = ({
|
||||
<form onSubmit={onSubmit}>
|
||||
<ModalHeader>
|
||||
<HeaderContainer>
|
||||
{iconByTypes[type]}
|
||||
<FieldTypeIcon type={type} />
|
||||
<ButtonText textColor="neutral800" as="h2" id="title">
|
||||
{formatMessage(
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user