mirror of
				https://github.com/strapi/strapi.git
				synced 2025-10-31 09:56:44 +00:00 
			
		
		
		
	Add dynamic to edit a field
This commit is contained in:
		
							parent
							
								
									c29212aa2c
								
							
						
					
					
						commit
						d9ca6451d7
					
				| @ -3,7 +3,7 @@ import React, { useState, useEffect, useRef, memo } from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import { FormattedMessage } from 'react-intl'; | ||||
| import { Link, useLocation } from 'react-router-dom'; | ||||
| import { get, isArray, isEmpty } from 'lodash'; | ||||
| import { get, cloneDeep, isArray, isEmpty } from 'lodash'; | ||||
| import { request } from 'strapi-helper-plugin'; | ||||
| import pluginId from '../../pluginId'; | ||||
| import useDataManager from '../../hooks/useDataManager'; | ||||
|  | ||||
| @ -138,7 +138,7 @@ const DataManagerProvider = ({ children }) => { | ||||
|     return <Redirect to={`/plugins/${pluginId}/content-types/${firstCTUid}`} />; | ||||
|   } | ||||
| 
 | ||||
|   console.log({ modifiedData }); | ||||
|   console.log({ modifiedData, contentTypes }); | ||||
| 
 | ||||
|   return ( | ||||
|     <DataManagerContext.Provider | ||||
|  | ||||
| @ -28,37 +28,44 @@ const reducer = (state, action) => { | ||||
|             return fromJS(rest); | ||||
|           } | ||||
|         ) | ||||
|         .updateIn(['modifiedData', 'schema', 'attributes'], obj => { | ||||
|           const type = get(rest, 'type'); | ||||
|           const target = get(rest, 'target', null); | ||||
|           const nature = get(rest, 'nature', null); | ||||
|           const currentUid = state.getIn(['modifiedData', 'uid']); | ||||
|         .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, | ||||
|               target, | ||||
|               type, | ||||
|               unique: rest.unique, | ||||
|               required: rest.required, | ||||
|               dominant: nature === 'manyToMany' ? !rest.dominant : null, | ||||
|               targetAttribute: name, | ||||
|             }; | ||||
|             // 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, | ||||
|                 target, | ||||
|                 type, | ||||
|                 unique: rest.unique, | ||||
|                 required: rest.required, | ||||
|                 dominant: nature === 'manyToMany' ? !rest.dominant : null, | ||||
|                 targetAttribute: name, | ||||
|               }; | ||||
| 
 | ||||
|             return obj.update(rest.targetAttribute, () => { | ||||
|               return fromJS(oppositeAttribute); | ||||
|             }); | ||||
|               return obj.update(rest.targetAttribute, () => { | ||||
|                 return fromJS(oppositeAttribute); | ||||
|               }); | ||||
|             } | ||||
| 
 | ||||
|             return obj; | ||||
|           } | ||||
| 
 | ||||
