Handle edit for components

This commit is contained in:
soupette 2019-12-05 16:27:55 +01:00
parent fac79f1785
commit e6ff857798
23 changed files with 490 additions and 47 deletions

View File

@ -7,14 +7,17 @@
}, },
"options": { "options": {
"increments": true, "increments": true,
"timestamps": ["created_at", "updated_at"], "timestamps": [
"created_at",
"updated_at"
],
"comment": "" "comment": ""
}, },
"attributes": { "attributes": {
"full_name": { "full_name": {
"type": "string" "type": "string"
}, },
"postal_code": { "postal_coder": {
"type": "string" "type": "string"
}, },
"city": { "city": {
@ -24,18 +27,30 @@
"type": "json", "type": "json",
"required": true "required": true
}, },
"country": { "addresses": {
"model": "country" "collection": "address",
"via": "address"
},
"address": {
"model": "address",
"via": "addresses"
}, },
"cover": { "cover": {
"model": "file", "model": "file",
"via": "related", "via": "related",
"plugin": "upload" "plugin": "upload",
"required": false
}, },
"images": { "images": {
"collection": "file", "collection": "file",
"via": "related", "via": "related",
"plugin": "upload" "plugin": "upload",
"required": false
},
"teter": {
"type": "component",
"repeatable": false,
"component": "default.teter"
} }
} }
} }

View File

@ -13,6 +13,10 @@
"attributes": { "attributes": {
"name": { "name": {
"type": "string" "type": "string"
},
"toto": {
"via": "category",
"model": "toto"
} }
} }
} }

View File

@ -7,7 +7,10 @@
}, },
"options": { "options": {
"increments": true, "increments": true,
"timestamps": ["created_at", "updated_at"], "timestamps": [
"created_at",
"updated_at"
],
"comment": "" "comment": ""
}, },
"attributes": { "attributes": {
@ -21,6 +24,10 @@
"maxLength": 3, "maxLength": 3,
"unique": true, "unique": true,
"minLength": 2 "minLength": 2
},
"toto": {
"via": "countries",
"model": "toto"
} }
} }
} }

View File

@ -26,6 +26,10 @@
"like_right": { "like_right": {
"model": "like", "model": "like",
"via": "like_left" "via": "like_left"
},
"totos": {
"via": "like",
"collection": "toto"
} }
} }
} }

View File

@ -21,6 +21,10 @@
"restaurant": { "restaurant": {
"model": "restaurant", "model": "restaurant",
"via": "menu" "via": "menu"
},
"totos": {
"via": "menus",
"collection": "toto"
} }
} }
} }

View File

@ -0,0 +1,52 @@
{
"routes": [
{
"method": "GET",
"path": "/totos",
"handler": "toto.find",
"config": {
"policies": []
}
},
{
"method": "GET",
"path": "/totos/count",
"handler": "toto.count",
"config": {
"policies": []
}
},
{
"method": "GET",
"path": "/totos/:id",
"handler": "toto.findOne",
"config": {
"policies": []
}
},
{
"method": "POST",
"path": "/totos",
"handler": "toto.create",
"config": {
"policies": []
}
},
{
"method": "PUT",
"path": "/totos/:id",
"handler": "toto.update",
"config": {
"policies": []
}
},
{
"method": "DELETE",
"path": "/totos/:id",
"handler": "toto.delete",
"config": {
"policies": []
}
}
]
}

View File

@ -0,0 +1,8 @@
'use strict';
/**
* Read the documentation (https://strapi.io/documentation/3.0.0-beta.x/concepts/controllers.html#core-controllers)
* to customize this controller
*/
module.exports = {};

View File

