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