|           return obj; | ||||
|         }); | ||||
|         ); | ||||
|     } | ||||
|     case 'GET_DATA_SUCCEEDED': | ||||
|       return state | ||||
|  | ||||
| @ -14,7 +14,7 @@ import { | ||||
| import { Inputs } from '@buffetjs/custom'; | ||||
| import { useHistory, useLocation } from 'react-router-dom'; | ||||
| import { FormattedMessage } from 'react-intl'; | ||||
| import { get, isEmpty, toString, upperFirst } from 'lodash'; | ||||
| import { get, has, isEmpty, set, toString, upperFirst } from 'lodash'; | ||||
| import pluginId from '../../pluginId'; | ||||
| import useQuery from '../../hooks/useQuery'; | ||||
| import useDataManager from '../../hooks/useDataManager'; | ||||
| @ -44,6 +44,7 @@ const FormModal = () => { | ||||
|     targetUid: null, | ||||
|     attributeType: null, | ||||
|     headerDisplayName: null, | ||||
|     pathToSchema: [], | ||||
|   }; | ||||
|   const [state, setState] = useState(initialStateData); | ||||
|   const [reducerState, dispatch] = useReducer(reducer, initialState, init); | ||||
| @ -59,34 +60,60 @@ const FormModal = () => { | ||||
|     modifiedData: allDataSchema, | ||||
|     sortedContentTypesList, | ||||
|   } = useDataManager(); | ||||
|   const { formErrors, modifiedData } = reducerState.toJS(); | ||||
|   const { formErrors, initialData, modifiedData } = reducerState.toJS(); | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     if (!isEmpty(search)) { | ||||
|       // Return 'null' if there isn't any attributeType search params
 | ||||
|       //TODO need targetUID
 | ||||
|       // TODO change target by targetUID & add displayName instead of target
 | ||||
|       const attributeType = query.get('attributeType'); | ||||
|       const modalType = query.get('modalType'); | ||||
|       const actionType = query.get('actionType'); | ||||
|       const attributeName = query.get('attributeName'); | ||||
|       const settingType = query.get('settingType'); | ||||
|       const forTarget = query.get('forTarget'); | ||||
|       const targetUid = query.get('targetUid'); | ||||
|       const headerDisplayName = query.get('headerDisplayName'); | ||||
|       const pathToSchema = | ||||
|         forTarget === 'contentType' || forTarget === 'component' | ||||
|           ? [forTarget] | ||||
|           : [forTarget, targetUid]; | ||||
| 
 | ||||
|       setState({ | ||||
|         attributeName: query.get('attributeName'), | ||||
|         attributeName, | ||||
|         actionType, | ||||
|         modalType, | ||||
|         settingType: query.get('settingType'), | ||||
|         forTarget: query.get('forTarget'), | ||||
|         targetUid: query.get('targetUid'), | ||||
|         headerDisplayName: query.get('headerDisplayName'), | ||||
|         settingType, | ||||
|         forTarget, | ||||
|         targetUid, | ||||
|         headerDisplayName, | ||||
|         attributeType, | ||||
|         pathToSchema, | ||||
|       }); | ||||
| 
 | ||||
|       // Set the predefined data structure to create an attribute
 | ||||
|       if ( | ||||
|         attributeType && | ||||
|         attributeType !== 'null' && | ||||
|         // This condition is added to prevent the reducer state to be cleared when navigating from the base tab to tha advanced one
 | ||||
|         state.modalType !== 'attribute' | ||||
|       ) { | ||||
|         const attributeToEditNotFormatted = get( | ||||
|           allDataSchema, | ||||
|           [...pathToSchema, 'schema', 'attributes', attributeName], | ||||
|           {} | ||||
|         ); | ||||
|         const attributeToEdit = { | ||||
|           ...attributeToEditNotFormatted, | ||||
|           name: attributeName, | ||||
|         }; | ||||
| 
 | ||||
|         if ( | ||||
|           attributeType === 'relation' && | ||||
|           !has(attributeToEdit, ['targetAttribute']) | ||||
|         ) { | ||||
|           set(attributeToEdit, ['targetAttribute'], '-'); | ||||
|         } | ||||
| 
 | ||||
|         dispatch({ | ||||
|           type: 'SET_ATTRIBUTE_DATA_SCHEMA', | ||||
|           attributeType, | ||||
| @ -96,6 +123,8 @@ const FormModal = () => { | ||||
|             'error' | ||||
|           ), | ||||
|           targetUid: get(sortedContentTypesList, ['0', 'uid'], 'error'), | ||||
|           isEditing: actionType === 'edit', | ||||
|           modifiedDataToSetForEditing: attributeToEdit, | ||||
|         }); | ||||
|       } | ||||
|     } | ||||
| @ -122,8 +151,6 @@ const FormModal = () => { | ||||
|     headerId = null; | ||||
|   } | ||||
| 
 | ||||
|   console.log({ allDataSchema }); | ||||
| 
 | ||||
|   const checkFormValidity = async () => { | ||||
|     let schema; | ||||
| 
 | ||||
| @ -134,15 +161,16 @@ const FormModal = () => { | ||||
|       // && state.forTarget !== 'components' &&
 | ||||
|       // state.forTarget !== 'component'
 | ||||
|     ) { | ||||
|       const pathToSchemaAttributes = | ||||
|         state.forTarget === 'contentType' || state.forTarget === 'component' | ||||
|           ? [state.forTarget] | ||||
|           : [state.forTarget, state.targetUid]; | ||||
|       const type = | ||||
|         state.attributeType === 'relation' ? 'relation' : modifiedData.type; | ||||
| 
 | ||||
|       schema = forms[state.modalType].schema( | ||||
|         get(allDataSchema, pathToSchemaAttributes, {}), | ||||
|         modifiedData.type, | ||||
|         modifiedData | ||||
|         get(allDataSchema, state.pathToSchema, {}), | ||||
|         type, | ||||
|         modifiedData, | ||||
|         state.actionType === 'edit', | ||||
|         state.attributeName, | ||||
|         initialData | ||||
|       ); | ||||
|     } else { | ||||
|       // TODO validate component schema
 | ||||
|  | ||||
| @ -4,6 +4,7 @@ import pluralize from 'pluralize'; | ||||
| const initialState = fromJS({ | ||||
|   formErrors: {}, | ||||
|   modifiedData: {}, | ||||
|   initialData: {}, | ||||
| }); | ||||
| 
 | ||||
| export const shouldPluralizeTargetAttribute = nature => | ||||
| @ -75,7 +76,20 @@ const reducer = (state, action) => { | ||||
|     case 'RESET_PROPS': | ||||
|       return initialState; | ||||
|     case 'SET_ATTRIBUTE_DATA_SCHEMA': { | ||||
|       const { attributeType, nameToSetForRelation, targetUid } = action; | ||||
|       const { | ||||
|         attributeType, | ||||
|         isEditing, | ||||
|         modifiedDataToSetForEditing, | ||||
|         nameToSetForRelation, | ||||
|         targetUid, | ||||
|       } = action; | ||||
| 
 | ||||
|       if (isEditing) { | ||||
|         return state | ||||
|           .update('modifiedData', () => fromJS(modifiedDataToSetForEditing)) | ||||
|           .update('initialData', () => fromJS(modifiedDataToSetForEditing)); | ||||
|       } | ||||
| 
 | ||||
|       let dataToSet; | ||||
| 
 | ||||
|       if (attributeType === 'text') { | ||||
|  | ||||
| @ -17,7 +17,7 @@ yup.addMethod(yup.mixed, 'defined', function() { | ||||
| 
 | ||||
| yup.addMethod(yup.string, 'unique', function( | ||||
|   message, | ||||
|   allReadyTakenValues, | ||||
|   alreadyTakenAttributes, | ||||
|   validator | ||||
| ) { | ||||
|   return this.test('unique', message, function(string) { | ||||
| @ -25,7 +25,7 @@ yup.addMethod(yup.string, 'unique', function( | ||||
|       return false; | ||||
|     } | ||||
| 
 | ||||
|     return !allReadyTakenValues.includes( | ||||
|     return !alreadyTakenAttributes.includes( | ||||
|       typeof validator === 'function' ? validator(string) : string | ||||
|     ); | ||||
|   }); | ||||
| @ -46,18 +46,42 @@ const ATTRIBUTES_THAT_DONT_HAVE_MIN_MAX_SETTINGS = [ | ||||
| 
 | ||||
| const forms = { | ||||
|   attribute: { | ||||
|     schema(currentSchema, attributeType, dataToValidate) { | ||||
|       const allreadyTakenAttributes = Object.keys( | ||||
|     schema( | ||||
|       currentSchema, | ||||
|       attributeType, | ||||
|       dataToValidate, | ||||
|       isEditing, | ||||
|       attributeToEditName, | ||||
|       initialData | ||||
|     ) { | ||||
|       const alreadyTakenAttributes = Object.keys( | ||||
|         get(currentSchema, ['schema', 'attributes'], {}) | ||||
|       ); | ||||
|       const targetAttributeAllreadyTakenValue = dataToValidate.name | ||||
|         ? [...allreadyTakenAttributes, dataToValidate.name] | ||||
|         : allreadyTakenAttributes; | ||||
|       ).filter(attribute => { | ||||
|         if (isEditing) { | ||||
|           return attribute !== attributeToEditName; | ||||
|         } | ||||
| 
 | ||||
|         return true; | ||||
|       }); | ||||
| 
 | ||||
|       let targetAttributeAlreadyTakenValue = dataToValidate.name | ||||
|         ? [...alreadyTakenAttributes, dataToValidate.name] | ||||
|         : alreadyTakenAttributes; | ||||
| 
 | ||||
|       if ( | ||||
|         isEditing && | ||||
|         attributeType === 'relation' && | ||||
|         dataToValidate.target === currentSchema.uid | ||||
|       ) { | ||||
|         targetAttributeAlreadyTakenValue = targetAttributeAlreadyTakenValue.filter( | ||||
|           attribute => attribute !== initialData.targetAttribute | ||||
|         ); | ||||
|       } | ||||
| 
 | ||||
|       const commonShape = { | ||||
|         name: yup | ||||
|           .string() | ||||
|           .unique(errorsTrads.unique, allreadyTakenAttributes) | ||||
|           .unique(errorsTrads.unique, alreadyTakenAttributes) | ||||
|           .required(errorsTrads.required), | ||||
|         type: yup.string().required(errorsTrads.required), | ||||
|         default: yup.string().nullable(), | ||||
| @ -144,13 +168,12 @@ const forms = { | ||||
|           return yup.object().shape({ | ||||
|             name: yup | ||||
|               .string() | ||||
|               .unique(errorsTrads.unique, allreadyTakenAttributes) | ||||
|               .unique(errorsTrads.unique, alreadyTakenAttributes) | ||||
|               .required(errorsTrads.required), | ||||
|             targetAttribute: yup | ||||
|               .string() | ||||
|               .unique(errorsTrads.unique, targetAttributeAllreadyTakenValue) | ||||
|               .unique(errorsTrads.unique, targetAttributeAlreadyTakenValue) | ||||
|               .required(errorsTrads.required), | ||||
|             type: yup.string().required(errorsTrads.required), | ||||
|             target: yup.string().required(errorsTrads.required), | ||||
|             nature: yup.string().required(), | ||||
|             dominant: yup.boolean().nullable(), | ||||
| @ -586,11 +609,11 @@ const forms = { | ||||
|     }, | ||||
|   }, | ||||
|   contentType: { | ||||
|     schema(allReadyTakenValues) { | ||||
|     schema(alreadyTakenAttributes) { | ||||
|       return yup.object().shape({ | ||||
|         name: yup | ||||
|           .string() | ||||
|           .unique(errorsTrads.unique, allReadyTakenValues, createUid) | ||||
|           .unique(errorsTrads.unique, alreadyTakenAttributes, createUid) | ||||
|           .required(errorsTrads.required), | ||||
|         collectionName: yup.string(), | ||||
|       }); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 soupette
						soupette