Merge branch 'plugin/content-manager-dev' of github.com:strapi/strapi into plugin/content-manager-dev

This commit is contained in:
Aurelsicoko 2017-09-19 13:57:17 +02:00
commit 4d2ec1f5b0
12 changed files with 351 additions and 101 deletions

View File

@ -6,6 +6,7 @@
import React from 'react'; import React from 'react';
import moment from 'moment'; import moment from 'moment';
import PropTypes from 'prop-types';
import { get, isEmpty, map, mapKeys, isObject, reject, includes } from 'lodash'; import { get, isEmpty, map, mapKeys, isObject, reject, includes } from 'lodash';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import DateTime from 'react-datetime'; import DateTime from 'react-datetime';
@ -67,37 +68,37 @@ class Input extends React.Component { // eslint-disable-line react/prefer-statel
validate = (value) => { validate = (value) => {
let errors = []; let errors = [];
// handle i18n // handle i18n
const requiredError = { id: 'error.validation.required' }; const requiredError = { id: `${this.props.pluginID}.error.validation.required` };
mapKeys(this.props.validations, (validationValue, validationKey) => { mapKeys(this.props.validations, (validationValue, validationKey) => {
switch (validationKey) { switch (validationKey) {
case 'max': case 'max':
if (parseInt(value, 10) > validationValue) { if (parseInt(value, 10) > validationValue) {
errors.push({ id: 'error.validation.max' }); errors.push({ id: `${this.props.pluginID}.error.validation.max` });
} }
break; break;
case 'maxLength': case 'maxLength':
if (value.length > validationValue) { if (value.length > validationValue) {
errors.push({ id: 'error.validation.maxLength' }); errors.push({ id: `${this.props.pluginID}.error.validation.maxLength` });
} }
break; break;
case 'min': case 'min':
if (parseInt(value, 10) < validationValue) { if (parseInt(value, 10) < validationValue) {
errors.push({ id: 'error.validation.min' }); errors.push({ id: `${this.props.pluginID}.error.validation.min` });
} }
break; break;
case 'minLength': case 'minLength':
if (value.length < validationValue) { if (value.length < validationValue) {
errors.push({ id: 'error.validation.minLength' }); errors.push({ id: `${this.props.pluginID}.error.validation.minLength` });
} }
break; break;
case 'required': case 'required':
if (value.length === 0) { if (value.length === 0) {
errors.push({ id: 'error.validation.required' }); errors.push({ id: `${this.props.pluginID}.error.validation.required` });
} }
break; break;
case 'regex': case 'regex':
if (!new RegExp(validationValue).test(value)) { if (!new RegExp(validationValue).test(value)) {
errors.push({ id: 'error.validation.regex' }); errors.push({ id: `${this.props.pluginID}.error.validation.regex` });
} }
break; break;
default: default:
@ -367,36 +368,37 @@ class Input extends React.Component { // eslint-disable-line react/prefer-statel
} }
Input.propTypes = { Input.propTypes = {
addon: React.PropTypes.oneOfType([ addon: PropTypes.oneOfType([
React.PropTypes.bool, PropTypes.bool,
React.PropTypes.string, PropTypes.string,
]), ]),
addRequiredInputDesign: React.PropTypes.bool, addRequiredInputDesign: PropTypes.bool,
customBootstrapClass: React.PropTypes.string, customBootstrapClass: PropTypes.string,
deactivateErrorHighlight: React.PropTypes.bool, deactivateErrorHighlight: PropTypes.bool,
didCheckErrors: React.PropTypes.bool, didCheckErrors: PropTypes.bool,
disabled: React.PropTypes.bool, disabled: PropTypes.bool,
errors: React.PropTypes.array, errors: PropTypes.array,
handleBlur: React.PropTypes.oneOfType([ handleBlur: PropTypes.oneOfType([
React.PropTypes.func, PropTypes.func,
React.PropTypes.bool, PropTypes.bool,
]), ]),
handleChange: React.PropTypes.func.isRequired, handleChange: PropTypes.func.isRequired,
handleFocus: React.PropTypes.func, handleFocus: PropTypes.func,
inputDescription: React.PropTypes.string, inputDescription: PropTypes.string,
label: React.PropTypes.string.isRequired, label: PropTypes.string.isRequired,
name: React.PropTypes.string.isRequired, name: PropTypes.string.isRequired,
noErrorsDescription: React.PropTypes.bool, noErrorsDescription: PropTypes.bool,
placeholder: React.PropTypes.string, placeholder: PropTypes.string,
selectOptions: React.PropTypes.array, pluginID: PropTypes.string,
selectOptionsFetchSucceeded: React.PropTypes.bool, selectOptions: PropTypes.array,
title: React.PropTypes.string, selectOptionsFetchSucceeded: PropTypes.bool,
type: React.PropTypes.string.isRequired, title: PropTypes.string,
validations: React.PropTypes.object.isRequired, type: PropTypes.string.isRequired,
value: React.PropTypes.oneOfType([ validations: PropTypes.object.isRequired,
React.PropTypes.string, value: PropTypes.oneOfType([
React.PropTypes.bool, PropTypes.string,
React.PropTypes.number, PropTypes.bool,
PropTypes.number,
]), ]),
}; };

View File

@ -7,7 +7,7 @@
// Dependencies. // Dependencies.
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { omit } from 'lodash'; import { findIndex, get, omit } from 'lodash';
// Components. // Components.
import Input from 'components/Input'; import Input from 'components/Input';
@ -43,6 +43,10 @@ class EditForm extends React.Component {
// List fields inputs // List fields inputs
const fields = Object.keys(displayedFields).map(attr => { const fields = Object.keys(displayedFields).map(attr => {
const details = displayedFields[attr]; const details = displayedFields[attr];
const errorIndex = findIndex(this.props.formErrors, ['name', attr]);
const errors = errorIndex !== -1 ? this.props.formErrors[errorIndex].errors : [];
const validationsIndex = findIndex(this.props.formValidations, ['name', attr]);
const validations = get(this.props.formValidations[validationsIndex], 'validations') || {};
return ( return (
<Input <Input
@ -53,7 +57,10 @@ class EditForm extends React.Component {
value={this.props.record.get(attr) || ''} value={this.props.record.get(attr) || ''}
placeholder={details.placeholder || details.label || attr || ''} placeholder={details.placeholder || details.label || attr || ''}
handleChange={this.props.handleChange} handleChange={this.props.handleChange}
validations={{}} validations={validations}
errors={errors}
didCheckErrors={this.props.didCheckErrors}
pluginID="content-manager"
/> />
); );
}); });
@ -70,6 +77,9 @@ class EditForm extends React.Component {
EditForm.propTypes = { EditForm.propTypes = {
currentModelName: PropTypes.string.isRequired, currentModelName: PropTypes.string.isRequired,
didCheckErrors: PropTypes.bool.isRequired,
formErrors: PropTypes.array.isRequired,
formValidations: PropTypes.array.isRequired,
handleChange: PropTypes.func.isRequired, handleChange: PropTypes.func.isRequired,
record: PropTypes.oneOfType([ record: PropTypes.oneOfType([
PropTypes.object, PropTypes.object,

View File

@ -4,7 +4,7 @@
* *
*/ */
import { fromJS } from 'immutable'; import { fromJS, List } from 'immutable';
import { LOAD_MODELS, LOADED_MODELS, UPDATE_SCHEMA } from './constants'; import { LOAD_MODELS, LOADED_MODELS, UPDATE_SCHEMA } from './constants';
@ -12,6 +12,7 @@ const initialState = fromJS({
loading: true, loading: true,
models: false, models: false,
schema: false, schema: false,
formValidations: List(),
}); });
function appReducer(state = initialState, action) { function appReducer(state = initialState, action) {

View File

@ -3,40 +3,46 @@
* Edit actions * Edit actions
* *
*/ */
import { get } from 'lodash';
import { getValidationsFromForm } from '../../utils/formValidations';
import { import {
SET_INITIAL_STATE, CANCEL_CHANGES,
DELETE_RECORD,
DELETE_RECORD_ERROR,
DELETE_RECORD_SUCCESS,
EDIT_RECORD,
EDIT_RECORD_ERROR,
EDIT_RECORD_SUCCESS,
SET_CURRENT_MODEL_NAME, SET_CURRENT_MODEL_NAME,
SET_IS_CREATING, SET_IS_CREATING,
SET_INITIAL_STATE,
LOAD_RECORD, LOAD_RECORD,
LOAD_RECORD_SUCCESS, LOAD_RECORD_SUCCESS,
SET_RECORD_ATTRIBUTE, SET_RECORD_ATTRIBUTE,
EDIT_RECORD,
EDIT_RECORD_SUCCESS,
EDIT_RECORD_ERROR,
DELETE_RECORD,
DELETE_RECORD_SUCCESS,
DELETE_RECORD_ERROR,
TOGGLE_NULL, TOGGLE_NULL,
CANCEL_CHANGES, SET_FORM_VALIDATIONS,
SET_FORM,
SET_FORM_ERRORS,
} from './constants'; } from './constants';
export function setInitialState() { export function cancelChanges() {
return { return {
type: SET_INITIAL_STATE, type: CANCEL_CHANGES,
}; };
} }
export function setCurrentModelName(currentModelName) { export function deleteRecord(id, modelName) {
return { return {
type: SET_CURRENT_MODEL_NAME, type: DELETE_RECORD,
currentModelName, id,
modelName,
}; };
} }
export function setIsCreating() { export function editRecord() {
return { return {
type: SET_IS_CREATING, type: EDIT_RECORD,
}; };
} }
@ -47,24 +53,17 @@ export function loadRecord(id) {
}; };
} }
export function recordLoaded(record) {
export function recordDeleted(id) {
return { return {
type: LOAD_RECORD_SUCCESS, type: DELETE_RECORD_SUCCESS,
record, id,
}; };
} }
export function setRecordAttribute(key, value) { export function recordDeleteError() {
return { return {
type: SET_RECORD_ATTRIBUTE, type: DELETE_RECORD_ERROR,
key,
value,
};
}
export function editRecord() {
return {
type: EDIT_RECORD,
}; };
} }
@ -80,24 +79,71 @@ export function recordEditError() {
}; };
} }
export function deleteRecord(id, modelName) { export function recordLoaded(record) {
return { return {
type: DELETE_RECORD, type: LOAD_RECORD_SUCCESS,
id, record,
modelName,
}; };
} }
export function recordDeleted(id) { export function setCurrentModelName(currentModelName) {
return { return {
type: DELETE_RECORD_SUCCESS, type: SET_CURRENT_MODEL_NAME,
id, currentModelName,
}; };
} }
export function recordDeleteError() { export function setForm(data) {
const form = [];
Object.keys(data).map(attr => {
form.push([attr, '']);
});
return { return {
type: DELETE_RECORD_ERROR, type: SET_FORM,
form,
}
}
export function setFormErrors(formErrors) {
return {
type: SET_FORM_ERRORS,
formErrors,
};
}
export function setFormValidations(data) {
const form = Object.keys(data).map(attr => {
return { name: attr, validations: get(data[attr], ['params']) || {} }
});
const formValidations = getValidationsFromForm(form, []);
return {
type: SET_FORM_VALIDATIONS,
formValidations,
}
}
export function setInitialState() {
return {
type: SET_INITIAL_STATE,
};
}
export function setIsCreating() {
return {
type: SET_IS_CREATING,
};
}
export function setRecordAttribute(key, value) {
return {
type: SET_RECORD_ATTRIBUTE,
key,
value,
}; };
} }
@ -106,9 +152,3 @@ export function toggleNull() {
type: TOGGLE_NULL, type: TOGGLE_NULL,
}; };
} }
export function cancelChanges() {
return {
type: CANCEL_CHANGES,
};
}

View File

@ -7,6 +7,9 @@
export const SET_INITIAL_STATE = 'app/Edit/SET_INITIAL_STATE'; export const SET_INITIAL_STATE = 'app/Edit/SET_INITIAL_STATE';
export const SET_CURRENT_MODEL_NAME = 'app/Edit/SET_CURRENT_MODEL_NAME'; export const SET_CURRENT_MODEL_NAME = 'app/Edit/SET_CURRENT_MODEL_NAME';
export const SET_IS_CREATING = 'app/Edit/SET_IS_CREATING'; export const SET_IS_CREATING = 'app/Edit/SET_IS_CREATING';
export const SET_FORM_VALIDATIONS = 'app/Edit/SET_FORM_VALIDATIONS';
export const SET_FORM = 'app/Edit/SET_FORM';
export const SET_FORM_ERRORS = 'app/Edit/SET_FORM_ERRORS';
export const LOAD_RECORD = 'app/Edit/LOAD_RECORD'; export const LOAD_RECORD = 'app/Edit/LOAD_RECORD';
export const LOAD_RECORD_SUCCESS = 'app/Edit/LOAD_RECORD_SUCCESS'; export const LOAD_RECORD_SUCCESS = 'app/Edit/LOAD_RECORD_SUCCESS';

View File

@ -11,7 +11,7 @@ import { connect } from 'react-redux';
import { bindActionCreators, compose } from 'redux'; import { bindActionCreators, compose } from 'redux';
import { createStructuredSelector } from 'reselect'; import { createStructuredSelector } from 'reselect';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { get, isObject } from 'lodash'; import { map, get, isObject, isEmpty } from 'lodash';
import { router } from 'app'; import { router } from 'app';
// Components. // Components.
@ -26,6 +26,7 @@ import { makeSelectModels, makeSelectSchema } from 'containers/App/selectors';
import injectReducer from 'utils/injectReducer'; import injectReducer from 'utils/injectReducer';
import injectSaga from 'utils/injectSaga'; import injectSaga from 'utils/injectSaga';
import templateObject from 'utils/templateObject'; import templateObject from 'utils/templateObject';
import { checkFormValidity } from '../../utils/formValidations';
// Styles. // Styles.
import styles from './styles.scss'; import styles from './styles.scss';
@ -40,6 +41,9 @@ import {
editRecord, editRecord,
toggleNull, toggleNull,
cancelChanges, cancelChanges,
setFormValidations,
setForm,
setFormErrors,
} from './actions'; } from './actions';
// Selectors. // Selectors.
@ -52,6 +56,10 @@ import {
makeSelectDeleting, makeSelectDeleting,
makeSelectIsCreating, makeSelectIsCreating,
makeSelectIsRelationComponentNull, makeSelectIsRelationComponentNull,
makeSelectForm,
makeSelectFormValidations,
makeSelectFormErrors,
makeSelectDidCheckErrors,
} from './selectors'; } from './selectors';
import reducer from './reducer'; import reducer from './reducer';
@ -74,7 +82,7 @@ export class Edit extends React.Component {
buttonBackground: 'primary', buttonBackground: 'primary',
buttonSize: 'buttonLg', buttonSize: 'buttonLg',
label: this.props.editing ? 'content-manager.containers.Edit.editing' : 'content-manager.containers.Edit.submit', label: this.props.editing ? 'content-manager.containers.Edit.editing' : 'content-manager.containers.Edit.submit',
onClick: this.props.editRecord, onClick: this.handleSubmit,
disabled: this.props.editing, disabled: this.props.editing,
type: 'submit', type: 'submit',
}, },
@ -94,7 +102,8 @@ export class Edit extends React.Component {
componentDidMount() { componentDidMount() {
this.props.setInitialState(); this.props.setInitialState();
this.props.setCurrentModelName(this.props.match.params.slug.toLowerCase()); this.props.setCurrentModelName(this.props.match.params.slug.toLowerCase());
this.props.setFormValidations(this.props.models[this.props.match.params.slug.toLowerCase()].attributes);
this.props.setForm(this.props.models[this.props.match.params.slug.toLowerCase()].attributes);
// Detect that the current route is the `create` route or not // Detect that the current route is the `create` route or not
if (this.props.match.params.id === 'create') { if (this.props.match.params.id === 'create') {
this.props.setIsCreating(); this.props.setIsCreating();
@ -118,12 +127,20 @@ export class Edit extends React.Component {
} }
handleSubmit = () => { handleSubmit = () => {
this.props.editRecord(); const form = this.props.form.toJS();
map(this.props.record.toJS(), (value, key) => form[key] = value);
const formErrors = checkFormValidity(form, this.props.formValidations.toJS());
if (isEmpty(formErrors)) {
this.props.editRecord();
} else {
this.props.setFormErrors(formErrors);
}
} }
handleSubmitOnEnterPress = (e) => { handleSubmitOnEnterPress = (e) => {
if (e.keyCode === 13) { if (e.keyCode === 13) {
this.props.editRecord(); this.handleSubmit();
} }
} }
@ -164,6 +181,9 @@ export class Edit extends React.Component {
handleChange={this.handleChange} handleChange={this.handleChange}
handleSubmit={this.handleSubmit} handleSubmit={this.handleSubmit}
editing={this.props.editing} editing={this.props.editing}
formErrors={this.props.formErrors.toJS()}
didCheckErrors={this.props.didCheckErrors}
formValidations={this.props.formValidations.toJS()}
/> />
</div> </div>
</div> </div>
@ -185,17 +205,25 @@ export class Edit extends React.Component {
); );
} }
} }
/* eslint-disable react/require-default-props */
Edit.propTypes = { Edit.propTypes = {
cancelChanges: PropTypes.func.isRequired, cancelChanges: PropTypes.func.isRequired,
currentModelName: PropTypes.oneOfType([ currentModelName: PropTypes.oneOfType([
PropTypes.bool, PropTypes.bool,
PropTypes.string, PropTypes.string,
]).isRequired, ]).isRequired,
// deleteRecord: PropTypes.func.isRequired, didCheckErrors: PropTypes.bool.isRequired,
// deleting: PropTypes.bool.isRequired,
editing: PropTypes.bool.isRequired, editing: PropTypes.bool.isRequired,
editRecord: PropTypes.func.isRequired, editRecord: PropTypes.func.isRequired,
form: PropTypes.object.isRequired,
formErrors: PropTypes.oneOfType([
PropTypes.array,
PropTypes.object,
]),
formValidations: PropTypes.oneOfType([
PropTypes.array,
PropTypes.object,
]),
isCreating: PropTypes.bool.isRequired, isCreating: PropTypes.bool.isRequired,
isRelationComponentNull: PropTypes.bool.isRequired, isRelationComponentNull: PropTypes.bool.isRequired,
loading: PropTypes.bool.isRequired, loading: PropTypes.bool.isRequired,
@ -219,6 +247,9 @@ Edit.propTypes = {
PropTypes.bool, PropTypes.bool,
]).isRequired, ]).isRequired,
setCurrentModelName: PropTypes.func.isRequired, setCurrentModelName: PropTypes.func.isRequired,
setForm: PropTypes.func.isRequired,
setFormErrors: PropTypes.func.isRequired,
setFormValidations: PropTypes.func.isRequired,
setInitialState: PropTypes.func.isRequired, setInitialState: PropTypes.func.isRequired,
setIsCreating: PropTypes.func.isRequired, setIsCreating: PropTypes.func.isRequired,
setRecordAttribute: PropTypes.func.isRequired, setRecordAttribute: PropTypes.func.isRequired,
@ -235,6 +266,10 @@ const mapStateToProps = createStructuredSelector({
schema: makeSelectSchema(), schema: makeSelectSchema(),
models: makeSelectModels(), models: makeSelectModels(),
isRelationComponentNull: makeSelectIsRelationComponentNull(), isRelationComponentNull: makeSelectIsRelationComponentNull(),
form: makeSelectForm(),
formValidations: makeSelectFormValidations(),
formErrors: makeSelectFormErrors(),
didCheckErrors: makeSelectDidCheckErrors(),
}); });
function mapDispatchToProps(dispatch) { function mapDispatchToProps(dispatch) {
@ -248,6 +283,9 @@ function mapDispatchToProps(dispatch) {
editRecord, editRecord,
toggleNull, toggleNull,
cancelChanges, cancelChanges,
setFormValidations,
setForm,
setFormErrors,
}, },
dispatch dispatch
); );

View File

@ -4,7 +4,7 @@
* *
*/ */
import { fromJS, Map } from 'immutable'; import { fromJS, Map, List } from 'immutable';
import { import {
SET_INITIAL_STATE, SET_INITIAL_STATE,
@ -21,6 +21,9 @@ import {
DELETE_RECORD_ERROR, DELETE_RECORD_ERROR,
TOGGLE_NULL, TOGGLE_NULL,
CANCEL_CHANGES, CANCEL_CHANGES,
SET_FORM_VALIDATIONS,
SET_FORM,
SET_FORM_ERRORS,
} from './constants'; } from './constants';
const initialState = fromJS({ const initialState = fromJS({
@ -31,6 +34,10 @@ const initialState = fromJS({
deleting: false, deleting: false,
isCreating: false, isCreating: false,
isRelationComponentNull: false, isRelationComponentNull: false,
formValidations: List(),
formErrors: List(),
form: Map({}),
didCheckErrors: false,
}); });
function editReducer(state = initialState, action) { function editReducer(state = initialState, action) {
@ -50,9 +57,12 @@ function editReducer(state = initialState, action) {
.set('model', action.model) .set('model', action.model)
.set('id', action.id); .set('id', action.id);
case LOAD_RECORD_SUCCESS: case LOAD_RECORD_SUCCESS:
return state.set('loading', false).set('record', fromJS(action.record)); return state
.set('loading', false)
.set('record', fromJS(action.record));
case SET_RECORD_ATTRIBUTE: case SET_RECORD_ATTRIBUTE:
return state.setIn(['record', action.key], fromJS(action.value)); return state
.setIn(['record', action.key], fromJS(action.value));
case EDIT_RECORD: case EDIT_RECORD:
return state.set('editing', true); return state.set('editing', true);
case EDIT_RECORD_SUCCESS: case EDIT_RECORD_SUCCESS:
@ -69,6 +79,15 @@ function editReducer(state = initialState, action) {
return state.set('isRelationComponentNull', !state.get('isRelationComponentNull')); return state.set('isRelationComponentNull', !state.get('isRelationComponentNull'));
case CANCEL_CHANGES: case CANCEL_CHANGES:
return state.set('record', Map({})); return state.set('record', Map({}));
case SET_FORM_VALIDATIONS:
return state
.set('formValidations', List(action.formValidations));
case SET_FORM:
return state.set('form', Map(action.form));
case SET_FORM_ERRORS:
return state
.set('formErrors', List(action.formErrors))
.set('didCheckErrors', !state.didCheckErrors);
default: default:
return state; return state;
} }

View File

@ -36,6 +36,18 @@ const makeSelectIsCreating = () =>
const makeSelectIsRelationComponentNull = () => const makeSelectIsRelationComponentNull = () =>
createSelector(selectEditDomain(), substate => substate.get('isRelationComponentNull')); createSelector(selectEditDomain(), substate => substate.get('isRelationComponentNull'));
const makeSelectForm = () =>
createSelector(selectEditDomain(), substate => substate.get('form'));
const makeSelectFormValidations = () =>
createSelector(selectEditDomain(), substate => substate.get('formValidations'));
const makeSelectFormErrors = () =>
createSelector(selectEditDomain(), substate => substate.get('formErrors'));
const makeSelectDidCheckErrors = () =>
createSelector(selectEditDomain(), substate => substate.get('didCheckErrors'));
export default selectEditDomain; export default selectEditDomain;
export { export {
makeSelectRecord, makeSelectRecord,
@ -45,4 +57,8 @@ export {
makeSelectDeleting, makeSelectDeleting,
makeSelectIsCreating, makeSelectIsCreating,
makeSelectIsRelationComponentNull, makeSelectIsRelationComponentNull,
makeSelectForm,
makeSelectFormValidations,
makeSelectFormErrors,
makeSelectDidCheckErrors,
}; };

View File

@ -87,14 +87,6 @@ export class List extends React.Component {
} }
changePage = (page) => {
router.push({
pathname: this.props.location.pathname,
search: `?page=${page}&limit=${this.props.limit}&sort=${this.props.sort}`,
});
this.props.changePage(page);
}
init(props) { init(props) {
const slug = props.match.params.slug; const slug = props.match.params.slug;
// Set current model name // Set current model name
@ -132,6 +124,14 @@ export class List extends React.Component {
}); });
} }
handleChangePage = (page) => {
router.push({
pathname: this.props.location.pathname,
search: `?page=${page}&limit=${this.props.limit}&sort=${this.props.sort}`,
});
this.props.changePage(page);
}
handleChangeSort = (sort) => { handleChangeSort = (sort) => {
router.push({ router.push({
pathname: this.props.location.pathname, pathname: this.props.location.pathname,
@ -248,7 +248,7 @@ export class List extends React.Component {
<TableFooter <TableFooter
limit={this.props.limit} limit={this.props.limit}
currentPage={this.props.currentPage} currentPage={this.props.currentPage}
changePage={this.changePage} changePage={this.handleChangePage}
count={this.props.count} count={this.props.count}
className="push-lg-right" className="push-lg-right"
handleChangeLimit={this.handleChangeLimit} handleChangeLimit={this.handleChangeLimit}

View File

@ -12,6 +12,19 @@
"containers.List.pluginHeaderDescription": "Manage your {label}", "containers.List.pluginHeaderDescription": "Manage your {label}",
"components.LimitSelect.itemsPerPage": "Items per page", "components.LimitSelect.itemsPerPage": "Items per page",
"containers.List.errorFetchRecords": "Error", "containers.List.errorFetchRecords": "Error",
"error.validation.required": "This value input is required.",
"error.validation.regex": "The value not match the regex.",
"error.validation.max": "The value is too high.",
"error.validation.min": "The value is too low.",
"error.validation.maxLength": "The value is too long.",
"error.validation.minLength": "The value is too shot.",
"error.contentTypeName.taken": "This name already exists",
"error.attribute.taken": "This field name already exists",
"error.attribute.key.taken": "This value already exists",
"error.attribute.sameKeyAndName": "Can't be equals",
"error.validation.minSupMax": "Can't be superior",
"pageNotFound": "Page not found", "pageNotFound": "Page not found",
"popUpWarning.button.cancel": "Cancel", "popUpWarning.button.cancel": "Cancel",

View File

@ -13,6 +13,18 @@
"components.LimitSelect.itemsPerPage": "Éléments par page", "components.LimitSelect.itemsPerPage": "Éléments par page",
"containers.List.errorFetchRecords": "Erreur", "containers.List.errorFetchRecords": "Erreur",
"error.validation.required": "Ce champ est obligatoire.",
"error.validation.regex": "La valeur ne correspond pas au format attendu.",
"error.validation.max": "La valeur est trop grande.",
"error.validation.min": "La valeur est trop basse.",
"error.validation.maxLength": "La valeur est trop longue.",
"error.validation.minLength": "La valeur est trop courte.",
"error.contentTypeName.taken": "Ce nom existe déjà",
"error.attribute.taken": "Ce champ existe déjà",
"error.attribute.key.taken": "Cette valeur existe déjà",
"error.attribute.sameKeyAndName": "Ne peuvent pas être égaux",
"error.validation.minSupMax": "Ne peut pas être plus grand",
"popUpWarning.button.cancel": "Annuler", "popUpWarning.button.cancel": "Annuler",
"popUpWarning.button.confirm": "Confirmer", "popUpWarning.button.confirm": "Confirmer",
"popUpWarning.title": "Confirmation requise", "popUpWarning.title": "Confirmation requise",

View File

@ -0,0 +1,96 @@
import { forEach, isObject, isArray, map, mapKeys, includes, reject, isEmpty, findIndex, isUndefined } from 'lodash';
/* eslint-disable consistent-return */
export function getValidationsFromForm(form, formValidations) {
map(form, (value, key) => {
// Check if the object
if (isObject(value) && !isArray(value)) {
forEach(value, (subValue) => {
// Check if it has nestedInputs
if (isArray(subValue) && value.type !== 'select') {
return getValidationsFromForm(subValue, formValidations);
}
});
}
if (isArray(value) && value.type !== 'select') {
return getValidationsFromForm(form[key], formValidations);
}
// Push the target and the validation
if (value.name) {
formValidations.push({ name: value.name, validations: value.validations });
}
});
return formValidations;
}
export function checkFormValidity(formData, formValidations) {
const errors = [];
forEach(formData, (value, key) => {
const validationValue = formValidations[findIndex(formValidations, ['name', key])];
if (!isUndefined(validationValue)) {
const inputErrors = validate(value, validationValue.validations);
if (!isEmpty(inputErrors)) {
errors.push({ name: key, errors: inputErrors });
}
}
});
return errors;
}
function validate(value, validations) {
let errors = [];
// Handle i18n
const requiredError = { id: 'content-manager.error.validation.required' };
mapKeys(validations, (validationValue, validationKey) => {
switch (validationKey) {
case 'max':
if (parseInt(value, 10) > validationValue) {
errors.push({ id: 'content-manager.error.validation.max' });
}
break;
case 'min':
if (parseInt(value, 10) < validationValue) {
errors.push({ id: 'content-manager.error.validation.min' });
}
break;
case 'maxLength':
if (value.length > validationValue) {
errors.push({ id: 'content-manager.error.validation.maxLength' });
}
break;
case 'minLength':
if (value.length < validationValue) {
errors.push({ id: 'content-manager.error.validation.minLength' });
}
break;
case 'required':
if (value.length === 0) {
errors.push({ id: 'content-manager.error.validation.required' });
}
break;
case 'regex':
if (!new RegExp(validationValue).test(value)) {
errors.push({ id: 'content-manager.error.validation.regex' });
}
break;
default:
errors = [];
}
});
if (includes(errors, requiredError)) {
errors = reject(errors, (error) => error !== requiredError);
}
return errors;
}