@ -0,0 +1,44 @@
'use strict';
/**
* Lifecycle callbacks for the `toto` model.
*/
module.exports = {
// Before saving a value.
// Fired before an `insert` or `update` query.
// beforeSave: async (model, attrs, options) => {},
// After saving a value.
// Fired after an `insert` or `update` query.
// afterSave: async (model, response, options) => {},
// Before fetching a value.
// Fired before a `fetch` operation.
// beforeFetch: async (model, columns, options) => {},
// After fetching a value.
// Fired after a `fetch` operation.
// afterFetch: async (model, response, options) => {},
// Before fetching all values.
// Fired before a `fetchAll` operation.
// beforeFetchAll: async (model, columns, options) => {},
// After fetching all values.
// Fired after a `fetchAll` operation.
// afterFetchAll: async (model, response, options) => {},
// Before creating a value.
// Fired before an `insert` query.
// beforeCreate: async (model, attrs, options) => {},
// After creating a value.
// Fired after an `insert` query.
// afterCreate: async (model, attrs, options) => {},
// Before updating a value.
// Fired before an `update` query.
// beforeUpdate: async (model, attrs, options) => {},
// After updating a value.
// Fired after an `update` query.
// afterUpdate: async (model, attrs, options) => {},
// Before destroying a value.
// Fired before a `delete` query.
// beforeDestroy: async (model, attrs, options) => {},
// After destroying a value.
// Fired after a `delete` query.
// afterDestroy: async (model, attrs, options) => {}
};

View File

@ -0,0 +1,86 @@
{
"connection": "default",
"collectionName": "totos",
"info": {
"name": "toto"
},
"attributes": {
"totosmanytomanyright": {
"collection": "toto",
"via": "totosmanytomany"
},
"totoonewayLeft": {
"model": "toto",
"via": "totoonewayRight"
},
"totosonetomanyLeft": {
"collection": "toto",
"via": "totoonetomanyRight"
},
"name": {
"type": "string",
"default": "something"
},
"menusections": {
"collection": "menusection"
},
"totosmanytooneright": {
"collection": "toto",
"via": "totomanytooneleft"
},
"totoonetomanyRight": {
"model": "toto",
"via": "totosonetomanyLeft"
},
"totosmanyway": {
"collection": "toto"
},
"countries": {
"collection": "country",
"via": "toto"
},
"menus": {
"collection": "menu",
"via": "totos",
"dominant": true
},
"address": {
"model": "address"
},
"toto": {
"model": "toto"
},
"totosmanytomany": {
"collection": "toto",
"via": "totosmanytomanyright",
"dominant": true
},
"like": {
"model": "like",
"via": "totos"
},
"compoField": {
"type": "component",
"repeatable": false,
"component": "compo.newcompo"
},
"totoonewayRight": {
"model": "toto",
"via": "totoonewayLeft"
},
"totomanytooneleft": {
"model": "toto",
"via": "totosmanytooneright"
},
"category": {
"model": "category",
"via": "toto"
},
"mainCompoField": {
"type": "component",
"repeatable": true,
"component": "nested.compo-with-nested",
"max": 2
}
}
}

View File

@ -0,0 +1,8 @@
'use strict';
/**
* Read the documentation (https://strapi.io/documentation/3.0.0-beta.x/concepts/services.html#core-services)
* to customize this service
*/
module.exports = {};

View File

@ -0,0 +1,14 @@
{
"connection": "default",
"collectionName": "components_compo_newcompos",
"info": {
"name": "newcompo",
"icon": "address-book"
},
"attributes": {
"name": {
"type": "string",
"default": "something"
}
}
}

View File

@ -0,0 +1,13 @@
{
"connection": "default",
"collectionName": "components_default_teters",
"info": {
"name": "teter",
"icon": "arrow-alt-circle-up"
},
"attributes": {
"terttterterter": {
"type": "string"
}
}
}

View File

@ -0,0 +1,21 @@
{
"connection": "default",
"collectionName": "components_nested_compo_with_nesteds",
"info": {
"name": "compo with nested",
"icon": "address-card"
},
"attributes": {
"something": {
"type": "string",
"default": "default value",
"required": true,
"unique": true
},
"nestcompofield": {
"type": "component",
"repeatable": false,
"component": "nested.nested-compo"
}
}
}

View File

@ -0,0 +1,18 @@
{
"connection": "default",
"collectionName": "components_nested_nested_compos",
"info": {
"name": "nested compo",
"icon": "angle-double-down"
},
"attributes": {
"name": {
"type": "string",
"default": "nested default",
"unique": true
},
"email": {
"type": "email"
}
}
}

