mirror of
https://github.com/strapi/strapi.git
synced 2025-11-13 08:38:09 +00:00
Handle add attributes to content type
This commit is contained in:
parent
7bf1f3183c
commit
28f35f678b
@ -8,6 +8,7 @@ import React from 'react';
|
|||||||
import { isEmpty, startCase } from 'lodash';
|
import { isEmpty, startCase } from 'lodash';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
import { router } from 'app';
|
import { router } from 'app';
|
||||||
|
import Button from 'components/Button';
|
||||||
import styles from './styles.scss';
|
import styles from './styles.scss';
|
||||||
|
|
||||||
/* eslint-disable jsx-a11y/no-static-element-interactions */
|
/* eslint-disable jsx-a11y/no-static-element-interactions */
|
||||||
@ -16,46 +17,68 @@ class ContentHeader extends React.Component { // eslint-disable-line react/prefe
|
|||||||
router.push(this.props.editPath);
|
router.push(this.props.editPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderButtonContainer = () => (
|
||||||
|
<div className={styles.buttonContainer}>
|
||||||
|
<FormattedMessage id="form.button.cancel">
|
||||||
|
{(message) => (
|
||||||
|
<Button type="button" label={message} buttonSize={"buttonMd"} buttonBackground={"secondary"} onClick={this.props.handleCancel} />
|
||||||
|
)}
|
||||||
|
</FormattedMessage>
|
||||||
|
<FormattedMessage id="form.button.save">
|
||||||
|
{(message) => (
|
||||||
|
<Button type="submit" label={message} buttonSize={"buttonLg"} buttonBackground={"primary"} onClick={this.props.handleSubmit} />
|
||||||
|
)}
|
||||||
|
</FormattedMessage>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
renderContentHeader = () => {
|
renderContentHeader = () => {
|
||||||
const containerClass = this.props.noMargin ? styles.contentHeaderNoMargin : styles.contentHeader;
|
const containerClass = this.props.noMargin ? styles.contentHeaderNoMargin : styles.contentHeader;
|
||||||
const editIcon = this.props.editIcon ?
|
const description = this.props.description || <FormattedMessage id="modelPage.contentHeader.emptyDescription.description" />;
|
||||||
<i className="fa fa-pencil" onClick={this.edit} role="button" />
|
const buttons = this.props.addButtons ? this.renderButtonContainer() : '';
|
||||||
: '';
|
|
||||||
|
|
||||||
const description = this.props.description || <FormattedMessage id="modelPage.contentHeader.emptyDescription.description" />
|
|
||||||
return (
|
return (
|
||||||
<div className={containerClass}>
|
<div className={containerClass}>
|
||||||
|
<div>
|
||||||
<div className={`${styles.title} ${styles.flex}`}>
|
<div className={`${styles.title} ${styles.flex}`}>
|
||||||
<span>{startCase(this.props.name)}</span>
|
<span>{startCase(this.props.name)}</span>
|
||||||
{editIcon}
|
<i className={`fa fa-${this.props.icoType}`} onClick={this.edit} role="button" />
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.subTitle}>{description}</div>
|
<div className={styles.subTitle}>{description}</div>
|
||||||
</div>
|
</div>
|
||||||
|
{buttons}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const containerClass = this.props.noMargin ? styles.contentHeaderNoMargin : styles.contentHeader;
|
const containerClass = this.props.noMargin ? styles.contentHeaderNoMargin : styles.contentHeader;
|
||||||
const description = isEmpty(this.props.description) ? '' : <FormattedMessage id={this.props.description} />;
|
const description = isEmpty(this.props.description) ? '' : <FormattedMessage id={this.props.description} />;
|
||||||
|
const buttons = this.props.addButtons ? this.renderButtonContainer() : '';
|
||||||
|
|
||||||
if (this.props.noI18n) return this.renderContentHeader();
|
if (this.props.editIcon) return this.renderContentHeader();
|
||||||
return (
|
return (
|
||||||
<div className={containerClass}>
|
<div className={containerClass}>
|
||||||
|
<div>
|
||||||
<div className={styles.title}>
|
<div className={styles.title}>
|
||||||
<FormattedMessage id={this.props.name} />
|
<FormattedMessage id={this.props.name} />
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.subTitle}>{description}</div>
|
<div className={styles.subTitle}>{description}</div>
|
||||||
</div>
|
</div>
|
||||||
|
{buttons}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ContentHeader.propTypes = {
|
ContentHeader.propTypes = {
|
||||||
|
addButtons: React.PropTypes.bool,
|
||||||
description: React.PropTypes.string,
|
description: React.PropTypes.string,
|
||||||
editIcon: React.PropTypes.bool,
|
editIcon: React.PropTypes.bool,
|
||||||
editPath: React.PropTypes.string,
|
editPath: React.PropTypes.string,
|
||||||
|
handleCancel: React.PropTypes.func,
|
||||||
|
handleSubmit: React.PropTypes.func,
|
||||||
|
icoType: React.PropTypes.string,
|
||||||
name: React.PropTypes.string,
|
name: React.PropTypes.string,
|
||||||
noI18n: React.PropTypes.bool,
|
|
||||||
noMargin: React.PropTypes.bool,
|
noMargin: React.PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -2,12 +2,16 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
margin: 2.4rem 0rem 3.3rem 0rem;
|
margin: 2.4rem 0rem 3.3rem 0rem;
|
||||||
font-family: Lato;
|
font-family: Lato;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contentHeaderNoMargin {
|
.contentHeaderNoMargin {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin-bottom: 3.2rem;
|
margin-bottom: 3.2rem;
|
||||||
font-family: Lato;
|
font-family: Lato;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
@ -46,3 +50,9 @@
|
|||||||
}
|
}
|
||||||
// color: #101622;
|
// color: #101622;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.buttonContainer {
|
||||||
|
> button:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import {
|
|||||||
MODELS_FETCH,
|
MODELS_FETCH,
|
||||||
MODELS_FETCH_SUCCEEDED,
|
MODELS_FETCH_SUCCEEDED,
|
||||||
STORE_TEMPORARY_MENU,
|
STORE_TEMPORARY_MENU,
|
||||||
|
TEMPORARY_CONTENT_TYPE_POSTED,
|
||||||
} from './constants';
|
} from './constants';
|
||||||
|
|
||||||
export function deleteContentType(itemToDelete) {
|
export function deleteContentType(itemToDelete) {
|
||||||
@ -90,3 +91,9 @@ export function storeTemporaryMenu(newMenu, position, nbElementToRemove) {
|
|||||||
nbElementToRemove,
|
nbElementToRemove,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function temporaryContentTypePosted() {
|
||||||
|
return {
|
||||||
|
type: TEMPORARY_CONTENT_TYPE_POSTED,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@ -8,3 +8,4 @@ export const DELETE_CONTENT_TYPE = 'ContentTypeBuilder/App/DELETE_CONTENT_TYPE';
|
|||||||
export const MODELS_FETCH = 'ContentTypeBuilder/App/MODELS_FETCH';
|
export const MODELS_FETCH = 'ContentTypeBuilder/App/MODELS_FETCH';
|
||||||
export const MODELS_FETCH_SUCCEEDED = 'ContentTypeBuilder/App/MODELS_FETCH_SUCCEEDED';
|
export const MODELS_FETCH_SUCCEEDED = 'ContentTypeBuilder/App/MODELS_FETCH_SUCCEEDED';
|
||||||
export const STORE_TEMPORARY_MENU = 'ContentTypeBuilder/App/STORE_TEMPORARY_MENU';
|
export const STORE_TEMPORARY_MENU = 'ContentTypeBuilder/App/STORE_TEMPORARY_MENU';
|
||||||
|
export const TEMPORARY_CONTENT_TYPE_POSTED = 'ContentTypeBuilder/App/TEMPORARY_CONTENT_TYPE_POSTED';
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import messages from '../../translations/en.json';
|
|||||||
|
|
||||||
import styles from './styles.scss';
|
import styles from './styles.scss';
|
||||||
import { modelsFetch } from './actions';
|
import { modelsFetch } from './actions';
|
||||||
|
import { makeSelectMenu } from './selectors';
|
||||||
|
|
||||||
define(map(messages, (message, id) => ({
|
define(map(messages, (message, id) => ({
|
||||||
id,
|
id,
|
||||||
@ -50,12 +51,13 @@ class App extends React.Component {
|
|||||||
const content = React.Children.map(this.props.children, child =>
|
const content = React.Children.map(this.props.children, child =>
|
||||||
React.cloneElement(child, {
|
React.cloneElement(child, {
|
||||||
exposedComponents: this.props.exposedComponents,
|
exposedComponents: this.props.exposedComponents,
|
||||||
|
menu: this.props.menu,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`${pluginId} ${styles.app}`}>
|
<div className={`${pluginId} ${styles.app}`}>
|
||||||
{React.Children.toArray(content)}
|
{React.Children.toArray(content, 'fuck')}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -68,6 +70,7 @@ App.contextTypes = {
|
|||||||
App.propTypes = {
|
App.propTypes = {
|
||||||
children: React.PropTypes.node,
|
children: React.PropTypes.node,
|
||||||
exposedComponents: React.PropTypes.object.isRequired,
|
exposedComponents: React.PropTypes.object.isRequired,
|
||||||
|
menu: React.PropTypes.array,
|
||||||
modelsFetch: React.PropTypes.func,
|
modelsFetch: React.PropTypes.func,
|
||||||
shouldRefetchContentType: React.PropTypes.bool,
|
shouldRefetchContentType: React.PropTypes.bool,
|
||||||
};
|
};
|
||||||
@ -82,6 +85,7 @@ export function mapDispatchToProps(dispatch) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
menu: makeSelectMenu(),
|
||||||
shouldRefetchContentType: makeSelectShouldRefetchContentType(),
|
shouldRefetchContentType: makeSelectShouldRefetchContentType(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import {
|
|||||||
MODELS_FETCH,
|
MODELS_FETCH,
|
||||||
MODELS_FETCH_SUCCEEDED,
|
MODELS_FETCH_SUCCEEDED,
|
||||||
STORE_TEMPORARY_MENU,
|
STORE_TEMPORARY_MENU,
|
||||||
|
TEMPORARY_CONTENT_TYPE_POSTED,
|
||||||
} from './constants';
|
} from './constants';
|
||||||
|
|
||||||
/* eslint-disable new-cap */
|
/* eslint-disable new-cap */
|
||||||
@ -39,6 +40,13 @@ function appReducer(state = initialState, action) {
|
|||||||
.updateIn(['menu', '0', 'items'], (list) => list.splice(action.position, action.nbElementToRemove, action.newLink))
|
.updateIn(['menu', '0', 'items'], (list) => list.splice(action.position, action.nbElementToRemove, action.newLink))
|
||||||
.update('models', array => array.splice(action.nbElementToRemove === 0 ? modelsSize : modelsSize - 1 , 1, action.newModel));
|
.update('models', array => array.splice(action.nbElementToRemove === 0 ? modelsSize : modelsSize - 1 , 1, action.newModel));
|
||||||
}
|
}
|
||||||
|
case TEMPORARY_CONTENT_TYPE_POSTED: {
|
||||||
|
const oldMenuItem = state.getIn(['menu', '0', 'items', size(state.getIn(['menu', '0', 'items']).toJS()) -2]);
|
||||||
|
oldMenuItem.isTemporary = false;
|
||||||
|
const newData = oldMenuItem;
|
||||||
|
return state
|
||||||
|
.updateIn(['menu', '0', 'items', size(state.getIn(['menu', '0', 'items']).toJS()) -2], () => newData);
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,14 +34,9 @@ export function* fetchModels() {
|
|||||||
|
|
||||||
// Individual exports for testing
|
// Individual exports for testing
|
||||||
export function* defaultSaga() {
|
export function* defaultSaga() {
|
||||||
// TODO check if problems
|
|
||||||
yield fork(takeLatest, MODELS_FETCH, fetchModels);
|
|
||||||
yield fork(takeLatest, DELETE_CONTENT_TYPE, deleteContentType);
|
yield fork(takeLatest, DELETE_CONTENT_TYPE, deleteContentType);
|
||||||
// const loadModelsWatcher = yield fork(takeLatest, MODELS_FETCH, fetchModels);
|
yield fork(takeLatest, MODELS_FETCH, fetchModels);
|
||||||
|
|
||||||
// Suspend execution until location changes
|
|
||||||
// yield take(MODELS_FETCH_SUCCEEDED);
|
|
||||||
// yield cancel(loadModelsWatcher);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// All sagas to be loaded
|
// All sagas to be loaded
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import {
|
|||||||
CONTENT_TYPE_FETCH,
|
CONTENT_TYPE_FETCH,
|
||||||
CONTENT_TYPE_FETCH_SUCCEEDED,
|
CONTENT_TYPE_FETCH_SUCCEEDED,
|
||||||
RESET_DID_FETCH_MODEL_PROP,
|
RESET_DID_FETCH_MODEL_PROP,
|
||||||
|
RESET_IS_FORM_SET,
|
||||||
SET_ATTRIBUTE_FORM,
|
SET_ATTRIBUTE_FORM,
|
||||||
SET_FORM,
|
SET_FORM,
|
||||||
} from './constants';
|
} from './constants';
|
||||||
@ -112,6 +113,12 @@ export function resetDidFetchModelProp() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function resetIsFormSet() {
|
||||||
|
return {
|
||||||
|
type: RESET_IS_FORM_SET,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function setAttributeForm(hash) {
|
export function setAttributeForm(hash) {
|
||||||
const hashArray = hash.split('::');
|
const hashArray = hash.split('::');
|
||||||
const formType = replace(hashArray[1], 'attribute', '');
|
const formType = replace(hashArray[1], 'attribute', '');
|
||||||
@ -122,10 +129,10 @@ export function setAttributeForm(hash) {
|
|||||||
name: '',
|
name: '',
|
||||||
params: Map({
|
params: Map({
|
||||||
type: formType,
|
type: formType,
|
||||||
required: false,
|
required: true,
|
||||||
// TODO remove with correct value
|
// TODO remove with correct value
|
||||||
minLength: true,
|
// minLength: true,
|
||||||
minLengthValue: 0,
|
// minLengthValue: 0,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -14,5 +14,6 @@ export const CONTENT_TYPE_EDIT = 'ContentTypeBuilder/Form/CONTENT_TYPE_EDIT';
|
|||||||
export const CONTENT_TYPE_FETCH = 'ContentTypeBuilder/Form/CONTENT_TYPE_FETCH';
|
export const CONTENT_TYPE_FETCH = 'ContentTypeBuilder/Form/CONTENT_TYPE_FETCH';
|
||||||
export const CONTENT_TYPE_FETCH_SUCCEEDED = 'ContentTypeBuilder/Form/CONTENT_TYPE_FETCH_SUCCEEDED';
|
export const CONTENT_TYPE_FETCH_SUCCEEDED = 'ContentTypeBuilder/Form/CONTENT_TYPE_FETCH_SUCCEEDED';
|
||||||
export const RESET_DID_FETCH_MODEL_PROP = 'ContentTypeBuilder/Form/RESET_DID_FETCH_MODEL_PROP';
|
export const RESET_DID_FETCH_MODEL_PROP = 'ContentTypeBuilder/Form/RESET_DID_FETCH_MODEL_PROP';
|
||||||
|
export const RESET_IS_FORM_SET = 'ContentTypeBuilder/Form/RESET_IS_FORM_SET';
|
||||||
export const SET_ATTRIBUTE_FORM = 'ContentTypeBuilder/Form/SET_ATTRIBUTE_FORM';
|
export const SET_ATTRIBUTE_FORM = 'ContentTypeBuilder/Form/SET_ATTRIBUTE_FORM';
|
||||||
export const SET_FORM = 'ContentTypeBuilder/Form/SET_FORM';
|
export const SET_FORM = 'ContentTypeBuilder/Form/SET_FORM';
|
||||||
|
|||||||
@ -23,7 +23,7 @@ import {
|
|||||||
import { router, store } from 'app';
|
import { router, store } from 'app';
|
||||||
|
|
||||||
import { storeTemporaryMenu } from 'containers/App/actions';
|
import { storeTemporaryMenu } from 'containers/App/actions';
|
||||||
|
import { addAttributeToContentType } from 'containers/ModelPage/actions';
|
||||||
import AttributeCard from 'components/AttributeCard';
|
import AttributeCard from 'components/AttributeCard';
|
||||||
import InputCheckboxWithNestedInputs from 'components/InputCheckboxWithNestedInputs';
|
import InputCheckboxWithNestedInputs from 'components/InputCheckboxWithNestedInputs';
|
||||||
import PopUpForm from 'components/PopUpForm';
|
import PopUpForm from 'components/PopUpForm';
|
||||||
@ -44,6 +44,7 @@ import {
|
|||||||
contentTypeFetch,
|
contentTypeFetch,
|
||||||
contentTypeFetchSucceeded,
|
contentTypeFetchSucceeded,
|
||||||
resetDidFetchModelProp,
|
resetDidFetchModelProp,
|
||||||
|
resetIsFormSet,
|
||||||
setAttributeForm,
|
setAttributeForm,
|
||||||
setForm,
|
setForm,
|
||||||
} from './actions';
|
} from './actions';
|
||||||
@ -100,6 +101,13 @@ export class Form extends React.Component { // eslint-disable-line react/prefer-
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addAttributeToContentType = () => {
|
||||||
|
this.props.addAttributeToContentType(this.props.modifiedDataAttribute);
|
||||||
|
// this.props.resetDidFetchModelProp();
|
||||||
|
this.props.resetIsFormSet();
|
||||||
|
router.push(`${this.props.redirectRoute}/${replace(this.props.hash.split('::')[0], '#create', '')}`);
|
||||||
|
}
|
||||||
|
|
||||||
addAttributeToTempContentType = () => {
|
addAttributeToTempContentType = () => {
|
||||||
const contentType = this.props.modifiedDataEdit;
|
const contentType = this.props.modifiedDataEdit;
|
||||||
contentType.attributes.push(this.props.modifiedDataAttribute);
|
contentType.attributes.push(this.props.modifiedDataAttribute);
|
||||||
@ -209,6 +217,8 @@ export class Form extends React.Component { // eslint-disable-line react/prefer-
|
|||||||
this.testContentType(
|
this.testContentType(
|
||||||
replace(split(this.props.hash, '::')[0], '#create', ''),
|
replace(split(this.props.hash, '::')[0], '#create', ''),
|
||||||
this.addAttributeToTempContentType,
|
this.addAttributeToTempContentType,
|
||||||
|
null,
|
||||||
|
this.addAttributeToContentType,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.createContentType(this.props.modifiedData);
|
this.createContentType(this.props.modifiedData);
|
||||||
@ -263,7 +273,7 @@ export class Form extends React.Component { // eslint-disable-line react/prefer-
|
|||||||
this.props.toggle();
|
this.props.toggle();
|
||||||
// Set the didFetchModel props to false when the modal is closing so the store is emptied
|
// Set the didFetchModel props to false when the modal is closing so the store is emptied
|
||||||
if (this.state.showModal) {
|
if (this.state.showModal) {
|
||||||
this.props.resetDidFetchModelProp();
|
this.props.resetIsFormSet();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,6 +337,7 @@ const mapStateToProps = selectForm();
|
|||||||
function mapDispatchToProps(dispatch) {
|
function mapDispatchToProps(dispatch) {
|
||||||
return bindActionCreators(
|
return bindActionCreators(
|
||||||
{
|
{
|
||||||
|
addAttributeToContentType,
|
||||||
changeInput,
|
changeInput,
|
||||||
changeInputAttribute,
|
changeInputAttribute,
|
||||||
connectionsFetch,
|
connectionsFetch,
|
||||||
@ -335,6 +346,7 @@ function mapDispatchToProps(dispatch) {
|
|||||||
contentTypeFetch,
|
contentTypeFetch,
|
||||||
contentTypeFetchSucceeded,
|
contentTypeFetchSucceeded,
|
||||||
resetDidFetchModelProp,
|
resetDidFetchModelProp,
|
||||||
|
resetIsFormSet,
|
||||||
setAttributeForm,
|
setAttributeForm,
|
||||||
setForm,
|
setForm,
|
||||||
storeTemporaryMenu,
|
storeTemporaryMenu,
|
||||||
@ -344,6 +356,7 @@ function mapDispatchToProps(dispatch) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Form.propTypes = {
|
Form.propTypes = {
|
||||||
|
addAttributeToContentType: React.PropTypes.func,
|
||||||
changeInput: React.PropTypes.func.isRequired,
|
changeInput: React.PropTypes.func.isRequired,
|
||||||
changeInputAttribute: React.PropTypes.func,
|
changeInputAttribute: React.PropTypes.func,
|
||||||
connectionsFetch: React.PropTypes.func.isRequired,
|
connectionsFetch: React.PropTypes.func.isRequired,
|
||||||
@ -365,6 +378,7 @@ Form.propTypes = {
|
|||||||
popUpHeaderNavLinks: React.PropTypes.array,
|
popUpHeaderNavLinks: React.PropTypes.array,
|
||||||
redirectRoute: React.PropTypes.string.isRequired,
|
redirectRoute: React.PropTypes.string.isRequired,
|
||||||
resetDidFetchModelProp: React.PropTypes.func,
|
resetDidFetchModelProp: React.PropTypes.func,
|
||||||
|
resetIsFormSet: React.PropTypes.func,
|
||||||
routePath: React.PropTypes.string,
|
routePath: React.PropTypes.string,
|
||||||
selectOptions: React.PropTypes.array,
|
selectOptions: React.PropTypes.array,
|
||||||
selectOptionsFetchSucceeded: React.PropTypes.bool,
|
selectOptionsFetchSucceeded: React.PropTypes.bool,
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import {
|
|||||||
CONTENT_TYPE_CREATE,
|
CONTENT_TYPE_CREATE,
|
||||||
CONTENT_TYPE_FETCH_SUCCEEDED,
|
CONTENT_TYPE_FETCH_SUCCEEDED,
|
||||||
RESET_DID_FETCH_MODEL_PROP,
|
RESET_DID_FETCH_MODEL_PROP,
|
||||||
|
RESET_IS_FORM_SET,
|
||||||
SET_ATTRIBUTE_FORM,
|
SET_ATTRIBUTE_FORM,
|
||||||
SET_FORM,
|
SET_FORM,
|
||||||
} from './constants';
|
} from './constants';
|
||||||
@ -67,6 +68,8 @@ function formReducer(state = initialState, action) {
|
|||||||
return state
|
return state
|
||||||
.set('didFetchModel', true)
|
.set('didFetchModel', true)
|
||||||
.set('isFormSet', false);
|
.set('isFormSet', false);
|
||||||
|
case RESET_IS_FORM_SET:
|
||||||
|
return state.set('isFormSet', false);
|
||||||
case SET_ATTRIBUTE_FORM: {
|
case SET_ATTRIBUTE_FORM: {
|
||||||
if (state.get('isFormSet')) {
|
if (state.get('isFormSet')) {
|
||||||
return state.set('form', Map(action.form));
|
return state.set('form', Map(action.form));
|
||||||
|
|||||||
@ -7,12 +7,29 @@ import { get } from 'lodash';
|
|||||||
import { storeData } from '../../utils/storeData';
|
import { storeData } from '../../utils/storeData';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
ADD_ATTRIBUTE_TO_CONTENT_TYPE,
|
||||||
|
CANCEL_CHANGES,
|
||||||
DEFAULT_ACTION,
|
DEFAULT_ACTION,
|
||||||
DELETE_ATTRIBUTE,
|
DELETE_ATTRIBUTE,
|
||||||
MODEL_FETCH,
|
MODEL_FETCH,
|
||||||
MODEL_FETCH_SUCCEEDED,
|
MODEL_FETCH_SUCCEEDED,
|
||||||
|
POST_CONTENT_TYPE_SUCCEEDED,
|
||||||
|
SUBMIT,
|
||||||
} from './constants';
|
} from './constants';
|
||||||
|
|
||||||
|
export function addAttributeToContentType(newAttribute) {
|
||||||
|
return {
|
||||||
|
type: ADD_ATTRIBUTE_TO_CONTENT_TYPE,
|
||||||
|
newAttribute,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cancelChanges() {
|
||||||
|
return {
|
||||||
|
type: CANCEL_CHANGES,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function deleteAttribute(position, modelName) {
|
export function deleteAttribute(position, modelName) {
|
||||||
const temporaryContentType = storeData.getContentType();
|
const temporaryContentType = storeData.getContentType();
|
||||||
let sendRequest = true;
|
let sendRequest = true;
|
||||||
@ -50,3 +67,15 @@ export function modelFetchSucceeded(model) {
|
|||||||
model,
|
model,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function postContentTypeSucceeded() {
|
||||||
|
return {
|
||||||
|
type: POST_CONTENT_TYPE_SUCCEEDED,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function submit() {
|
||||||
|
return {
|
||||||
|
type: SUBMIT,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -4,7 +4,11 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
export const ADD_ATTRIBUTE_TO_CONTENT_TYPE = 'ContentTypeBuilder/ModelPage/ADD_ATTRIBUTE_TO_CONTENT_TYPE';
|
||||||
|
export const CANCEL_CHANGES = 'ContentTypeBuilder/ModelPage/CANCEL_CHANGES';
|
||||||
export const DEFAULT_ACTION = 'ContentTypeBuilder/ModelPage/DEFAULT_ACTION';
|
export const DEFAULT_ACTION = 'ContentTypeBuilder/ModelPage/DEFAULT_ACTION';
|
||||||
export const DELETE_ATTRIBUTE = 'ContentTypeBuilder/ModelPage/DELETE_ATTRIBUTE';
|
export const DELETE_ATTRIBUTE = 'ContentTypeBuilder/ModelPage/DELETE_ATTRIBUTE';
|
||||||
export const MODEL_FETCH = 'ContentTypeBuilder/ModelPage/MODEL_FETCH';
|
export const MODEL_FETCH = 'ContentTypeBuilder/ModelPage/MODEL_FETCH';
|
||||||
export const MODEL_FETCH_SUCCEEDED = 'ContentTypeBuilder/ModelPage/MODEL_FETCH_SUCCEEDED';
|
export const MODEL_FETCH_SUCCEEDED = 'ContentTypeBuilder/ModelPage/MODEL_FETCH_SUCCEEDED';
|
||||||
|
export const POST_CONTENT_TYPE_SUCCEEDED = 'ContentTypeBuilder/ModelPage/POST_CONTENT_TYPE_SUCCEEDED';
|
||||||
|
export const SUBMIT = 'ContentTypeBuilder/ModelPage/SUBMIT';
|
||||||
|
|||||||
@ -14,7 +14,6 @@ import { Link } from 'react-router';
|
|||||||
import { router } from 'app';
|
import { router } from 'app';
|
||||||
|
|
||||||
// Global selectors
|
// Global selectors
|
||||||
import { makeSelectMenu } from 'containers/App/selectors';
|
|
||||||
import { makeSelectDidFetchModel } from 'containers/Form/selectors';
|
import { makeSelectDidFetchModel } from 'containers/Form/selectors';
|
||||||
|
|
||||||
import AttributeRow from 'components/AttributeRow';
|
import AttributeRow from 'components/AttributeRow';
|
||||||
@ -27,9 +26,11 @@ import PluginLeftMenu from 'components/PluginLeftMenu';
|
|||||||
import { storeData } from '../../utils/storeData';
|
import { storeData } from '../../utils/storeData';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
cancelChanges,
|
||||||
deleteAttribute,
|
deleteAttribute,
|
||||||
modelFetch,
|
modelFetch,
|
||||||
modelFetchSucceeded,
|
modelFetchSucceeded,
|
||||||
|
submit,
|
||||||
} from './actions';
|
} from './actions';
|
||||||
|
|
||||||
import selectModelPage from './selectors';
|
import selectModelPage from './selectors';
|
||||||
@ -88,11 +89,6 @@ export class ModelPage extends React.Component { // eslint-disable-line react/pr
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
handleEditAttribute = (attributeName) => {
|
|
||||||
const index = findIndex(this.props.modelPage.model.attributes, ['name', attributeName]);
|
|
||||||
console.log(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
fetchModel = () => {
|
fetchModel = () => {
|
||||||
if (storeData.getIsModelTemporary() && get(storeData.getContentType(), 'name') === this.props.params.modelName) {
|
if (storeData.getIsModelTemporary() && get(storeData.getContentType(), 'name') === this.props.params.modelName) {
|
||||||
this.props.modelFetchSucceeded({ model: storeData.getContentType() });
|
this.props.modelFetchSucceeded({ model: storeData.getContentType() });
|
||||||
@ -118,6 +114,12 @@ export class ModelPage extends React.Component { // eslint-disable-line react/pr
|
|||||||
this.props.deleteAttribute(index, this.props.params.modelName);
|
this.props.deleteAttribute(index, this.props.params.modelName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleEditAttribute = (attributeName) => {
|
||||||
|
const index = findIndex(this.props.modelPage.model.attributes, ['name', attributeName]);
|
||||||
|
console.log(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
toggleModal = () => {
|
toggleModal = () => {
|
||||||
const locationHash = this.props.location.hash ? '' : '#create::contentType::baseSettings';
|
const locationHash = this.props.location.hash ? '' : '#create::contentType::baseSettings';
|
||||||
router.push(`plugins/content-type-builder/models/${this.props.params.modelName}${locationHash}`);
|
router.push(`plugins/content-type-builder/models/${this.props.params.modelName}${locationHash}`);
|
||||||
@ -185,6 +187,8 @@ export class ModelPage extends React.Component { // eslint-disable-line react/pr
|
|||||||
render() {
|
render() {
|
||||||
// Url to redirects the user if he modifies the temporary content type name
|
// Url to redirects the user if he modifies the temporary content type name
|
||||||
const redirectRoute = replace(this.props.route.path, '/:modelName', '');
|
const redirectRoute = replace(this.props.route.path, '/:modelName', '');
|
||||||
|
const addButtons = size(get(this.props.modelPage.model, 'attributes')) > 0;
|
||||||
|
|
||||||
const content = size(this.props.modelPage.model.attributes) === 0 ?
|
const content = size(this.props.modelPage.model.attributes) === 0 ?
|
||||||
<EmptyAttributesView handleClick={this.handleClickAddAttribute} /> :
|
<EmptyAttributesView handleClick={this.handleClickAddAttribute} /> :
|
||||||
<List
|
<List
|
||||||
@ -194,7 +198,6 @@ export class ModelPage extends React.Component { // eslint-disable-line react/pr
|
|||||||
renderCustomLi={this.renderCustomLi}
|
renderCustomLi={this.renderCustomLi}
|
||||||
handleButtonClick={this.handleClickAddAttribute}
|
handleButtonClick={this.handleClickAddAttribute}
|
||||||
/>;
|
/>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.modelPage}>
|
<div className={styles.modelPage}>
|
||||||
<div className="container-fluid">
|
<div className="container-fluid">
|
||||||
@ -209,9 +212,12 @@ export class ModelPage extends React.Component { // eslint-disable-line react/pr
|
|||||||
<ContentHeader
|
<ContentHeader
|
||||||
name={this.props.modelPage.model.name}
|
name={this.props.modelPage.model.name}
|
||||||
description={this.props.modelPage.model.description}
|
description={this.props.modelPage.model.description}
|
||||||
noI18n
|
icoType="pencil"
|
||||||
editIcon
|
editIcon
|
||||||
editPath={`${redirectRoute}/${this.props.params.modelName}#edit${this.props.params.modelName}::contentType::baseSettings`}
|
editPath={`${redirectRoute}/${this.props.params.modelName}#edit${this.props.params.modelName}::contentType::baseSettings`}
|
||||||
|
addButtons={addButtons}
|
||||||
|
handleSubmit={this.props.submit}
|
||||||
|
handleCancel={this.props.cancelChanges}
|
||||||
/>
|
/>
|
||||||
{content}
|
{content}
|
||||||
</div>
|
</div>
|
||||||
@ -234,22 +240,24 @@ export class ModelPage extends React.Component { // eslint-disable-line react/pr
|
|||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
modelPage: selectModelPage(),
|
modelPage: selectModelPage(),
|
||||||
menu: makeSelectMenu(),
|
|
||||||
didFetchModel: makeSelectDidFetchModel(),
|
didFetchModel: makeSelectDidFetchModel(),
|
||||||
});
|
});
|
||||||
|
|
||||||
function mapDispatchToProps(dispatch) {
|
function mapDispatchToProps(dispatch) {
|
||||||
return bindActionCreators(
|
return bindActionCreators(
|
||||||
{
|
{
|
||||||
|
cancelChanges,
|
||||||
deleteAttribute,
|
deleteAttribute,
|
||||||
modelFetch,
|
modelFetch,
|
||||||
modelFetchSucceeded,
|
modelFetchSucceeded,
|
||||||
|
submit,
|
||||||
},
|
},
|
||||||
dispatch,
|
dispatch,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ModelPage.propTypes = {
|
ModelPage.propTypes = {
|
||||||
|
cancelChanges: React.PropTypes.func,
|
||||||
deleteAttribute: React.PropTypes.func,
|
deleteAttribute: React.PropTypes.func,
|
||||||
didFetchModel: React.PropTypes.bool,
|
didFetchModel: React.PropTypes.bool,
|
||||||
location: React.PropTypes.object,
|
location: React.PropTypes.object,
|
||||||
@ -259,6 +267,7 @@ ModelPage.propTypes = {
|
|||||||
modelPage: React.PropTypes.object,
|
modelPage: React.PropTypes.object,
|
||||||
params: React.PropTypes.object,
|
params: React.PropTypes.object,
|
||||||
route: React.PropTypes.object,
|
route: React.PropTypes.object,
|
||||||
|
submit: React.PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(ModelPage);
|
export default connect(mapStateToProps, mapDispatchToProps)(ModelPage);
|
||||||
|
|||||||
@ -7,27 +7,40 @@
|
|||||||
import { fromJS, Map, List } from 'immutable';
|
import { fromJS, Map, List } from 'immutable';
|
||||||
/* eslint-disable new-cap */
|
/* eslint-disable new-cap */
|
||||||
import {
|
import {
|
||||||
|
ADD_ATTRIBUTE_TO_CONTENT_TYPE,
|
||||||
|
CANCEL_CHANGES,
|
||||||
DELETE_ATTRIBUTE,
|
DELETE_ATTRIBUTE,
|
||||||
MODEL_FETCH_SUCCEEDED,
|
MODEL_FETCH_SUCCEEDED,
|
||||||
|
POST_CONTENT_TYPE_SUCCEEDED,
|
||||||
} from './constants';
|
} from './constants';
|
||||||
|
|
||||||
const initialState = fromJS({
|
const initialState = fromJS({
|
||||||
|
initialModel: Map({
|
||||||
|
attributes: List(),
|
||||||
|
}),
|
||||||
model: Map({
|
model: Map({
|
||||||
attributes: List(),
|
attributes: List(),
|
||||||
}),
|
}),
|
||||||
|
postContentTypeSuccess: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
function modelPageReducer(state = initialState, action) {
|
function modelPageReducer(state = initialState, action) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
case ADD_ATTRIBUTE_TO_CONTENT_TYPE:
|
||||||
|
return state.updateIn(['model', 'attributes'], (list) => list.push(action.newAttribute));
|
||||||
|
case CANCEL_CHANGES:
|
||||||
|
return state.set('model', state.get('initialModel'));
|
||||||
case DELETE_ATTRIBUTE:
|
case DELETE_ATTRIBUTE:
|
||||||
// console.log(action.position);
|
|
||||||
// console.log(state.getIn(['model', 'attributes']))
|
|
||||||
return state
|
return state
|
||||||
.updateIn(['model', 'attributes'], (list) => list.splice(action.position, 1));
|
.updateIn(['model', 'attributes'], (list) => list.splice(action.position, 1));
|
||||||
case MODEL_FETCH_SUCCEEDED:
|
case MODEL_FETCH_SUCCEEDED:
|
||||||
return state
|
return state
|
||||||
.set('model', Map(action.model.model))
|
.set('model', Map(action.model.model))
|
||||||
.setIn(['model', 'attributes'], List(action.model.model.attributes));
|
.set('initialModel', Map(action.model.model))
|
||||||
|
.setIn(['model', 'attributes'], List(action.model.model.attributes))
|
||||||
|
.setIn(['initialModel', 'attributes'], List(action.model.model.attributes));
|
||||||
|
case POST_CONTENT_TYPE_SUCCEEDED:
|
||||||
|
return state.set('postContentTypeSuccess', !state.get('postContentTypeSuccess'));
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,16 @@
|
|||||||
import { LOCATION_CHANGE } from 'react-router-redux';
|
import { LOCATION_CHANGE } from 'react-router-redux';
|
||||||
|
import { get } from 'lodash';
|
||||||
import { takeLatest } from 'redux-saga';
|
import { takeLatest } from 'redux-saga';
|
||||||
import { call, take, put, fork, cancel, select } from 'redux-saga/effects';
|
import { call, take, put, fork, cancel, select } from 'redux-saga/effects';
|
||||||
|
|
||||||
import request from 'utils/request';
|
import request from 'utils/request';
|
||||||
import { DELETE_ATTRIBUTE, MODEL_FETCH } from './constants';
|
|
||||||
import { modelFetchSucceeded } from './actions';
|
import { temporaryContentTypePosted } from 'containers/App/actions';
|
||||||
|
|
||||||
|
import { storeData } from '../../utils/storeData';
|
||||||
|
|
||||||
|
import { DELETE_ATTRIBUTE, MODEL_FETCH, SUBMIT } from './constants';
|
||||||
|
import { modelFetchSucceeded, postContentTypeSucceeded } from './actions';
|
||||||
import { makeSelectModel } from './selectors';
|
import { makeSelectModel } from './selectors';
|
||||||
|
|
||||||
// Individual exports for testing
|
// Individual exports for testing
|
||||||
@ -37,14 +44,40 @@ export function* fetchModel(action) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function* submitChanges() {
|
||||||
|
try {
|
||||||
|
const modelName = get(storeData.getContentType(), 'name');
|
||||||
|
|
||||||
|
const body = yield select(makeSelectModel());
|
||||||
|
|
||||||
|
const method = modelName === body.name ? 'POST' : 'PUT';
|
||||||
|
const baseUrl = '/content-type-builder/models/';
|
||||||
|
const requestUrl = method === 'POST' ? baseUrl : `${baseUrl}${body.name}`;
|
||||||
|
const opts = { method, body };
|
||||||
|
|
||||||
|
yield call(request, requestUrl, opts);
|
||||||
|
|
||||||
|
if (method === 'POST') {
|
||||||
|
storeData.clearAppStorage();
|
||||||
|
yield put(temporaryContentTypePosted());
|
||||||
|
yield put(postContentTypeSucceeded());
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch(error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function* defaultSaga() {
|
export function* defaultSaga() {
|
||||||
const loadModelWatcher = yield fork(takeLatest, MODEL_FETCH, fetchModel);
|
const loadModelWatcher = yield fork(takeLatest, MODEL_FETCH, fetchModel);
|
||||||
const deleteAttributeWatcher = yield fork(takeLatest, DELETE_ATTRIBUTE, attributeDelete);
|
const deleteAttributeWatcher = yield fork(takeLatest, DELETE_ATTRIBUTE, attributeDelete);
|
||||||
|
const loadSubmitChanges = yield fork(takeLatest, SUBMIT, submitChanges);
|
||||||
|
|
||||||
yield take(LOCATION_CHANGE);
|
yield take(LOCATION_CHANGE);
|
||||||
|
|
||||||
yield cancel(loadModelWatcher);
|
yield cancel(loadModelWatcher);
|
||||||
yield cancel(deleteAttributeWatcher);
|
yield cancel(deleteAttributeWatcher);
|
||||||
|
yield cancel(loadSubmitChanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
// All sagas to be loaded
|
// All sagas to be loaded
|
||||||
|
|||||||
@ -23,6 +23,9 @@
|
|||||||
"form.attribute.item.requiredField.description": "You won't be able to create an entry if this field is empty",
|
"form.attribute.item.requiredField.description": "You won't be able to create an entry if this field is empty",
|
||||||
"form.attribute.item.uniqueField.description": "You won't be able to create an entry if there is an existing entry with identical content",
|
"form.attribute.item.uniqueField.description": "You won't be able to create an entry if there is an existing entry with identical content",
|
||||||
|
|
||||||
|
"form.button.cancel": "Cancel",
|
||||||
|
"form.button.save": "Save",
|
||||||
|
|
||||||
"form.contentType.item.connections": "Connection",
|
"form.contentType.item.connections": "Connection",
|
||||||
"form.contentType.item.name": "Name",
|
"form.contentType.item.name": "Name",
|
||||||
"form.contentType.item.description": "Description",
|
"form.contentType.item.description": "Description",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user