mirror of
https://github.com/strapi/strapi.git
synced 2025-08-31 12:23:05 +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