View File

@ -12,26 +12,37 @@ import List from '../List';
import convertAttrObjToArray from '../../utils/convertAttrObjToArray'; import convertAttrObjToArray from '../../utils/convertAttrObjToArray';
import useDataManager from '../../hooks/useDataManager'; import useDataManager from '../../hooks/useDataManager';
function ComponentList({ customRowComponent, component }) { function ComponentList({
const { modifiedData } = useDataManager(); customRowComponent,
component,
mainTypeName,
const getComponentSchema = componentName => { firstLoopComponentName,
return get(modifiedData, ['components', componentName], { firstLoopComponentUid,
}) {
const { modifiedData } = useDataManager();
const {
schema: { name: componentName, attributes },
} = get(modifiedData, ['components', component], {
schema: { attributes: {} }, schema: { attributes: {} },
}); });
};
const {
schema: { attributes },
} = getComponentSchema(component);
return ( return (
<tr className="component-row"> <tr className="component-row">
<td colSpan={12}> <td colSpan={12}>
{List({ <List
customRowComponent, customRowComponent={customRowComponent}
items: convertAttrObjToArray(attributes), items={convertAttrObjToArray(attributes)}
})} targetUid={component}
mainTypeName={mainTypeName}
firstLoopComponentName={firstLoopComponentName || componentName}
firstLoopComponentUid={firstLoopComponentUid || component}
editTarget="components"
secondLoopComponentName={
firstLoopComponentName ? componentName : null
}
secondLoopComponentUid={firstLoopComponentUid ? component : null}
/>
</td> </td>
</tr> </tr>
); );
@ -45,6 +56,10 @@ ComponentList.defaultProps = {
ComponentList.propTypes = { ComponentList.propTypes = {
component: PropTypes.string, component: PropTypes.string,
customRowComponent: PropTypes.func, customRowComponent: PropTypes.func,
firstLoopComponentName: PropTypes.string,
firstLoopComponentUid: PropTypes.string,
mainTypeName: PropTypes.string.isRequired,
targetUid: PropTypes.string.isRequired,
}; };
export default ComponentList; export default ComponentList;

View File

@ -21,6 +21,14 @@ function List({
items, items,
addField, addField,
addComponentToDZ, addComponentToDZ,
targetUid,
mainTypeName,
editTarget,
firstLoopComponentName,
firstLoopComponentUid,
secondLoopComponentName,
secondLoopComponentUid,
}) { }) {
const { formatMessage } = useGlobalContext(); const { formatMessage } = useGlobalContext();
const addButtonProps = { const addButtonProps = {
@ -36,14 +44,36 @@ function List({
<tbody> <tbody>
{items.map(item => { {items.map(item => {
const { type } = item; const { type } = item;
const CustomRow = customRowComponent;
return ( return (
<React.Fragment key={JSON.stringify(item)}> <React.Fragment key={JSON.stringify(item)}>
{customRowComponent(item)} <CustomRow
{...item}
targetUid={targetUid}
// NEW props
mainTypeName={mainTypeName}
editTarget={editTarget}
firstLoopComponentName={firstLoopComponentName}
firstLoopComponentUid={firstLoopComponentUid}
secondLoopComponentName={secondLoopComponentName}
secondLoopComponentUid={secondLoopComponentUid}
/>
{type === 'component' && ( {type === 'component' && (
<ComponentList <ComponentList
{...item} {...item}
// mainDataType={dataType}
// mainDataTypeName={dataTypeName}
customRowComponent={customRowComponent} customRowComponent={customRowComponent}
targetUid={targetUid}
// NEW PROPS
mainTypeName={mainTypeName}
editTarget={editTarget}
firstLoopComponentName={firstLoopComponentName}
firstLoopComponentUid={firstLoopComponentUid}
/> />
)} )}
@ -70,6 +100,11 @@ List.defaultProps = {
className: null, className: null,
customRowComponent: null, customRowComponent: null,
items: [], items: [],
firstLoopComponentName: null,
firstLoopComponentUid: null,
secondLoopComponentName: null,
secondLoopComponentUid: null,
}; };
List.propTypes = { List.propTypes = {
@ -77,7 +112,16 @@ List.propTypes = {
addComponentToDZ: PropTypes.func, addComponentToDZ: PropTypes.func,
className: PropTypes.string, className: PropTypes.string,
customRowComponent: PropTypes.func, customRowComponent: PropTypes.func,
// dataType: PropTypes.string.isRequired,
// dataTypeName: PropTypes.string.isRequired,
editTarget: PropTypes.string.isRequired,
firstLoopComponentName: PropTypes.string,
firstLoopComponentUid: PropTypes.string,
items: PropTypes.instanceOf(Array), items: PropTypes.instanceOf(Array),
mainTypeName: PropTypes.string.isRequired,
secondLoopComponentName: PropTypes.string,
secondLoopComponentUid: PropTypes.string,
targetUid: PropTypes.string.isRequired,
}; };
export default List; export default List;

View File

@ -14,11 +14,19 @@ function ListRow({
attributeName, attributeName,
configurable, configurable,
name, name,
nature,
onClick, onClick,
onClickDelete, onClickDelete,
plugin, plugin,
target, target,
targetUid,
type, type,
mainTypeName,
editTarget,
firstLoopComponentName,
firstLoopComponentUid,
secondLoopComponentName,
secondLoopComponentUid,
}) { }) {
const ico = ['integer', 'biginteger', 'float', 'decimal'].includes(type) const ico = ['integer', 'biginteger', 'float', 'decimal'].includes(type)
? 'number' ? 'number'
@ -35,7 +43,31 @@ function ListRow({
const handleClick = () => { const handleClick = () => {
if (configurable !== false) { if (configurable !== false) {
onClick(attributeName, name, type, name); const attrType = nature ? 'relation' : type;
let headerDisplayName = mainTypeName;
if (firstLoopComponentName) {
headerDisplayName = firstLoopComponentName;
}
if (secondLoopComponentUid) {
headerDisplayName = secondLoopComponentName;
}
onClick(
// Tells where the attribute is located in the main modifiedData object : contentType, component or components
editTarget,
// main data type uid
secondLoopComponentUid || firstLoopComponentUid || targetUid,
// Name of the attribute
name,
// Type of the attribute
attrType,
headerDisplayName,
firstLoopComponentUid ? mainTypeName : null,
secondLoopComponentName ? firstLoopComponentName : null,
secondLoopComponentUid ? firstLoopComponentUid : null
);
} }
}; };
@ -102,21 +134,35 @@ function ListRow({
ListRow.defaultProps = { ListRow.defaultProps = {
configurable: true, configurable: true,
firstLoopComponentName: null,
firstLoopComponentUid: null,
nature: null,
onClick: () => {}, onClick: () => {},
onClickDelete: () => {}, onClickDelete: () => {},
plugin: null, plugin: null,
secondLoopComponentName: null,
secondLoopComponentUid: null,
target: null, target: null,
targetUid: null,
type: null, type: null,
}; };
ListRow.propTypes = { ListRow.propTypes = {
attributeName: PropTypes.string.isRequired, attributeName: PropTypes.string.isRequired,
configurable: PropTypes.bool, configurable: PropTypes.bool,
editTarget: PropTypes.string.isRequired,
firstLoopComponentName: PropTypes.string,
firstLoopComponentUid: PropTypes.string,
mainTypeName: PropTypes.string.isRequired,
name: PropTypes.string.isRequired, name: PropTypes.string.isRequired,
nature: PropTypes.string,
onClick: PropTypes.func, onClick: PropTypes.func,
onClickDelete: PropTypes.func, onClickDelete: PropTypes.func,
plugin: PropTypes.string, plugin: PropTypes.string,
secondLoopComponentName: PropTypes.string,
secondLoopComponentUid: PropTypes.string,
target: PropTypes.string, target: PropTypes.string,
targetUid: PropTypes.string,
type: PropTypes.string, type: PropTypes.string,
}; };

View File

@ -34,8 +34,6 @@ const ModalHeader = ({
// iconName = toLower(iconType); // iconName = toLower(iconType);
} }
console.log(iconName);
return ( return (
<section> <section>
<HeaderModalTitle style={{ textTransform: 'none' }}> <HeaderModalTitle style={{ textTransform: 'none' }}>

View File

@ -29,7 +29,6 @@ const WrapperSelect = ({ error, label, name, type, ...rest }) => {
}; };
}, },
option: (base, state) => { option: (base, state) => {
console.log({ base, state });
return { return {
...base, ...base,
// backgroundColor: state.isSelected ? '#DEEBFF' : base.backgroundColor, // backgroundColor: state.isSelected ? '#DEEBFF' : base.backgroundColor,

View File

@ -316,6 +316,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
components, components,
isInContentTypeView isInContentTypeView
); );
const dataShape = orderAllDataAttributesWithImmutable( const dataShape = orderAllDataAttributesWithImmutable(
newSchemaToSet, newSchemaToSet,
isInContentTypeView isInContentTypeView

View File

@ -202,6 +202,7 @@ const reducer = (state, action) => {
initialAttribute, initialAttribute,
} = action; } = action;
let newState = state; let newState = state;
const initialAttributeName = get(initialAttribute, ['name'], ''); const initialAttributeName = get(initialAttribute, ['name'], '');
const pathToDataToEdit = ['component', 'contentType'].includes(forTarget) const pathToDataToEdit = ['component', 'contentType'].includes(forTarget)
? [forTarget] ? [forTarget]
@ -270,6 +271,10 @@ const reducer = (state, action) => {
hadInternalRelation && hadInternalRelation &&
didCreateInternalRelation && didCreateInternalRelation &&
isEditingRelation; 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 ( if (
@ -283,7 +288,8 @@ const reducer = (state, action) => {
// Set the opposite attribute that will be updated when the loop attribute matches the name // Set the opposite attribute that will be updated when the loop attribute matches the name
if ( if (
shouldUpdateOppositeAttributeBecauseOfNatureChange || shouldUpdateOppositeAttributeBecauseOfNatureChange ||
shouldCreateOppositeAttributeBecauseOfNatureChange shouldCreateOppositeAttributeBecauseOfNatureChange ||
shouldCreateOppositeAttributeBecauseOfTargetChange
) { ) {
oppositeAttributeNameToUpdate = oppositeAttributeNameToUpdate =
initialAttribute.targetAttribute; initialAttribute.targetAttribute;
@ -308,7 +314,10 @@ const reducer = (state, action) => {
// Then (if needed) create the opposite attribute the case is changing the relation from // 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 // We do it here so keep the order of the attributes
// oneWay || manyWay to something another relation // oneWay || manyWay to something another relation
if (shouldCreateOppositeAttributeBecauseOfNatureChange) { if (
shouldCreateOppositeAttributeBecauseOfNatureChange ||
shouldCreateOppositeAttributeBecauseOfTargetChange
) {
acc[ acc[
oppositeAttributeNameToCreateBecauseOfNatureChange oppositeAttributeNameToCreateBecauseOfNatureChange
] = fromJS(oppositeAttributeToCreate); ] = fromJS(oppositeAttributeToCreate);

View File

@ -93,8 +93,6 @@ const FormModal = () => {
? [forTarget] ? [forTarget]
: [forTarget, targetUid]; : [forTarget, targetUid];
console.log({ modalType });
setState({ setState({
actionType, actionType,
attributeName, attributeName,
@ -271,9 +269,7 @@ const FormModal = () => {
// TODO improve icon logic // TODO improve icon logic
let iconType = ['component', 'contentType'].includes(state.modalType) let iconType = ['component', 'contentType'].includes(state.modalType)
? state.modalType ? state.modalType
: state.attributeType; : state.forTarget;
console.log({ modalType: state });
if (state.modalType === 'addComponentToDynamicZone') { if (state.modalType === 'addComponentToDynamicZone') {
iconType = 'dynamiczone'; iconType = 'dynamiczone';
@ -660,6 +656,8 @@ const FormModal = () => {
push({ search: nextSearch }); push({ search: nextSearch });
return;
// Adding an existing component // Adding an existing component
} else { } else {
if (isInFirstComponentStep) { if (isInFirstComponentStep) {
@ -928,8 +926,7 @@ const FormModal = () => {
? { paddingTop: '0.5rem', paddingBottom: '3rem' } ? { paddingTop: '0.5rem', paddingBottom: '3rem' }
: {}; : {};
console.log({ iconType }); console.log({ modifiedData });
return ( return (
<Modal <Modal
isOpen={isOpen} isOpen={isOpen}

View File

@ -57,6 +57,7 @@ const ListPage = () => {
'attributes', 'attributes',
]; ];
const targetUid = get(modifiedData, [firstMainDataPath, 'uid']); const targetUid = get(modifiedData, [firstMainDataPath, 'uid']);
const attributes = get(modifiedData, mainDataTypeAttributesPath, {}); const attributes = get(modifiedData, mainDataTypeAttributesPath, {});
const attributesLength = Object.keys(attributes).length; const attributesLength = Object.keys(attributes).length;
const currentDataName = get( const currentDataName = get(
@ -87,17 +88,25 @@ const ListPage = () => {
}; };
const handleClickEditField = async ( const handleClickEditField = async (
forTarget,
targetUid, targetUid,
attrName, attributeName,
type, type,
headerDisplayName, headerDisplayName,
headerDisplayCategory = null headerDisplayCategory = null,
headerDisplaySubCategory = null,
subTargetUid = null
// TODO ADD LOGIC headerDisplaySubCategory when editing a field // TODO ADD LOGIC headerDisplaySubCategory when editing a field
// It should be the same one as adding a field // It should be the same one as adding a field
) => { ) => {
let attributeType; let attributeType;
console.log('ooo'); console.log({
headerDisplayName,
headerDisplayCategory,
headerDisplaySubCategory,
subTargetUid,
});
switch (type) { switch (type) {
case 'integer': case 'integer':
@ -117,16 +126,35 @@ const ListPage = () => {
attributeType = type; attributeType = type;
} }
const step = type === 'component' ? '&step=2' : ''; // const step = type === 'component' ? '&step=2' : '';
const displayCategory = headerDisplayCategory // const displayCategory = headerDisplayCategory
? `&headerDisplayCategory=${headerDisplayCategory}` // ? `&headerDisplayCategory=${headerDisplayCategory}`
: ''; // : '';
await wait(); await wait();
push({ const search = {
search: `modalType=attribute&actionType=edit&settingType=base&forTarget=${forTarget}&targetUid=${targetUid}&attributeName=${attrName}&attributeType=${attributeType}&headerDisplayName=${headerDisplayName}${step}${displayCategory}`, modalType: 'attribute',
}); actionType: 'edit',
settingType: 'base',
forTarget,
targetUid,
attributeName,
attributeType,
headerDisplayName,
headerDisplayCategory,
headerDisplaySubCategory,
step: type === 'component' ? '2' : null,
subTargetUid,
};
await wait();
push({ search: makeSearch(search, true) });
// push({
// search: `modalType=attribute&actionType=edit&settingType=base&forTarget=${forTarget}&targetUid=${targetUid}&attributeName=${attrName}&attributeType=${attributeType}&headerDisplayName=${headerDisplayName}${step}${displayCategory}`,
// });
}; };
// const handleClickEditMain = () => { // const handleClickEditMain = () => {
@ -235,6 +263,7 @@ const ListPage = () => {
const CustomRow = props => { const CustomRow = props => {
const { name } = props; const { name } = props;
return ( return (
<ListRow <ListRow
{...props} {...props}
@ -277,6 +306,13 @@ const ListPage = () => {
customRowComponent={props => <CustomRow {...props} />} customRowComponent={props => <CustomRow {...props} />}
addField={handleClickAddAttributeMainData} addField={handleClickAddAttributeMainData}
addComponentToDZ={handleClickAddComponentToDZ} addComponentToDZ={handleClickAddComponentToDZ}
targetUid={targetUid}
dataType={forTarget}
dataTypeName={currentDataName}
//
mainTypeName={currentDataName}
editTarget={forTarget}
// parentTarget={forTarget}
></List> ></List>
</ListWrapper> </ListWrapper>
</div> </div>