mirror of
https://github.com/strapi/strapi.git
synced 2025-11-13 16:52:18 +00:00
Merge pull request #5286 from strapi/single-types/uid
add Uid attributes in CTB
This commit is contained in:
commit
da7ebced8d
@ -7,12 +7,7 @@ import {
|
|||||||
useGlobalContext,
|
useGlobalContext,
|
||||||
PopUpWarning,
|
PopUpWarning,
|
||||||
} from 'strapi-helper-plugin';
|
} from 'strapi-helper-plugin';
|
||||||
import {
|
import { useHistory, useLocation, useRouteMatch, Redirect } from 'react-router-dom';
|
||||||
useHistory,
|
|
||||||
useLocation,
|
|
||||||
useRouteMatch,
|
|
||||||
Redirect,
|
|
||||||
} from 'react-router-dom';
|
|
||||||
import DataManagerContext from '../../contexts/DataManagerContext';
|
import DataManagerContext from '../../contexts/DataManagerContext';
|
||||||
import getTrad from '../../utils/getTrad';
|
import getTrad from '../../utils/getTrad';
|
||||||
import makeUnique from '../../utils/makeUnique';
|
import makeUnique from '../../utils/makeUnique';
|
||||||
@ -55,21 +50,17 @@ const DataManagerProvider = ({ allIcons, children }) => {
|
|||||||
} = reducerState.toJS();
|
} = reducerState.toJS();
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
const { push } = useHistory();
|
const { push } = useHistory();
|
||||||
const contentTypeMatch = useRouteMatch(
|
const contentTypeMatch = useRouteMatch(`/plugins/${pluginId}/content-types/:uid`);
|
||||||
`/plugins/${pluginId}/content-types/:uid`
|
|
||||||
);
|
|
||||||
const componentMatch = useRouteMatch(
|
const componentMatch = useRouteMatch(
|
||||||
`/plugins/${pluginId}/component-categories/:categoryUid/:componentUid`
|
`/plugins/${pluginId}/component-categories/:categoryUid/:componentUid`
|
||||||
);
|
);
|
||||||
|
|
||||||
const formatMessageRef = useRef();
|
const formatMessageRef = useRef();
|
||||||
formatMessageRef.current = formatMessage;
|
formatMessageRef.current = formatMessage;
|
||||||
const isInDevelopmentMode =
|
const isInDevelopmentMode = currentEnvironment === 'development' && autoReload;
|
||||||
currentEnvironment === 'development' && autoReload;
|
|
||||||
|
|
||||||
const isInContentTypeView = contentTypeMatch !== null;
|
const isInContentTypeView = contentTypeMatch !== null;
|
||||||
const firstKeyToMainSchema = isInContentTypeView
|
const firstKeyToMainSchema = isInContentTypeView ? 'contentType' : 'component';
|
||||||
? 'contentType'
|
|
||||||
: 'component';
|
|
||||||
const currentUid = isInContentTypeView
|
const currentUid = isInContentTypeView
|
||||||
? get(contentTypeMatch, 'params.uid', null)
|
? get(contentTypeMatch, 'params.uid', null)
|
||||||
: get(componentMatch, 'params.componentUid', null);
|
: get(componentMatch, 'params.componentUid', null);
|
||||||
@ -80,10 +71,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
|
|||||||
|
|
||||||
getDataRef.current = async () => {
|
getDataRef.current = async () => {
|
||||||
try {
|
try {
|
||||||
const [
|
const [{ data: componentsArray }, { data: contentTypesArray }] = await Promise.all(
|
||||||
{ data: componentsArray },
|
|
||||||
{ data: contentTypesArray },
|
|
||||||
] = await Promise.all(
|
|
||||||
['components', 'content-types'].map(endPoint => {
|
['components', 'content-types'].map(endPoint => {
|
||||||
return request(`/${pluginId}/${endPoint}`, {
|
return request(`/${pluginId}/${endPoint}`, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
@ -118,11 +106,11 @@ const DataManagerProvider = ({ allIcons, children }) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// We need to set the modifiedData after the data has been retrieved
|
// We need to set the modifiedData after the data has been retrieved
|
||||||
// and also on pathname change
|
// and also on pathname change
|
||||||
if (!isLoading) {
|
if (!isLoading && currentUid) {
|
||||||
setModifiedData();
|
setModifiedData();
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [isLoading, pathname]);
|
}, [isLoading, pathname, currentUid]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (currentEnvironment === 'development' && !autoReload) {
|
if (currentEnvironment === 'development' && !autoReload) {
|
||||||
@ -135,8 +123,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
|
|||||||
}, [autoReload, currentEnvironment]);
|
}, [autoReload, currentEnvironment]);
|
||||||
|
|
||||||
const didModifiedComponents =
|
const didModifiedComponents =
|
||||||
getCreatedAndModifiedComponents(modifiedData.components || {}, components)
|
getCreatedAndModifiedComponents(modifiedData.components || {}, components).length > 0;
|
||||||
.length > 0;
|
|
||||||
|
|
||||||
const addAttribute = (
|
const addAttribute = (
|
||||||
attributeToSet,
|
attributeToSet,
|
||||||
@ -158,10 +145,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const addCreatedComponentToDynamicZone = (
|
const addCreatedComponentToDynamicZone = (dynamicZoneTarget, componentsToAdd) => {
|
||||||
dynamicZoneTarget,
|
|
||||||
componentsToAdd
|
|
||||||
) => {
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'ADD_CREATED_COMPONENT_TO_DYNAMIC_ZONE',
|
type: 'ADD_CREATED_COMPONENT_TO_DYNAMIC_ZONE',
|
||||||
dynamicZoneTarget,
|
dynamicZoneTarget,
|
||||||
@ -181,10 +165,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
|
|||||||
componentCategory,
|
componentCategory,
|
||||||
shouldAddComponentToData = false
|
shouldAddComponentToData = false
|
||||||
) => {
|
) => {
|
||||||
const type =
|
const type = schemaType === 'contentType' ? 'CREATE_SCHEMA' : 'CREATE_COMPONENT_SCHEMA';
|
||||||
schemaType === 'contentType'
|
|
||||||
? 'CREATE_SCHEMA'
|
|
||||||
: 'CREATE_COMPONENT_SCHEMA';
|
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type,
|
type,
|
||||||
@ -204,15 +185,9 @@ const DataManagerProvider = ({ allIcons, children }) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeAttribute = (
|
const removeAttribute = (mainDataKey, attributeToRemoveName, componentUid = '') => {
|
||||||
mainDataKey,
|
|
||||||
attributeToRemoveName,
|
|
||||||
componentUid = ''
|
|
||||||
) => {
|
|
||||||
const type =
|
const type =
|
||||||
mainDataKey === 'components'
|
mainDataKey === 'components' ? 'REMOVE_FIELD_FROM_DISPLAYED_COMPONENT' : 'REMOVE_FIELD';
|
||||||
? 'REMOVE_FIELD_FROM_DISPLAYED_COMPONENT'
|
|
||||||
: 'REMOVE_FIELD';
|
|
||||||
|
|
||||||
if (mainDataKey === 'contentType') {
|
if (mainDataKey === 'contentType') {
|
||||||
emitEvent('willDeleteFieldOfContentType');
|
emitEvent('willDeleteFieldOfContentType');
|
||||||
@ -253,17 +228,11 @@ const DataManagerProvider = ({ allIcons, children }) => {
|
|||||||
const deleteData = async () => {
|
const deleteData = async () => {
|
||||||
try {
|
try {
|
||||||
const requestURL = `/${pluginId}/${endPoint}/${currentUid}`;
|
const requestURL = `/${pluginId}/${endPoint}/${currentUid}`;
|
||||||
const isTemporary = get(
|
const isTemporary = get(modifiedData, [firstKeyToMainSchema, 'isTemporary'], false);
|
||||||
modifiedData,
|
|
||||||
[firstKeyToMainSchema, 'isTemporary'],
|
|
||||||
false
|
|
||||||
);
|
|
||||||
const userConfirm = window.confirm(
|
const userConfirm = window.confirm(
|
||||||
formatMessage({
|
formatMessage({
|
||||||
id: getTrad(
|
id: getTrad(
|
||||||
`popUpWarning.bodyMessage.${
|
`popUpWarning.bodyMessage.${isInContentTypeView ? 'contentType' : 'component'}.delete`
|
||||||
isInContentTypeView ? 'contentType' : 'component'
|
|
||||||
}.delete`
|
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -338,9 +307,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
|
|||||||
|
|
||||||
const getAllNestedComponents = () => {
|
const getAllNestedComponents = () => {
|
||||||
const appNestedCompo = retrieveNestedComponents(components);
|
const appNestedCompo = retrieveNestedComponents(components);
|
||||||
const editingDataNestedCompos = retrieveNestedComponents(
|
const editingDataNestedCompos = retrieveNestedComponents(modifiedData.components || {});
|
||||||
modifiedData.components || {}
|
|
||||||
);
|
|
||||||
|
|
||||||
return makeUnique([...editingDataNestedCompos, ...appNestedCompo]);
|
return makeUnique([...editingDataNestedCompos, ...appNestedCompo]);
|
||||||
};
|
};
|
||||||
@ -370,10 +337,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
|
|||||||
isInContentTypeView
|
isInContentTypeView
|
||||||
);
|
);
|
||||||
|
|
||||||
const dataShape = orderAllDataAttributesWithImmutable(
|
const dataShape = orderAllDataAttributesWithImmutable(newSchemaToSet, isInContentTypeView);
|
||||||
newSchemaToSet,
|
|
||||||
isInContentTypeView
|
|
||||||
);
|
|
||||||
|
|
||||||
// This prevents from losing the created content type or component when clicking on the link from the left menu
|
// This prevents from losing the created content type or component when clicking on the link from the left menu
|
||||||
const hasJustCreatedSchema =
|
const hasJustCreatedSchema =
|
||||||
@ -401,11 +365,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
|
|||||||
|
|
||||||
const submitData = async additionalContentTypeData => {
|
const submitData = async additionalContentTypeData => {
|
||||||
try {
|
try {
|
||||||
const isCreating = get(
|
const isCreating = get(modifiedData, [firstKeyToMainSchema, 'isTemporary'], false);
|
||||||
modifiedData,
|
|
||||||
[firstKeyToMainSchema, 'isTemporary'],
|
|
||||||
false
|
|
||||||
);
|
|
||||||
const body = {
|
const body = {
|
||||||
components: getComponentsToPost(
|
components: getComponentsToPost(
|
||||||
modifiedData.components,
|
modifiedData.components,
|
||||||
@ -505,14 +465,11 @@ const DataManagerProvider = ({ allIcons, children }) => {
|
|||||||
value={{
|
value={{
|
||||||
addAttribute,
|
addAttribute,
|
||||||
addCreatedComponentToDynamicZone,
|
addCreatedComponentToDynamicZone,
|
||||||
allComponentsCategories: retrieveSpecificInfoFromComponents(
|
allComponentsCategories: retrieveSpecificInfoFromComponents(components, ['category']),
|
||||||
components,
|
allComponentsIconAlreadyTaken: retrieveSpecificInfoFromComponents(components, [
|
||||||
['category']
|
'schema',
|
||||||
),
|
'icon',
|
||||||
allComponentsIconAlreadyTaken: retrieveSpecificInfoFromComponents(
|
]),
|
||||||
components,
|
|
||||||
['schema', 'icon']
|
|
||||||
),
|
|
||||||
allIcons,
|
allIcons,
|
||||||
changeDynamicZoneComponents,
|
changeDynamicZoneComponents,
|
||||||
components,
|
components,
|
||||||
|
|||||||
@ -34,8 +34,7 @@ const addComponentsToState = (state, componentToAddUid, objToUpdate) => {
|
|||||||
const isTemporaryComponent = componentToAdd.get('isTemporary');
|
const isTemporaryComponent = componentToAdd.get('isTemporary');
|
||||||
const componentToAddSchema = componentToAdd.getIn(['schema', 'attributes']);
|
const componentToAddSchema = componentToAdd.getIn(['schema', 'attributes']);
|
||||||
const hasComponentAlreadyBeenAdded =
|
const hasComponentAlreadyBeenAdded =
|
||||||
state.getIn(['modifiedData', 'components', componentToAddUid]) !==
|
state.getIn(['modifiedData', 'components', componentToAddUid]) !== undefined;
|
||||||
undefined;
|
|
||||||
|
|
||||||
// created components are already in the modifiedData.components
|
// created components are already in the modifiedData.components
|
||||||
// We don't add them because all modifications will be lost
|
// We don't add them because all modifications will be lost
|
||||||
@ -52,17 +51,13 @@ const addComponentsToState = (state, componentToAddUid, objToUpdate) => {
|
|||||||
|
|
||||||
// We need to add the nested components to the modifiedData.components as well
|
// We need to add the nested components to the modifiedData.components as well
|
||||||
nestedComponents.forEach(componentUid => {
|
nestedComponents.forEach(componentUid => {
|
||||||
const isTemporary =
|
const isTemporary = state.getIn(['components', componentUid, 'isTemporary']) || false;
|
||||||
state.getIn(['components', componentUid, 'isTemporary']) || false;
|
|
||||||
const hasNestedComponentAlreadyBeenAdded =
|
const hasNestedComponentAlreadyBeenAdded =
|
||||||
state.getIn(['modifiedData', 'components', componentUid]) !== undefined;
|
state.getIn(['modifiedData', 'components', componentUid]) !== undefined;
|
||||||
|
|
||||||
// Same logic here otherwise we will lose the modifications added to the components
|
// Same logic here otherwise we will lose the modifications added to the components
|
||||||
if (!isTemporary && !hasNestedComponentAlreadyBeenAdded) {
|
if (!isTemporary && !hasNestedComponentAlreadyBeenAdded) {
|
||||||
newObj = newObj.set(
|
newObj = newObj.set(componentUid, state.getIn(['components', componentUid]));
|
||||||
componentUid,
|
|
||||||
state.getIn(['components', componentUid])
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -84,52 +79,42 @@ const reducer = (state, action) => {
|
|||||||
: [forTarget, targetUid];
|
: [forTarget, targetUid];
|
||||||
|
|
||||||
return state
|
return state
|
||||||
.updateIn(
|
.updateIn(['modifiedData', ...pathToDataToEdit, 'schema', 'attributes', name], () => {
|
||||||
['modifiedData', ...pathToDataToEdit, 'schema', 'attributes', name],
|
return fromJS(rest);
|
||||||
() => {
|
})
|
||||||
return fromJS(rest);
|
.updateIn(['modifiedData', ...pathToDataToEdit, 'schema', 'attributes'], obj => {
|
||||||
|
const type = get(rest, 'type', 'relation');
|
||||||
|
const target = get(rest, 'target', null);
|
||||||
|
const nature = get(rest, 'nature', null);
|
||||||
|
const currentUid = state.getIn(['modifiedData', ...pathToDataToEdit, 'uid']);
|
||||||
|
|
||||||
|
// When the user in creating a relation with the same content type we need to create another attribute
|
||||||
|
// that is the opposite of the created one
|
||||||
|
if (
|
||||||
|
type === 'relation' &&
|
||||||
|
nature !== 'oneWay' &&
|
||||||
|
nature !== 'manyWay' &&
|
||||||
|
target === currentUid
|
||||||
|
) {
|
||||||
|
const oppositeAttribute = {
|
||||||
|
nature: getOppositeNature(nature),
|
||||||
|
target,
|
||||||
|
unique: rest.unique,
|
||||||
|
// Leave this if we allow the required on the relation
|
||||||
|
// required: rest.required,
|
||||||
|
dominant: nature === 'manyToMany' ? !rest.dominant : null,
|
||||||
|
targetAttribute: name,
|
||||||
|
columnName: rest.targetColumnName,
|
||||||
|
targetColumnName: rest.columnName,
|
||||||
|
};
|
||||||
|
|
||||||
|
return obj.update(rest.targetAttribute, () => {
|
||||||
|
return fromJS(oppositeAttribute);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
)
|
|
||||||
.updateIn(
|
|
||||||
['modifiedData', ...pathToDataToEdit, 'schema', 'attributes'],
|
|
||||||
obj => {
|
|
||||||
const type = get(rest, 'type', 'relation');
|
|
||||||
const target = get(rest, 'target', null);
|
|
||||||
const nature = get(rest, 'nature', null);
|
|
||||||
const currentUid = state.getIn([
|
|
||||||
'modifiedData',
|
|
||||||
...pathToDataToEdit,
|
|
||||||
'uid',
|
|
||||||
]);
|
|
||||||
|
|
||||||
// When the user in creating a relation with the same content type we need to create another attribute
|
return obj;
|
||||||
// that is the opposite of the created one
|
})
|
||||||
if (
|
|
||||||
type === 'relation' &&
|
|
||||||
nature !== 'oneWay' &&
|
|
||||||
nature !== 'manyWay' &&
|
|
||||||
target === currentUid
|
|
||||||
) {
|
|
||||||
const oppositeAttribute = {
|
|
||||||
nature: getOppositeNature(nature),
|
|
||||||
target,
|
|
||||||
unique: rest.unique,
|
|
||||||
// Leave this if we allow the required on the relation
|
|
||||||
// required: rest.required,
|
|
||||||
dominant: nature === 'manyToMany' ? !rest.dominant : null,
|
|
||||||
targetAttribute: name,
|
|
||||||
columnName: rest.targetColumnName,
|
|
||||||
targetColumnName: rest.columnName,
|
|
||||||
};
|
|
||||||
|
|
||||||
return obj.update(rest.targetAttribute, () => {
|
|
||||||
return fromJS(oppositeAttribute);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.updateIn(['modifiedData', 'components'], existingCompos => {
|
.updateIn(['modifiedData', 'components'], existingCompos => {
|
||||||
if (action.shouldAddComponentToData) {
|
if (action.shouldAddComponentToData) {
|
||||||
return addComponentsToState(state, rest.component, existingCompos);
|
return addComponentsToState(state, rest.component, existingCompos);
|
||||||
@ -142,14 +127,7 @@ const reducer = (state, action) => {
|
|||||||
const { dynamicZoneTarget, componentsToAdd } = action;
|
const { dynamicZoneTarget, componentsToAdd } = action;
|
||||||
|
|
||||||
return state.updateIn(
|
return state.updateIn(
|
||||||
[
|
['modifiedData', 'contentType', 'schema', 'attributes', dynamicZoneTarget, 'components'],
|
||||||
'modifiedData',
|
|
||||||
'contentType',
|
|
||||||
'schema',
|
|
||||||
'attributes',
|
|
||||||
dynamicZoneTarget,
|
|
||||||
'components',
|
|
||||||
],
|
|
||||||
list => {
|
list => {
|
||||||
return list.concat(componentsToAdd);
|
return list.concat(componentsToAdd);
|
||||||
}
|
}
|
||||||
@ -165,14 +143,7 @@ const reducer = (state, action) => {
|
|||||||
|
|
||||||
return state
|
return state
|
||||||
.updateIn(
|
.updateIn(
|
||||||
[
|
['modifiedData', 'contentType', 'schema', 'attributes', dynamicZoneTarget, 'components'],
|
||||||
'modifiedData',
|
|
||||||
'contentType',
|
|
||||||
'schema',
|
|
||||||
'attributes',
|
|
||||||
dynamicZoneTarget,
|
|
||||||
'components',
|
|
||||||
],
|
|
||||||
list => {
|
list => {
|
||||||
return fromJS(makeUnique([...list.toJS(), ...newComponents]));
|
return fromJS(makeUnique([...list.toJS(), ...newComponents]));
|
||||||
}
|
}
|
||||||
@ -196,9 +167,7 @@ const reducer = (state, action) => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return state.updateIn(['contentTypes', action.uid], () =>
|
return state.updateIn(['contentTypes', action.uid], () => fromJS(newSchema));
|
||||||
fromJS(newSchema)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
case 'CREATE_COMPONENT_SCHEMA': {
|
case 'CREATE_COMPONENT_SCHEMA': {
|
||||||
const newSchema = {
|
const newSchema = {
|
||||||
@ -214,14 +183,10 @@ const reducer = (state, action) => {
|
|||||||
if (action.shouldAddComponentToData) {
|
if (action.shouldAddComponentToData) {
|
||||||
return state
|
return state
|
||||||
.updateIn(['components', action.uid], () => fromJS(newSchema))
|
.updateIn(['components', action.uid], () => fromJS(newSchema))
|
||||||
.updateIn(['modifiedData', 'components', action.uid], () =>
|
.updateIn(['modifiedData', 'components', action.uid], () => fromJS(newSchema));
|
||||||
fromJS(newSchema)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return state.updateIn(['components', action.uid], () =>
|
return state.updateIn(['components', action.uid], () => fromJS(newSchema));
|
||||||
fromJS(newSchema)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
case 'DELETE_NOT_SAVED_TYPE': {
|
case 'DELETE_NOT_SAVED_TYPE': {
|
||||||
// Doing so will also reset the modified and the initial data
|
// Doing so will also reset the modified and the initial data
|
||||||
@ -243,144 +208,129 @@ const reducer = (state, action) => {
|
|||||||
? [forTarget]
|
? [forTarget]
|
||||||
: [forTarget, targetUid];
|
: [forTarget, targetUid];
|
||||||
|
|
||||||
return newState.updateIn(
|
return newState.updateIn(['modifiedData', ...pathToDataToEdit, 'schema'], obj => {
|
||||||
['modifiedData', ...pathToDataToEdit, 'schema'],
|
let oppositeAttributeNameToRemove = null;
|
||||||
obj => {
|
let oppositeAttributeNameToUpdate = null;
|
||||||
let oppositeAttributeNameToRemove = null;
|
let oppositeAttributeNameToCreateBecauseOfNatureChange = null;
|
||||||
let oppositeAttributeNameToUpdate = null;
|
let oppositeAttributeToCreate = null;
|
||||||
let oppositeAttributeNameToCreateBecauseOfNatureChange = null;
|
|
||||||
let oppositeAttributeToCreate = null;
|
|
||||||
|
|
||||||
const newObj = OrderedMap(
|
const newObj = OrderedMap(
|
||||||
obj
|
obj
|
||||||
.get('attributes')
|
.get('attributes')
|
||||||
.keySeq()
|
.keySeq()
|
||||||
.reduce((acc, current) => {
|
.reduce((acc, current) => {
|
||||||
const isEditingCurrentAttribute =
|
const isEditingCurrentAttribute = current === initialAttributeName;
|
||||||
current === initialAttributeName;
|
|
||||||
|
|
||||||
if (isEditingCurrentAttribute) {
|
if (isEditingCurrentAttribute) {
|
||||||
const currentUid = state.getIn([
|
const currentUid = state.getIn(['modifiedData', ...pathToDataToEdit, 'uid']);
|
||||||
'modifiedData',
|
const isEditingRelation = has(initialAttribute, 'nature');
|
||||||
...pathToDataToEdit,
|
const didChangeTargetRelation = initialAttribute.target !== rest.target;
|
||||||
'uid',
|
const didCreateInternalRelation = rest.target === currentUid;
|
||||||
]);
|
const nature = rest.nature;
|
||||||
const isEditingRelation = has(initialAttribute, 'nature');
|
const initialNature = initialAttribute.nature;
|
||||||
const didChangeTargetRelation =
|
const hadInternalRelation = initialAttribute.target === currentUid;
|
||||||
initialAttribute.target !== rest.target;
|
const didChangeRelationNature = initialAttribute.nature !== nature;
|
||||||
const didCreateInternalRelation = rest.target === currentUid;
|
const shouldRemoveOppositeAttributeBecauseOfTargetChange =
|
||||||
const nature = rest.nature;
|
didChangeTargetRelation &&
|
||||||
const initialNature = initialAttribute.nature;
|
!didCreateInternalRelation &&
|
||||||
const hadInternalRelation =
|
hadInternalRelation &&
|
||||||
initialAttribute.target === currentUid;
|
isEditingRelation;
|
||||||
const didChangeRelationNature =
|
const shouldRemoveOppositeAttributeBecauseOfNatureChange =
|
||||||
initialAttribute.nature !== nature;
|
didChangeRelationNature &&
|
||||||
const shouldRemoveOppositeAttributeBecauseOfTargetChange =
|
hadInternalRelation &&
|
||||||
didChangeTargetRelation &&
|
['oneWay', 'manyWay'].includes(nature) &&
|
||||||
!didCreateInternalRelation &&
|
isEditingRelation;
|
||||||
hadInternalRelation &&
|
const shouldUpdateOppositeAttributeBecauseOfNatureChange =
|
||||||
isEditingRelation;
|
!ONE_SIDE_RELATIONS.includes(initialNature) &&
|
||||||
const shouldRemoveOppositeAttributeBecauseOfNatureChange =
|
!ONE_SIDE_RELATIONS.includes(nature) &&
|
||||||
didChangeRelationNature &&
|
hadInternalRelation &&
|
||||||
hadInternalRelation &&
|
didCreateInternalRelation &&
|
||||||
['oneWay', 'manyWay'].includes(nature) &&
|
isEditingRelation;
|
||||||
isEditingRelation;
|
const shouldCreateOppositeAttributeBecauseOfNatureChange =
|
||||||
const shouldUpdateOppositeAttributeBecauseOfNatureChange =
|
ONE_SIDE_RELATIONS.includes(initialNature) &&
|
||||||
!ONE_SIDE_RELATIONS.includes(initialNature) &&
|
!ONE_SIDE_RELATIONS.includes(nature) &&
|
||||||
!ONE_SIDE_RELATIONS.includes(nature) &&
|
hadInternalRelation &&
|
||||||
hadInternalRelation &&
|
didCreateInternalRelation &&
|
||||||
didCreateInternalRelation &&
|
isEditingRelation;
|
||||||
isEditingRelation;
|
const shouldCreateOppositeAttributeBecauseOfTargetChange =
|
||||||
const shouldCreateOppositeAttributeBecauseOfNatureChange =
|
didChangeTargetRelation &&
|
||||||
ONE_SIDE_RELATIONS.includes(initialNature) &&
|
didCreateInternalRelation &&
|
||||||
!ONE_SIDE_RELATIONS.includes(nature) &&
|
!ONE_SIDE_RELATIONS.includes(nature);
|
||||||
hadInternalRelation &&
|
|
||||||
didCreateInternalRelation &&
|
|
||||||
isEditingRelation;
|
|
||||||
const shouldCreateOppositeAttributeBecauseOfTargetChange =
|
|
||||||
didChangeTargetRelation &&
|
|
||||||
didCreateInternalRelation &&
|
|
||||||
!ONE_SIDE_RELATIONS.includes(nature);
|
|
||||||
|
|
||||||
// Update the opposite attribute name so it is removed at the end of the loop
|
// Update the opposite attribute name so it is removed at the end of the loop
|
||||||
|
if (
|
||||||
|
shouldRemoveOppositeAttributeBecauseOfTargetChange ||
|
||||||
|
shouldRemoveOppositeAttributeBecauseOfNatureChange
|
||||||
|
) {
|
||||||
|
oppositeAttributeNameToRemove = initialAttribute.targetAttribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the opposite attribute that will be updated when the loop attribute matches the name
|
||||||
|
if (
|
||||||
|
shouldUpdateOppositeAttributeBecauseOfNatureChange ||
|
||||||
|
shouldCreateOppositeAttributeBecauseOfNatureChange ||
|
||||||
|
shouldCreateOppositeAttributeBecauseOfTargetChange
|
||||||
|
) {
|
||||||
|
oppositeAttributeNameToUpdate = initialAttribute.targetAttribute;
|
||||||
|
oppositeAttributeNameToCreateBecauseOfNatureChange = rest.targetAttribute;
|
||||||
|
|
||||||
|
oppositeAttributeToCreate = {
|
||||||
|
nature: getOppositeNature(rest.nature),
|
||||||
|
target: rest.target,
|
||||||
|
unique: rest.unique,
|
||||||
|
// Leave this if we allow the required on the relation
|
||||||
|
// required: rest.required,
|
||||||
|
dominant: rest.nature === 'manyToMany' ? !rest.dominant : null,
|
||||||
|
targetAttribute: name,
|
||||||
|
columnName: rest.targetColumnName,
|
||||||
|
targetColumnName: rest.columnName,
|
||||||
|
};
|
||||||
|
|
||||||
|
// First update the current attribute with the value
|
||||||
|
acc[name] = fromJS(rest);
|
||||||
|
|
||||||
|
// Then (if needed) create the opposite attribute the case is changing the relation from
|
||||||
|
// We do it here so keep the order of the attributes
|
||||||
|
// oneWay || manyWay to something another relation
|
||||||
if (
|
if (
|
||||||
shouldRemoveOppositeAttributeBecauseOfTargetChange ||
|
|
||||||
shouldRemoveOppositeAttributeBecauseOfNatureChange
|
|
||||||
) {
|
|
||||||
oppositeAttributeNameToRemove =
|
|
||||||
initialAttribute.targetAttribute;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the opposite attribute that will be updated when the loop attribute matches the name
|
|
||||||
if (
|
|
||||||
shouldUpdateOppositeAttributeBecauseOfNatureChange ||
|
|
||||||
shouldCreateOppositeAttributeBecauseOfNatureChange ||
|
shouldCreateOppositeAttributeBecauseOfNatureChange ||
|
||||||
shouldCreateOppositeAttributeBecauseOfTargetChange
|
shouldCreateOppositeAttributeBecauseOfTargetChange
|
||||||
) {
|
) {
|
||||||
oppositeAttributeNameToUpdate =
|
acc[oppositeAttributeNameToCreateBecauseOfNatureChange] = fromJS(
|
||||||
initialAttribute.targetAttribute;
|
oppositeAttributeToCreate
|
||||||
oppositeAttributeNameToCreateBecauseOfNatureChange =
|
);
|
||||||
rest.targetAttribute;
|
|
||||||
|
|
||||||
oppositeAttributeToCreate = {
|
oppositeAttributeToCreate = null;
|
||||||
nature: getOppositeNature(rest.nature),
|
oppositeAttributeNameToCreateBecauseOfNatureChange = null;
|
||||||
target: rest.target,
|
|
||||||
unique: rest.unique,
|
|
||||||
// Leave this if we allow the required on the relation
|
|
||||||
// required: rest.required,
|
|
||||||
dominant:
|
|
||||||
rest.nature === 'manyToMany' ? !rest.dominant : null,
|
|
||||||
targetAttribute: name,
|
|
||||||
columnName: rest.targetColumnName,
|
|
||||||
targetColumnName: rest.columnName,
|
|
||||||
};
|
|
||||||
|
|
||||||
// First update the current attribute with the value
|
|
||||||
acc[name] = fromJS(rest);
|
|
||||||
|
|
||||||
// Then (if needed) create the opposite attribute the case is changing the relation from
|
|
||||||
// We do it here so keep the order of the attributes
|
|
||||||
// oneWay || manyWay to something another relation
|
|
||||||
if (
|
|
||||||
shouldCreateOppositeAttributeBecauseOfNatureChange ||
|
|
||||||
shouldCreateOppositeAttributeBecauseOfTargetChange
|
|
||||||
) {
|
|
||||||
acc[
|
|
||||||
oppositeAttributeNameToCreateBecauseOfNatureChange
|
|
||||||
] = fromJS(oppositeAttributeToCreate);
|
|
||||||
|
|
||||||
oppositeAttributeToCreate = null;
|
|
||||||
oppositeAttributeNameToCreateBecauseOfNatureChange = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
acc[name] = fromJS(rest);
|
return acc;
|
||||||
} else if (current === oppositeAttributeNameToUpdate) {
|
|
||||||
acc[
|
|
||||||
oppositeAttributeNameToCreateBecauseOfNatureChange
|
|
||||||
] = fromJS(oppositeAttributeToCreate);
|
|
||||||
} else {
|
|
||||||
acc[current] = obj.getIn(['attributes', current]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return acc;
|
acc[name] = fromJS(rest);
|
||||||
}, {})
|
} else if (current === oppositeAttributeNameToUpdate) {
|
||||||
);
|
acc[oppositeAttributeNameToCreateBecauseOfNatureChange] = fromJS(
|
||||||
|
oppositeAttributeToCreate
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
acc[current] = obj.getIn(['attributes', current]);
|
||||||
|
}
|
||||||
|
|
||||||
let updatedObj;
|
return acc;
|
||||||
|
}, {})
|
||||||
|
);
|
||||||
|
|
||||||
// Remove the opposite attribute
|
let updatedObj;
|
||||||
if (oppositeAttributeNameToRemove !== null) {
|
|
||||||
updatedObj = newObj.remove(oppositeAttributeNameToRemove);
|
|
||||||
} else {
|
|
||||||
updatedObj = newObj;
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj.set('attributes', updatedObj);
|
// Remove the opposite attribute
|
||||||
|
if (oppositeAttributeNameToRemove !== null) {
|
||||||
|
updatedObj = newObj.remove(oppositeAttributeNameToRemove);
|
||||||
|
} else {
|
||||||
|
updatedObj = newObj;
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
return obj.set('attributes', updatedObj);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'GET_DATA_SUCCEEDED': {
|
case 'GET_DATA_SUCCEEDED': {
|
||||||
@ -418,35 +368,18 @@ const reducer = (state, action) => {
|
|||||||
]);
|
]);
|
||||||
case 'REMOVE_FIELD': {
|
case 'REMOVE_FIELD': {
|
||||||
const { mainDataKey, attributeToRemoveName } = action;
|
const { mainDataKey, attributeToRemoveName } = action;
|
||||||
const pathToAttributes = [
|
const pathToAttributes = ['modifiedData', mainDataKey, 'schema', 'attributes'];
|
||||||
'modifiedData',
|
const pathToAttributeToRemove = [...pathToAttributes, attributeToRemoveName];
|
||||||
mainDataKey,
|
|
||||||
'schema',
|
|
||||||
'attributes',
|
|
||||||
];
|
|
||||||
const pathToAttributeToRemove = [
|
|
||||||
...pathToAttributes,
|
|
||||||
attributeToRemoveName,
|
|
||||||
];
|
|
||||||
|
|
||||||
const attributeToRemoveData = state.getIn(pathToAttributeToRemove);
|
const attributeToRemoveData = state.getIn(pathToAttributeToRemove);
|
||||||
|
|
||||||
const isRemovingRelationAttribute =
|
const isRemovingRelationAttribute = attributeToRemoveData.get('nature') !== undefined;
|
||||||
attributeToRemoveData.get('nature') !== undefined;
|
|
||||||
// Only content types can have relations with themselves since
|
// Only content types can have relations with themselves since
|
||||||
// components can only have oneWay or manyWay relations
|
// components can only have oneWay or manyWay relations
|
||||||
const canTheAttributeToRemoveHaveARelationWithItself =
|
const canTheAttributeToRemoveHaveARelationWithItself = mainDataKey === 'contentType';
|
||||||
mainDataKey === 'contentType';
|
|
||||||
|
|
||||||
if (
|
if (isRemovingRelationAttribute && canTheAttributeToRemoveHaveARelationWithItself) {
|
||||||
isRemovingRelationAttribute &&
|
const { target, nature, targetAttribute } = attributeToRemoveData.toJS();
|
||||||
canTheAttributeToRemoveHaveARelationWithItself
|
|
||||||
) {
|
|
||||||
const {
|
|
||||||
target,
|
|
||||||
nature,
|
|
||||||
targetAttribute,
|
|
||||||
} = attributeToRemoveData.toJS();
|
|
||||||
const uid = state.getIn(['modifiedData', 'contentType', 'uid']);
|
const uid = state.getIn(['modifiedData', 'contentType', 'uid']);
|
||||||
const shouldRemoveOppositeAttribute =
|
const shouldRemoveOppositeAttribute =
|
||||||
target === uid && !ONE_SIDE_RELATIONS.includes(nature);
|
target === uid && !ONE_SIDE_RELATIONS.includes(nature);
|
||||||
@ -458,7 +391,15 @@ const reducer = (state, action) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return state.removeIn(pathToAttributeToRemove);
|
return state.removeIn(pathToAttributeToRemove).updateIn([...pathToAttributes], attributes => {
|
||||||
|
return attributes.keySeq().reduce((acc, current) => {
|
||||||
|
if (acc.getIn([current, 'targetField']) === attributeToRemoveName) {
|
||||||
|
return acc.removeIn([current, 'targetField']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, attributes);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
case 'SET_MODIFIED_DATA': {
|
case 'SET_MODIFIED_DATA': {
|
||||||
let newState = state
|
let newState = state
|
||||||
@ -502,9 +443,7 @@ const reducer = (state, action) => {
|
|||||||
|
|
||||||
if (schemaType === 'component') {
|
if (schemaType === 'component') {
|
||||||
newState = newState.updateIn(['components'], obj => {
|
newState = newState.updateIn(['components'], obj => {
|
||||||
return obj.update(uid, () =>
|
return obj.update(uid, () => newState.getIn(['modifiedData', 'component']));
|
||||||
newState.getIn(['modifiedData', 'component'])
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -417,13 +417,7 @@ const data = {
|
|||||||
collectionName: '',
|
collectionName: '',
|
||||||
attributes: {
|
attributes: {
|
||||||
price_range: {
|
price_range: {
|
||||||
enum: [
|
enum: ['very_cheap', 'cheap', 'average', 'expensive', 'very_expensive'],
|
||||||
'very_cheap',
|
|
||||||
'cheap',
|
|
||||||
'average',
|
|
||||||
'expensive',
|
|
||||||
'very_expensive',
|
|
||||||
],
|
|
||||||
type: 'enumeration',
|
type: 'enumeration',
|
||||||
},
|
},
|
||||||
closing_period: {
|
closing_period: {
|
||||||
@ -479,6 +473,17 @@ const data = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
'application::homepage.homepage': {
|
||||||
|
uid: 'application::homepage.homepage',
|
||||||
|
schema: {
|
||||||
|
name: 'homepage',
|
||||||
|
attributes: {
|
||||||
|
title: { type: 'string' },
|
||||||
|
description: { type: 'string' },
|
||||||
|
homepageuidfield: { type: 'uid', targetField: 'description' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -17,10 +17,7 @@ describe('CTB | containers | DataManagerProvider | reducer | REMOVE_FIELD', () =
|
|||||||
const state = initialState
|
const state = initialState
|
||||||
.set('contentTypes', fromJS(testData.contentTypes))
|
.set('contentTypes', fromJS(testData.contentTypes))
|
||||||
.set('initialContentTypes', fromJS(testData.contentTypes))
|
.set('initialContentTypes', fromJS(testData.contentTypes))
|
||||||
.setIn(
|
.setIn(['modifiedData', 'contentType'], fromJS(testData.contentTypes[contentTypeUID]))
|
||||||
['modifiedData', 'contentType'],
|
|
||||||
fromJS(testData.contentTypes[contentTypeUID])
|
|
||||||
)
|
|
||||||
.setIn(['modifiedData', 'components'], fromJS({}));
|
.setIn(['modifiedData', 'components'], fromJS({}));
|
||||||
|
|
||||||
const expected = state.removeIn([
|
const expected = state.removeIn([
|
||||||
@ -49,10 +46,7 @@ describe('CTB | containers | DataManagerProvider | reducer | REMOVE_FIELD', () =
|
|||||||
const state = initialState
|
const state = initialState
|
||||||
.set('contentTypes', fromJS(testData.contentTypes))
|
.set('contentTypes', fromJS(testData.contentTypes))
|
||||||
.set('initialContentTypes', fromJS(testData.contentTypes))
|
.set('initialContentTypes', fromJS(testData.contentTypes))
|
||||||
.setIn(
|
.setIn(['modifiedData', 'contentType'], fromJS(testData.contentTypes[contentTypeUID]))
|
||||||
['modifiedData', 'contentType'],
|
|
||||||
fromJS(testData.contentTypes[contentTypeUID])
|
|
||||||
)
|
|
||||||
.setIn(['modifiedData', 'components'], fromJS({}));
|
.setIn(['modifiedData', 'components'], fromJS({}));
|
||||||
|
|
||||||
const expected = state.removeIn([
|
const expected = state.removeIn([
|
||||||
@ -163,10 +157,7 @@ describe('CTB | containers | DataManagerProvider | reducer | REMOVE_FIELD', () =
|
|||||||
.setIn(['contentTypes', contentTypeUID], fromJS(contentType))
|
.setIn(['contentTypes', contentTypeUID], fromJS(contentType))
|
||||||
.setIn(['modifiedData', 'contentType'], fromJS(contentType));
|
.setIn(['modifiedData', 'contentType'], fromJS(contentType));
|
||||||
|
|
||||||
const expected = state.setIn(
|
const expected = state.setIn(['modifiedData', 'contentType'], fromJS(expectedContentType));
|
||||||
['modifiedData', 'contentType'],
|
|
||||||
fromJS(expectedContentType)
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(reducer(state, action)).toEqual(expected);
|
expect(reducer(state, action)).toEqual(expected);
|
||||||
});
|
});
|
||||||
@ -257,10 +248,7 @@ describe('CTB | containers | DataManagerProvider | reducer | REMOVE_FIELD', () =
|
|||||||
.setIn(['contentTypes', contentTypeUID], fromJS(contentType))
|
.setIn(['contentTypes', contentTypeUID], fromJS(contentType))
|
||||||
.setIn(['modifiedData', 'contentType'], fromJS(contentType));
|
.setIn(['modifiedData', 'contentType'], fromJS(contentType));
|
||||||
|
|
||||||
const expected = state.setIn(
|
const expected = state.setIn(['modifiedData', 'contentType'], fromJS(expectedContentType));
|
||||||
['modifiedData', 'contentType'],
|
|
||||||
fromJS(expectedContentType)
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(reducer(state, action)).toEqual(expected);
|
expect(reducer(state, action)).toEqual(expected);
|
||||||
expect(
|
expect(
|
||||||
@ -271,4 +259,35 @@ describe('CTB | containers | DataManagerProvider | reducer | REMOVE_FIELD', () =
|
|||||||
).toEqual(expected);
|
).toEqual(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('Removing a field that is targeted by a UID field', () => {
|
||||||
|
it('Should remove the attribute correctly and remove the targetField from the UID field', () => {
|
||||||
|
const contentTypeUID = 'application::homepage.homepage';
|
||||||
|
const attributeToRemoveName = 'description';
|
||||||
|
const action = {
|
||||||
|
type: 'REMOVE_FIELD',
|
||||||
|
mainDataKey: 'contentType',
|
||||||
|
attributeToRemoveName,
|
||||||
|
componentUid: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const state = initialState
|
||||||
|
.set('contentTypes', fromJS(testData.contentTypes))
|
||||||
|
.set('initialContentTypes', fromJS(testData.contentTypes))
|
||||||
|
.setIn(['modifiedData', 'contentType'], fromJS(testData.contentTypes[contentTypeUID]))
|
||||||
|
.setIn(['modifiedData', 'components'], fromJS({}));
|
||||||
|
|
||||||
|
const expected = state
|
||||||
|
.removeIn(['modifiedData', 'contentType', 'schema', 'attributes', attributeToRemoveName])
|
||||||
|
.removeIn([
|
||||||
|
'modifiedData',
|
||||||
|
'contentType',
|
||||||
|
'schema',
|
||||||
|
'attributes',
|
||||||
|
'homepageuidfield',
|
||||||
|
'targetField',
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(reducer(state, action)).toEqual(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -327,6 +327,7 @@ const FormModal = () => {
|
|||||||
const isOpen = !isEmpty(search);
|
const isOpen = !isEmpty(search);
|
||||||
const isPickingAttribute = state.modalType === 'chooseAttribute';
|
const isPickingAttribute = state.modalType === 'chooseAttribute';
|
||||||
const uid = createUid(modifiedData.name || '');
|
const uid = createUid(modifiedData.name || '');
|
||||||
|
const attributes = get(allDataSchema, [...state.pathToSchema, 'schema', 'attributes'], null);
|
||||||
|
|
||||||
const checkFormValidity = async () => {
|
const checkFormValidity = async () => {
|
||||||
let schema;
|
let schema;
|
||||||
@ -394,7 +395,6 @@ const FormModal = () => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
schema = forms[state.modalType].schema(
|
schema = forms[state.modalType].schema(
|
||||||
get(allDataSchema, state.pathToSchema, {}),
|
get(allDataSchema, state.pathToSchema, {}),
|
||||||
type,
|
type,
|
||||||
@ -1090,180 +1090,184 @@ const FormModal = () => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
: form(modifiedData, state.attributeType, state.step, state.actionType).items.map(
|
: form(
|
||||||
(row, index) => {
|
modifiedData,
|
||||||
return (
|
state.attributeType,
|
||||||
<div className="row" key={index} style={{ marginBottom: 4 }}>
|
state.step,
|
||||||
{row.map((input, i) => {
|
state.actionType,
|
||||||
// The divider type is used mainly the advanced tab
|
attributes
|
||||||
// It is the one responsible for displaying the settings label
|
).items.map((row, index) => {
|
||||||
if (input.type === 'divider') {
|
return (
|
||||||
return (
|
<div className="row" key={index} style={{ marginBottom: 4 }}>
|
||||||
<div
|
{row.map((input, i) => {
|
||||||
className="col-12"
|
// The divider type is used mainly the advanced tab
|
||||||
style={{
|
// It is the one responsible for displaying the settings label
|
||||||
marginBottom: '1.4rem',
|
if (input.type === 'divider') {
|
||||||
marginTop: -2,
|
|
||||||
fontWeight: 500,
|
|
||||||
}}
|
|
||||||
key="divider"
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id={getTrad('form.attribute.item.settings.name')}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The spacer type is used mainly to align the icon picker...
|
|
||||||
if (input.type === 'spacer') {
|
|
||||||
return <div key="spacer" style={{ height: 11 }} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The spacer type is used mainly to align the icon picker...
|
|
||||||
if (input.type === 'spacer-small') {
|
|
||||||
return <div key="spacer" style={{ height: 4 }} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.type === 'spacer-medium') {
|
|
||||||
return <div key="spacer" style={{ height: 8 }} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This type is used in the addComponentToDynamicZone modal when selecting the option add an existing component
|
|
||||||
// It pushes select the components to the right
|
|
||||||
if (input.type === 'pushRight') {
|
|
||||||
return <div key={`${index}.${i}`} className={`col-${input.size}`} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.type === 'relation') {
|
|
||||||
return (
|
|
||||||
<RelationForm
|
|
||||||
key="relation"
|
|
||||||
mainBoxHeader={get(headers, [0, 'label'], '')}
|
|
||||||
modifiedData={modifiedData}
|
|
||||||
naturePickerType={state.forTarget}
|
|
||||||
onChange={handleChange}
|
|
||||||
errors={formErrors}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve the error for a specific input
|
|
||||||
const errorId = get(
|
|
||||||
formErrors,
|
|
||||||
[
|
|
||||||
...input.name
|
|
||||||
.split('.')
|
|
||||||
// The filter here is used when creating a component
|
|
||||||
// in the component step 1 modal
|
|
||||||
// Since the component info is stored in the
|
|
||||||
// componentToCreate object we can access the error
|
|
||||||
// By removing the key
|
|
||||||
.filter(key => key !== 'componentToCreate'),
|
|
||||||
'id',
|
|
||||||
],
|
|
||||||
null
|
|
||||||
);
|
|
||||||
|
|
||||||
const retrievedValue = get(modifiedData, input.name, '');
|
|
||||||
|
|
||||||
let value;
|
|
||||||
|
|
||||||
// Condition for the boolean default value
|
|
||||||
// The radio input doesn't accept false, true or null as value
|
|
||||||
// So we pass them as string
|
|
||||||
// This way the data stays accurate and we don't have to operate
|
|
||||||
// any data mutation
|
|
||||||
if (input.name === 'default' && state.attributeType === 'boolean') {
|
|
||||||
value = toString(retrievedValue);
|
|
||||||
// Same here for the enum
|
|
||||||
} else if (input.name === 'enum' && Array.isArray(retrievedValue)) {
|
|
||||||
value = retrievedValue.join('\n');
|
|
||||||
} else if (input.name === 'uid') {
|
|
||||||
value = input.value;
|
|
||||||
} else {
|
|
||||||
value = retrievedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The addon input is not present in @buffetjs so we are using the old lib
|
|
||||||
// for the moment that's why we don't want them be passed to buffet
|
|
||||||
// like the other created inputs
|
|
||||||
if (input.type === 'addon') {
|
|
||||||
return (
|
|
||||||
<InputsIndex
|
|
||||||
key={input.name}
|
|
||||||
{...input}
|
|
||||||
type="string"
|
|
||||||
onChange={handleChange}
|
|
||||||
value={value}
|
|
||||||
style={{ marginTop: 8, marginBottom: 11 }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`col-${input.size || 6}`} key={input.name}>
|
<div
|
||||||
<Inputs
|
className="col-12"
|
||||||
{...input}
|
style={{
|
||||||
modifiedData={modifiedData}
|
marginBottom: '1.4rem',
|
||||||
addComponentsToDynamicZone={handleClickAddComponentsToDynamicZone}
|
marginTop: -2,
|
||||||
customInputs={{
|
fontWeight: 500,
|
||||||
componentIconPicker: ComponentIconPicker,
|
}}
|
||||||
componentSelect: WrapperSelect,
|
key="divider"
|
||||||
creatableSelect: WrapperSelect,
|
>
|
||||||
customCheckboxWithChildren: CustomCheckbox,
|
<FormattedMessage
|
||||||
booleanBox: BooleanBox,
|
id={getTrad('form.attribute.item.settings.name')}
|
||||||
}}
|
|
||||||
isCreating={isCreating}
|
|
||||||
// Props for the componentSelect
|
|
||||||
isCreatingComponentWhileAddingAField={
|
|
||||||
isCreatingComponentWhileAddingAField
|
|
||||||
}
|
|
||||||
// Props for the componentSelect
|
|
||||||
// Since the component is created after adding it to a type
|
|
||||||
// its name and category can't be retrieved from the data manager
|
|
||||||
componentCategoryNeededForAddingAfieldWhileCreatingAComponent={get(
|
|
||||||
componentToCreate,
|
|
||||||
'category',
|
|
||||||
null
|
|
||||||
)}
|
|
||||||
// Props for the componentSelect same explanation
|
|
||||||
componentNameNeededForAddingAfieldWhileCreatingAComponent={get(
|
|
||||||
componentToCreate,
|
|
||||||
'name',
|
|
||||||
null
|
|
||||||
)}
|
|
||||||
isAddingAComponentToAnotherComponent={
|
|
||||||
state.forTarget === 'components' ||
|
|
||||||
state.forTarget === 'component'
|
|
||||||
}
|
|
||||||
value={value}
|
|
||||||
error={isEmpty(errorId) ? null : formatMessage({ id: errorId })}
|
|
||||||
onChange={handleChange}
|
|
||||||
onBlur={() => {}}
|
|
||||||
description={
|
|
||||||
get(input, 'description.id', null)
|
|
||||||
? formatMessage(input.description)
|
|
||||||
: input.description
|
|
||||||
}
|
|
||||||
placeholder={
|
|
||||||
get(input, 'placeholder.id', null)
|
|
||||||
? formatMessage(input.placeholder)
|
|
||||||
: input.placeholder
|
|
||||||
}
|
|
||||||
label={
|
|
||||||
get(input, 'label.id', null)
|
|
||||||
? formatMessage(input.label)
|
|
||||||
: input.label
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
}
|
||||||
</div>
|
|
||||||
);
|
// The spacer type is used mainly to align the icon picker...
|
||||||
}
|
if (input.type === 'spacer') {
|
||||||
)}
|
return <div key="spacer" style={{ height: 11 }} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The spacer type is used mainly to align the icon picker...
|
||||||
|
if (input.type === 'spacer-small') {
|
||||||
|
return <div key="spacer" style={{ height: 4 }} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.type === 'spacer-medium') {
|
||||||
|
return <div key="spacer" style={{ height: 8 }} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type is used in the addComponentToDynamicZone modal when selecting the option add an existing component
|
||||||
|
// It pushes select the components to the right
|
||||||
|
if (input.type === 'pushRight') {
|
||||||
|
return <div key={`${index}.${i}`} className={`col-${input.size}`} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.type === 'relation') {
|
||||||
|
return (
|
||||||
|
<RelationForm
|
||||||
|
key="relation"
|
||||||
|
mainBoxHeader={get(headers, [0, 'label'], '')}
|
||||||
|
modifiedData={modifiedData}
|
||||||
|
naturePickerType={state.forTarget}
|
||||||
|
onChange={handleChange}
|
||||||
|
errors={formErrors}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve the error for a specific input
|
||||||
|
const errorId = get(
|
||||||
|
formErrors,
|
||||||
|
[
|
||||||
|
...input.name
|
||||||
|
.split('.')
|
||||||
|
// The filter here is used when creating a component
|
||||||
|
// in the component step 1 modal
|
||||||
|
// Since the component info is stored in the
|
||||||
|
// componentToCreate object we can access the error
|
||||||
|
// By removing the key
|
||||||
|
.filter(key => key !== 'componentToCreate'),
|
||||||
|
'id',
|
||||||
|
],
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
const retrievedValue = get(modifiedData, input.name, '');
|
||||||
|
|
||||||
|
let value;
|
||||||
|
|
||||||
|
// Condition for the boolean default value
|
||||||
|
// The radio input doesn't accept false, true or null as value
|
||||||
|
// So we pass them as string
|
||||||
|
// This way the data stays accurate and we don't have to operate
|
||||||
|
// any data mutation
|
||||||
|
if (input.name === 'default' && state.attributeType === 'boolean') {
|
||||||
|
value = toString(retrievedValue);
|
||||||
|
// Same here for the enum
|
||||||
|
} else if (input.name === 'enum' && Array.isArray(retrievedValue)) {
|
||||||
|
value = retrievedValue.join('\n');
|
||||||
|
} else if (input.name === 'uid') {
|
||||||
|
value = input.value;
|
||||||
|
} else {
|
||||||
|
value = retrievedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The addon input is not present in @buffetjs so we are using the old lib
|
||||||
|
// for the moment that's why we don't want them be passed to buffet
|
||||||
|
// like the other created inputs
|
||||||
|
if (input.type === 'addon') {
|
||||||
|
return (
|
||||||
|
<InputsIndex
|
||||||
|
key={input.name}
|
||||||
|
{...input}
|
||||||
|
type="string"
|
||||||
|
onChange={handleChange}
|
||||||
|
value={value}
|
||||||
|
style={{ marginTop: 8, marginBottom: 11 }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`col-${input.size || 6}`} key={input.name}>
|
||||||
|
<Inputs
|
||||||
|
{...input}
|
||||||
|
modifiedData={modifiedData}
|
||||||
|
addComponentsToDynamicZone={handleClickAddComponentsToDynamicZone}
|
||||||
|
customInputs={{
|
||||||
|
componentIconPicker: ComponentIconPicker,
|
||||||
|
componentSelect: WrapperSelect,
|
||||||
|
creatableSelect: WrapperSelect,
|
||||||
|
customCheckboxWithChildren: CustomCheckbox,
|
||||||
|
booleanBox: BooleanBox,
|
||||||
|
}}
|
||||||
|
isCreating={isCreating}
|
||||||
|
// Props for the componentSelect
|
||||||
|
isCreatingComponentWhileAddingAField={
|
||||||
|
isCreatingComponentWhileAddingAField
|
||||||
|
}
|
||||||
|
// Props for the componentSelect
|
||||||
|
// Since the component is created after adding it to a type
|
||||||
|
// its name and category can't be retrieved from the data manager
|
||||||
|
componentCategoryNeededForAddingAfieldWhileCreatingAComponent={get(
|
||||||
|
componentToCreate,
|
||||||
|
'category',
|
||||||
|
null
|
||||||
|
)}
|
||||||
|
// Props for the componentSelect same explanation
|
||||||
|
componentNameNeededForAddingAfieldWhileCreatingAComponent={get(
|
||||||
|
componentToCreate,
|
||||||
|
'name',
|
||||||
|
null
|
||||||
|
)}
|
||||||
|
isAddingAComponentToAnotherComponent={
|
||||||
|
state.forTarget === 'components' ||
|
||||||
|
state.forTarget === 'component'
|
||||||
|
}
|
||||||
|
value={value}
|
||||||
|
error={isEmpty(errorId) ? null : formatMessage({ id: errorId })}
|
||||||
|
onChange={handleChange}
|
||||||
|
onBlur={() => {}}
|
||||||
|
description={
|
||||||
|
get(input, 'description.id', null)
|
||||||
|
? formatMessage(input.description)
|
||||||
|
: input.description
|
||||||
|
}
|
||||||
|
placeholder={
|
||||||
|
get(input, 'placeholder.id', null)
|
||||||
|
? formatMessage(input.placeholder)
|
||||||
|
: input.placeholder
|
||||||
|
}
|
||||||
|
label={
|
||||||
|
get(input, 'label.id', null)
|
||||||
|
? formatMessage(input.label)
|
||||||
|
: input.label
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
</ModalForm>
|
</ModalForm>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import * as yup from 'yup';
|
import * as yup from 'yup';
|
||||||
import { get, isEmpty, toLower, trim, toNumber } from 'lodash';
|
import { get, isEmpty, toLower, trim, toNumber } from 'lodash';
|
||||||
import { translatedErrors as errorsTrads } from 'strapi-helper-plugin';
|
import { translatedErrors as errorsTrads } from 'strapi-helper-plugin';
|
||||||
@ -8,22 +8,14 @@ import getTrad from '../../../utils/getTrad';
|
|||||||
import { createComponentUid, createUid, nameToSlug } from './createUid';
|
import { createComponentUid, createUid, nameToSlug } from './createUid';
|
||||||
import componentForm from './componentForm';
|
import componentForm from './componentForm';
|
||||||
import fields from './staticFields';
|
import fields from './staticFields';
|
||||||
import {
|
import { NAME_REGEX, ENUM_REGEX, CATEGORY_NAME_REGEX } from './attributesRegexes';
|
||||||
NAME_REGEX,
|
|
||||||
ENUM_REGEX,
|
|
||||||
CATEGORY_NAME_REGEX,
|
|
||||||
} from './attributesRegexes';
|
|
||||||
import RESERVED_NAMES from './reservedNames';
|
import RESERVED_NAMES from './reservedNames';
|
||||||
|
|
||||||
/* eslint-disable indent */
|
/* eslint-disable indent */
|
||||||
/* eslint-disable prefer-arrow-callback */
|
/* eslint-disable prefer-arrow-callback */
|
||||||
|
|
||||||
yup.addMethod(yup.mixed, 'defined', function() {
|
yup.addMethod(yup.mixed, 'defined', function() {
|
||||||
return this.test(
|
return this.test('defined', errorsTrads.required, value => value !== undefined);
|
||||||
'defined',
|
|
||||||
errorsTrads.required,
|
|
||||||
value => value !== undefined
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
yup.addMethod(yup.string, 'unique', function(
|
yup.addMethod(yup.string, 'unique', function(
|
||||||
@ -83,12 +75,7 @@ yup.addMethod(yup.array, 'matchesEnumRegex', function(message) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const ATTRIBUTES_THAT_DONT_HAVE_MIN_MAX_SETTINGS = [
|
const ATTRIBUTES_THAT_DONT_HAVE_MIN_MAX_SETTINGS = ['boolean', 'date', 'enumeration', 'media'];
|
||||||
'boolean',
|
|
||||||
'date',
|
|
||||||
'enumeration',
|
|
||||||
'media',
|
|
||||||
];
|
|
||||||
|
|
||||||
const forms = {
|
const forms = {
|
||||||
attribute: {
|
attribute: {
|
||||||
@ -193,10 +180,7 @@ const forms = {
|
|||||||
.integer()
|
.integer()
|
||||||
.when('maxLength', (maxLength, schema) => {
|
.when('maxLength', (maxLength, schema) => {
|
||||||
if (maxLength) {
|
if (maxLength) {
|
||||||
return schema.max(
|
return schema.max(maxLength, getTrad('error.validation.minSupMax'));
|
||||||
maxLength,
|
|
||||||
getTrad('error.validation.minSupMax')
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return schema;
|
return schema;
|
||||||
@ -232,10 +216,7 @@ const forms = {
|
|||||||
.of(yup.string())
|
.of(yup.string())
|
||||||
.min(1, errorsTrads.min)
|
.min(1, errorsTrads.min)
|
||||||
.matchesEnumRegex(errorsTrads.regex)
|
.matchesEnumRegex(errorsTrads.regex)
|
||||||
.hasNotEmptyValues(
|
.hasNotEmptyValues('Empty strings are not allowed', dataToValidate.enum),
|
||||||
'Empty strings are not allowed',
|
|
||||||
dataToValidate.enum
|
|
||||||
),
|
|
||||||
enumName: yup.string().nullable(),
|
enumName: yup.string().nullable(),
|
||||||
});
|
});
|
||||||
case 'number':
|
case 'number':
|
||||||
@ -256,10 +237,7 @@ const forms = {
|
|||||||
.matches(/^\d*$/)
|
.matches(/^\d*$/)
|
||||||
.when('max', (max, schema) => {
|
.when('max', (max, schema) => {
|
||||||
if (max) {
|
if (max) {
|
||||||
return schema.isInferior(
|
return schema.isInferior(getTrad('error.validation.minSupMax'), max);
|
||||||
getTrad('error.validation.minSupMax'),
|
|
||||||
max
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return schema;
|
return schema;
|
||||||
@ -275,9 +253,7 @@ const forms = {
|
|||||||
let defaultType = yup.number();
|
let defaultType = yup.number();
|
||||||
|
|
||||||
if (dataToValidate.type === 'integer') {
|
if (dataToValidate.type === 'integer') {
|
||||||
defaultType = yup
|
defaultType = yup.number().integer('component.Input.error.validation.integer');
|
||||||
.number()
|
|
||||||
.integer('component.Input.error.validation.integer');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return yup.object().shape({
|
return yup.object().shape({
|
||||||
@ -344,8 +320,7 @@ const forms = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
autoFocus: false,
|
autoFocus: false,
|
||||||
disabled:
|
disabled: targetAttributeValue === null || targetAttributeValue === '-',
|
||||||
targetAttributeValue === null || targetAttributeValue === '-',
|
|
||||||
name: 'targetColumnName',
|
name: 'targetColumnName',
|
||||||
label: '',
|
label: '',
|
||||||
type: 'addon',
|
type: 'addon',
|
||||||
@ -366,12 +341,7 @@ const forms = {
|
|||||||
[fields.required],
|
[fields.required],
|
||||||
[fields.unique],
|
[fields.unique],
|
||||||
];
|
];
|
||||||
const dynamiczoneItems = [
|
const dynamiczoneItems = [[fields.required], [fields.divider], [fields.max], [fields.min]];
|
||||||
[fields.required],
|
|
||||||
[fields.divider],
|
|
||||||
[fields.max],
|
|
||||||
[fields.min],
|
|
||||||
];
|
|
||||||
|
|
||||||
if (type === 'component') {
|
if (type === 'component') {
|
||||||
if (step === '1') {
|
if (step === '1') {
|
||||||
@ -386,11 +356,10 @@ const forms = {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const items = defaultItems.slice();
|
let items = defaultItems.slice();
|
||||||
|
|
||||||
if (type === 'number' && data.type !== 'biginteger') {
|
if (type === 'number' && data.type !== 'biginteger') {
|
||||||
const step =
|
const step = data.type === 'decimal' || data.type === 'float' ? 'any' : '1';
|
||||||
data.type === 'decimal' || data.type === 'float' ? 'any' : '1';
|
|
||||||
|
|
||||||
items.splice(0, 1, [
|
items.splice(0, 1, [
|
||||||
{
|
{
|
||||||
@ -441,10 +410,7 @@ const forms = {
|
|||||||
].concat(
|
].concat(
|
||||||
data.enum
|
data.enum
|
||||||
? data.enum
|
? data.enum
|
||||||
.filter(
|
.filter((val, index) => data.enum.indexOf(val) === index && !isEmpty(val))
|
||||||
(val, index) =>
|
|
||||||
data.enum.indexOf(val) === index && !isEmpty(val)
|
|
||||||
)
|
|
||||||
.map(val => (
|
.map(val => (
|
||||||
<option key={val} value={val}>
|
<option key={val} value={val}>
|
||||||
{val}
|
{val}
|
||||||
@ -463,9 +429,7 @@ const forms = {
|
|||||||
type: 'text',
|
type: 'text',
|
||||||
validations: {},
|
validations: {},
|
||||||
description: {
|
description: {
|
||||||
id: getTrad(
|
id: getTrad('form.attribute.item.enumeration.graphql.description'),
|
||||||
'form.attribute.item.enumeration.graphql.description'
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
@ -473,13 +437,20 @@ const forms = {
|
|||||||
items.splice(0, 1, [
|
items.splice(0, 1, [
|
||||||
{
|
{
|
||||||
...fields.default,
|
...fields.default,
|
||||||
// type: data.type || 'date',
|
|
||||||
type: 'date',
|
type: 'date',
|
||||||
value: null,
|
value: null,
|
||||||
withDefaultValue: false,
|
withDefaultValue: false,
|
||||||
disabled: data.type !== 'date',
|
disabled: data.type !== 'date',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
} else if (type === 'uid') {
|
||||||
|
const uidItems = [
|
||||||
|
[{ ...fields.default, disabled: Boolean(data.targetField), type: 'text' }],
|
||||||
|
[fields.divider],
|
||||||
|
[fields.required],
|
||||||
|
];
|
||||||
|
|
||||||
|
items = uidItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ATTRIBUTES_THAT_DONT_HAVE_MIN_MAX_SETTINGS.includes(type)) {
|
if (!ATTRIBUTES_THAT_DONT_HAVE_MIN_MAX_SETTINGS.includes(type)) {
|
||||||
@ -490,11 +461,7 @@ const forms = {
|
|||||||
name: type === 'number' ? 'max' : 'maxLength',
|
name: type === 'number' ? 'max' : 'maxLength',
|
||||||
type: 'customCheckboxWithChildren',
|
type: 'customCheckboxWithChildren',
|
||||||
label: {
|
label: {
|
||||||
id: getTrad(
|
id: getTrad(`form.attribute.item.maximum${type === 'number' ? '' : 'Length'}`),
|
||||||
`form.attribute.item.maximum${
|
|
||||||
type === 'number' ? '' : 'Length'
|
|
||||||
}`
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
|
|
||||||
validations: {},
|
validations: {},
|
||||||
@ -506,11 +473,7 @@ const forms = {
|
|||||||
name: type === 'number' ? 'min' : 'minLength',
|
name: type === 'number' ? 'min' : 'minLength',
|
||||||
type: 'customCheckboxWithChildren',
|
type: 'customCheckboxWithChildren',
|
||||||
label: {
|
label: {
|
||||||
id: getTrad(
|
id: getTrad(`form.attribute.item.minimum${type === 'number' ? '' : 'Length'}`),
|
||||||
`form.attribute.item.minimum${
|
|
||||||
type === 'number' ? '' : 'Length'
|
|
||||||
}`
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
validations: {},
|
validations: {},
|
||||||
},
|
},
|
||||||
@ -534,7 +497,7 @@ const forms = {
|
|||||||
items,
|
items,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
base(data, type, step) {
|
base(data, type, step, actionType, attributes) {
|
||||||
if (type === 'relation') {
|
if (type === 'relation') {
|
||||||
return {
|
return {
|
||||||
items: [
|
items: [
|
||||||
@ -552,9 +515,7 @@ const forms = {
|
|||||||
if (type === 'component' && step === '1') {
|
if (type === 'component' && step === '1') {
|
||||||
const itemsToConcat =
|
const itemsToConcat =
|
||||||
data.createComponent === true
|
data.createComponent === true
|
||||||
? [[{ type: 'spacer' }]].concat(
|
? [[{ type: 'spacer' }]].concat(componentForm.base('componentToCreate.'))
|
||||||
componentForm.base('componentToCreate.')
|
|
||||||
)
|
|
||||||
: [[{ type: 'spacer' }]];
|
: [[{ type: 'spacer' }]];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -581,19 +542,13 @@ const forms = {
|
|||||||
size: 12,
|
size: 12,
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
headerId: getTrad(
|
headerId: getTrad('form.attribute.component.option.repeatable'),
|
||||||
'form.attribute.component.option.repeatable'
|
descriptionId: getTrad('form.attribute.component.option.repeatable.description'),
|
||||||
),
|
|
||||||
descriptionId: getTrad(
|
|
||||||
'form.attribute.component.option.repeatable.description'
|
|
||||||
),
|
|
||||||
value: true,
|
value: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
headerId: getTrad('form.attribute.component.option.single'),
|
headerId: getTrad('form.attribute.component.option.single'),
|
||||||
descriptionId: getTrad(
|
descriptionId: getTrad('form.attribute.component.option.single.description'),
|
||||||
'form.attribute.component.option.single.description'
|
|
||||||
),
|
|
||||||
value: false,
|
value: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -615,9 +570,7 @@ const forms = {
|
|||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
headerId: getTrad(
|
headerId: getTrad(
|
||||||
`form.attribute.${type}.option.${
|
`form.attribute.${type}.option.${type === 'text' ? 'short-text' : 'multiple'}`
|
||||||
type === 'text' ? 'short-text' : 'multiple'
|
|
||||||
}`
|
|
||||||
),
|
),
|
||||||
descriptionId: getTrad(
|
descriptionId: getTrad(
|
||||||
`form.attribute.${type}.option.${
|
`form.attribute.${type}.option.${
|
||||||
@ -628,9 +581,7 @@ const forms = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
headerId: getTrad(
|
headerId: getTrad(
|
||||||
`form.attribute.${type}.option.${
|
`form.attribute.${type}.option.${type === 'text' ? 'long-text' : 'single'}`
|
||||||
type === 'text' ? 'long-text' : 'single'
|
|
||||||
}`
|
|
||||||
),
|
),
|
||||||
descriptionId: getTrad(
|
descriptionId: getTrad(
|
||||||
`form.attribute.${type}.option.${
|
`form.attribute.${type}.option.${
|
||||||
@ -751,6 +702,35 @@ const forms = {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type === 'uid') {
|
||||||
|
const options = Object.keys(attributes)
|
||||||
|
.filter(key => attributes[key].type === 'string')
|
||||||
|
.map(key => ({ id: key, value: key }));
|
||||||
|
|
||||||
|
items[0].push({
|
||||||
|
label: {
|
||||||
|
id: getTrad('modalForm.attribute.target-field'),
|
||||||
|
},
|
||||||
|
name: 'targetField',
|
||||||
|
type: 'select',
|
||||||
|
options: [{ id: getTrad('none'), value: '' }, ...options].map((option, index) => (
|
||||||
|
// eslint-disable-next-line react/no-array-index-key
|
||||||
|
<Fragment key={index}>
|
||||||
|
{index === 0 ? (
|
||||||
|
<FormattedMessage id={option.id}>
|
||||||
|
{msg => <option value={option.value}>{msg}</option>}
|
||||||
|
</FormattedMessage>
|
||||||
|
) : (
|
||||||
|
<option value={option.value}>{option.value}</option>
|
||||||
|
)}
|
||||||
|
</Fragment>
|
||||||
|
)),
|
||||||
|
validations: {
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
items,
|
items,
|
||||||
};
|
};
|
||||||
@ -814,15 +794,11 @@ const forms = {
|
|||||||
type: 'booleanBox',
|
type: 'booleanBox',
|
||||||
size: 12,
|
size: 12,
|
||||||
onChangeCallback: () =>
|
onChangeCallback: () =>
|
||||||
strapi.notification.info(
|
strapi.notification.info(getTrad('contentType.kind.change.warning')),
|
||||||
getTrad('contentType.kind.change.warning')
|
|
||||||
),
|
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
headerId: getTrad('menu.section.models.name.singular'),
|
headerId: getTrad('menu.section.models.name.singular'),
|
||||||
descriptionId: getTrad(
|
descriptionId: getTrad('form.button.collection-type.description'),
|
||||||
'form.button.collection-type.description'
|
|
||||||
),
|
|
||||||
value: 'collectionType',
|
value: 'collectionType',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -860,12 +836,7 @@ const forms = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
component: {
|
component: {
|
||||||
schema(
|
schema(alreadyTakenAttributes, componentCategory, isEditing = false, compoUid = null) {
|
||||||
alreadyTakenAttributes,
|
|
||||||
componentCategory,
|
|
||||||
isEditing = false,
|
|
||||||
compoUid = null
|
|
||||||
) {
|
|
||||||
const takenNames = isEditing
|
const takenNames = isEditing
|
||||||
? alreadyTakenAttributes.filter(uid => uid !== compoUid)
|
? alreadyTakenAttributes.filter(uid => uid !== compoUid)
|
||||||
: alreadyTakenAttributes;
|
: alreadyTakenAttributes;
|
||||||
@ -873,12 +844,7 @@ const forms = {
|
|||||||
return yup.object().shape({
|
return yup.object().shape({
|
||||||
name: yup
|
name: yup
|
||||||
.string()
|
.string()
|
||||||
.unique(
|
.unique(errorsTrads.unique, takenNames, createComponentUid, componentCategory)
|
||||||
errorsTrads.unique,
|
|
||||||
takenNames,
|
|
||||||
createComponentUid,
|
|
||||||
componentCategory
|
|
||||||
)
|
|
||||||
.isAllowed(getTrad('error.contentTypeName.reserved-name'))
|
.isAllowed(getTrad('error.contentTypeName.reserved-name'))
|
||||||
.required(errorsTrads.required),
|
.required(errorsTrads.required),
|
||||||
category: yup
|
category: yup
|
||||||
@ -911,9 +877,7 @@ const forms = {
|
|||||||
const isCreatingComponent = get(data, 'createComponent', false);
|
const isCreatingComponent = get(data, 'createComponent', false);
|
||||||
|
|
||||||
const itemsToConcat = isCreatingComponent
|
const itemsToConcat = isCreatingComponent
|
||||||
? [[{ type: 'spacer' }]].concat(
|
? [[{ type: 'spacer' }]].concat(componentForm.base('componentToCreate.'))
|
||||||
componentForm.base('componentToCreate.')
|
|
||||||
)
|
|
||||||
: [
|
: [
|
||||||
[{ type: 'spacer' }],
|
[{ type: 'spacer' }],
|
||||||
[
|
[
|
||||||
|
|||||||
@ -257,8 +257,9 @@ const ListView = () => {
|
|||||||
};
|
};
|
||||||
const goToCMSettingsPage = () => {
|
const goToCMSettingsPage = () => {
|
||||||
const endPoint = isInContentTypeView
|
const endPoint = isInContentTypeView
|
||||||
? `/plugins/content-manager/${targetUid}/ctm-configurations/edit-settings/content-types`
|
? `/plugins/content-manager/${contentTypeKind}/${targetUid}/ctm-configurations/edit-settings/content-types`
|
||||||
: `/plugins/content-manager/ctm-configurations/edit-settings/components/${targetUid}/`;
|
: `/plugins/content-manager/ctm-configurations/edit-settings/components/${targetUid}/`;
|
||||||
|
|
||||||
push(endPoint);
|
push(endPoint);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -28,7 +28,7 @@
|
|||||||
"attribute.text": "Text",
|
"attribute.text": "Text",
|
||||||
"attribute.text.description": "Krátký nebo delší text",
|
"attribute.text.description": "Krátký nebo delší text",
|
||||||
"attribute.time": "Čas",
|
"attribute.time": "Čas",
|
||||||
"attribute.uid": "Uuid",
|
"attribute.uid": "Uid",
|
||||||
"attribute.uid.description": "Unikátní identifikátor",
|
"attribute.uid.description": "Unikátní identifikátor",
|
||||||
"button.attributes.add.another": "Přidat další pole",
|
"button.attributes.add.another": "Přidat další pole",
|
||||||
"button.component.add": "Přidat komponent",
|
"button.component.add": "Přidat komponent",
|
||||||
|
|||||||
@ -28,8 +28,9 @@
|
|||||||
"attribute.text.description": "Small or long text like title or description",
|
"attribute.text.description": "Small or long text like title or description",
|
||||||
"attribute.text": "Text",
|
"attribute.text": "Text",
|
||||||
"attribute.time": "Time",
|
"attribute.time": "Time",
|
||||||
|
"attribute.timestamp": "Timestamp",
|
||||||
"attribute.uid.description": "Unique identifier",
|
"attribute.uid.description": "Unique identifier",
|
||||||
"attribute.uid": "Uuid",
|
"attribute.uid": "Uid",
|
||||||
"button.attributes.add.another": "Add another field",
|
"button.attributes.add.another": "Add another field",
|
||||||
"button.component.add": "Add a component",
|
"button.component.add": "Add a component",
|
||||||
"button.component.create": "Create new component",
|
"button.component.create": "Create new component",
|
||||||
@ -118,6 +119,7 @@
|
|||||||
"menu.section.models.name.singular": "Collection Type",
|
"menu.section.models.name.singular": "Collection Type",
|
||||||
"menu.section.single-types.name.plural": "Single Types",
|
"menu.section.single-types.name.plural": "Single Types",
|
||||||
"menu.section.single-types.name.singular": "Single Type",
|
"menu.section.single-types.name.singular": "Single Type",
|
||||||
|
"modalForm.attribute.target-field": "Attached field",
|
||||||
"modalForm.attribute.form.base.name.description": "No space is allowed for the name of the attribute",
|
"modalForm.attribute.form.base.name.description": "No space is allowed for the name of the attribute",
|
||||||
"modalForm.attribute.form.base.name": "Name",
|
"modalForm.attribute.form.base.name": "Name",
|
||||||
"modalForm.attribute.text.type-selection": "Type",
|
"modalForm.attribute.text.type-selection": "Type",
|
||||||
@ -139,6 +141,7 @@
|
|||||||
"modalForm.sub-header.chooseAttribute.contentType": "Select a field for your collection type",
|
"modalForm.sub-header.chooseAttribute.contentType": "Select a field for your collection type",
|
||||||
"modelPage.attribute.relationWith": "Relation with",
|
"modelPage.attribute.relationWith": "Relation with",
|
||||||
"modelPage.contentHeader.emptyDescription.description": "There is no description",
|
"modelPage.contentHeader.emptyDescription.description": "There is no description",
|
||||||
|
"none": "None",
|
||||||
"notification.info.creating.notSaved": "Please save your work before creating a new collection type or component",
|
"notification.info.creating.notSaved": "Please save your work before creating a new collection type or component",
|
||||||
"notification.info.autoreaload-disable": "The autoReload feature is required to use this plugin. Start your server with `strapi develop`",
|
"notification.info.autoreaload-disable": "The autoReload feature is required to use this plugin. Start your server with `strapi develop`",
|
||||||
"plugin.description.long": "Modelize the data structure of your API. Create new fields and relations in just a minute. The files are automatically created and updated in your project.",
|
"plugin.description.long": "Modelize the data structure of your API. Create new fields and relations in just a minute. The files are automatically created and updated in your project.",
|
||||||
|
|||||||
@ -50,7 +50,10 @@
|
|||||||
"menu.section.models.name.singular": "Collection",
|
"menu.section.models.name.singular": "Collection",
|
||||||
"menu.section.single-types.name.plural": "Single Types",
|
"menu.section.single-types.name.plural": "Single Types",
|
||||||
"menu.section.single-types.name.singular": "Single Type",
|
"menu.section.single-types.name.singular": "Single Type",
|
||||||
|
"modalForm.attribute.target-field": "Champ associé",
|
||||||
|
"modalForm.attribute.target-field.none": "Aucun",
|
||||||
"modalForm.singleType.header-create": "Créer un single type",
|
"modalForm.singleType.header-create": "Créer un single type",
|
||||||
|
"none": "Aucun",
|
||||||
"button.single-types.create": "Créer un single type",
|
"button.single-types.create": "Créer un single type",
|
||||||
"modelPage.attribute.relationWith": "Relation avec",
|
"modelPage.attribute.relationWith": "Relation avec",
|
||||||
"modelPage.contentHeader.emptyDescription.description": "Il n'y a pas de description",
|
"modelPage.contentHeader.emptyDescription.description": "Il n'y a pas de description",
|
||||||
|
|||||||
@ -68,7 +68,7 @@
|
|||||||
"attribute.richtext.description": "Edytor tekstu z możliwością formatowania",
|
"attribute.richtext.description": "Edytor tekstu z możliwością formatowania",
|
||||||
"attribute.text.description": "Krótki lub długi tekst jak tytuł lub opis",
|
"attribute.text.description": "Krótki lub długi tekst jak tytuł lub opis",
|
||||||
"attribute.time": "Czas",
|
"attribute.time": "Czas",
|
||||||
"attribute.uid": "Uuid",
|
"attribute.uid": "Uid",
|
||||||
"attribute.uid.description": "Unikalny identyfikator",
|
"attribute.uid.description": "Unikalny identyfikator",
|
||||||
"button.attributes.add.another": "Dodaj kolejne pole",
|
"button.attributes.add.another": "Dodaj kolejne pole",
|
||||||
"button.component.add": "Dodaj komponent",
|
"button.component.add": "Dodaj komponent",
|
||||||
|
|||||||
@ -28,7 +28,7 @@
|
|||||||
"attribute.text": "Text",
|
"attribute.text": "Text",
|
||||||
"attribute.text.description": "Простой текст для заголовка или описания",
|
"attribute.text.description": "Простой текст для заголовка или описания",
|
||||||
"attribute.time": "Time",
|
"attribute.time": "Time",
|
||||||
"attribute.uid": "Uuid",
|
"attribute.uid": "Uid",
|
||||||
"attribute.uid.description": "Уникальный идентификатор",
|
"attribute.uid.description": "Уникальный идентификатор",
|
||||||
"button.attributes.add.another": "Ещё поле",
|
"button.attributes.add.another": "Ещё поле",
|
||||||
"button.component.add": "Добавить компонент",
|
"button.component.add": "Добавить компонент",
|
||||||
|
|||||||
@ -28,7 +28,7 @@
|
|||||||
"attribute.text": "Text",
|
"attribute.text": "Text",
|
||||||
"attribute.text.description": "Krátky alebo dlhší text",
|
"attribute.text.description": "Krátky alebo dlhší text",
|
||||||
"attribute.time": "Čas",
|
"attribute.time": "Čas",
|
||||||
"attribute.uid": "Uuid",
|
"attribute.uid": "Uid",
|
||||||
"attribute.uid.description": "Unikátny identifikátor",
|
"attribute.uid.description": "Unikátny identifikátor",
|
||||||
"button.attributes.add.another": "Pridať ďalšie políčko",
|
"button.attributes.add.another": "Pridať ďalšie políčko",
|
||||||
"button.component.add": "Pridať komponent",
|
"button.component.add": "Pridať komponent",
|
||||||
|
|||||||
@ -28,7 +28,7 @@
|
|||||||
"attribute.text": "文本",
|
"attribute.text": "文本",
|
||||||
"attribute.text.description": "较短或较长的文字,例如标题或说明",
|
"attribute.text.description": "较短或较长的文字,例如标题或说明",
|
||||||
"attribute.time": "时间",
|
"attribute.time": "时间",
|
||||||
"attribute.uid": "Uuid",
|
"attribute.uid": "Uid",
|
||||||
"attribute.uid.description": "唯一标识符",
|
"attribute.uid.description": "唯一标识符",
|
||||||
"button.attributes.add.another": "添加一个新字段",
|
"button.attributes.add.another": "添加一个新字段",
|
||||||
"button.component.add": "添加组件",
|
"button.component.add": "添加组件",
|
||||||
|
|||||||
@ -5,6 +5,7 @@ const getAttributeDisplayedType = type => {
|
|||||||
case 'date':
|
case 'date':
|
||||||
case 'datetime':
|
case 'datetime':
|
||||||
case 'time':
|
case 'time':
|
||||||
|
case 'timestamp':
|
||||||
displayedType = 'date';
|
displayedType = 'date';
|
||||||
break;
|
break;
|
||||||
case 'integer':
|
case 'integer':
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user