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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -14,11 +14,19 @@ function ListRow({
attributeName,
configurable,
name,
nature,
onClick,
onClickDelete,
plugin,
target,
targetUid,
type,
mainTypeName,
editTarget,
firstLoopComponentName,
firstLoopComponentUid,
secondLoopComponentName,
secondLoopComponentUid,
}) {
const ico = ['integer', 'biginteger', 'float', 'decimal'].includes(type)
? 'number'
@ -35,7 +43,31 @@ function ListRow({
const handleClick = () => {
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 = {
configurable: true,
firstLoopComponentName: null,
firstLoopComponentUid: null,
nature: null,
onClick: () => {},
onClickDelete: () => {},
plugin: null,
secondLoopComponentName: null,
secondLoopComponentUid: null,
target: null,
targetUid: null,
type: null,
};
ListRow.propTypes = {
attributeName: PropTypes.string.isRequired,
configurable: PropTypes.bool,
editTarget: PropTypes.string.isRequired,
firstLoopComponentName: PropTypes.string,
firstLoopComponentUid: PropTypes.string,
mainTypeName: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
nature: PropTypes.string,
onClick: PropTypes.func,
onClickDelete: PropTypes.func,
plugin: PropTypes.string,
secondLoopComponentName: PropTypes.string,
secondLoopComponentUid: PropTypes.string,
target: PropTypes.string,
targetUid: PropTypes.string,
type: PropTypes.string,
};

View File

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

View File

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

View File

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

View File

@ -202,6 +202,7 @@ const reducer = (state, action) => {
initialAttribute,
} = action;
let newState = state;
const initialAttributeName = get(initialAttribute, ['name'], '');
const pathToDataToEdit = ['component', 'contentType'].includes(forTarget)
? [forTarget]
@ -270,6 +271,10 @@ const reducer = (state, action) => {
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
if (
@ -283,7 +288,8 @@ const reducer = (state, action) => {
// Set the opposite attribute that will be updated when the loop attribute matches the name
if (
shouldUpdateOppositeAttributeBecauseOfNatureChange ||
shouldCreateOppositeAttributeBecauseOfNatureChange
shouldCreateOppositeAttributeBecauseOfNatureChange ||
shouldCreateOppositeAttributeBecauseOfTargetChange
) {
oppositeAttributeNameToUpdate =
initialAttribute.targetAttribute;
@ -308,7 +314,10 @@ const reducer = (state, action) => {
// 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) {
if (
shouldCreateOppositeAttributeBecauseOfNatureChange ||
shouldCreateOppositeAttributeBecauseOfTargetChange
) {
acc[
oppositeAttributeNameToCreateBecauseOfNatureChange
] = fromJS(oppositeAttributeToCreate);

View File

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

View File

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