Add metadata to draft entry fields (#196)
* Add metadata to draft entry fields * Do not render widget if value is null * Pass along metadata * Namespace queries to avoid conflict * Query relational field on mount (for when editing entries) * Make sure metadata is Immutable * Added collection name as metadata keys
This commit is contained in:
parent
e47a12f6ec
commit
ddfdc59941
@ -22,6 +22,7 @@ export const DRAFT_CREATE_FROM_ENTRY = 'DRAFT_CREATE_FROM_ENTRY';
|
|||||||
export const DRAFT_CREATE_EMPTY = 'DRAFT_CREATE_EMPTY';
|
export const DRAFT_CREATE_EMPTY = 'DRAFT_CREATE_EMPTY';
|
||||||
export const DRAFT_DISCARD = 'DRAFT_DISCARD';
|
export const DRAFT_DISCARD = 'DRAFT_DISCARD';
|
||||||
export const DRAFT_CHANGE = 'DRAFT_CHANGE';
|
export const DRAFT_CHANGE = 'DRAFT_CHANGE';
|
||||||
|
export const DRAFT_CHANGE_FIELD = 'DRAFT_CHANGE_FIELD';
|
||||||
|
|
||||||
export const ENTRY_PERSIST_REQUEST = 'ENTRY_PERSIST_REQUEST';
|
export const ENTRY_PERSIST_REQUEST = 'ENTRY_PERSIST_REQUEST';
|
||||||
export const ENTRY_PERSIST_SUCCESS = 'ENTRY_PERSIST_SUCCESS';
|
export const ENTRY_PERSIST_SUCCESS = 'ENTRY_PERSIST_SUCCESS';
|
||||||
@ -112,7 +113,7 @@ export function entryPersistFail(collection, entry, error) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function emmptyDraftCreated(entry) {
|
export function emptyDraftCreated(entry) {
|
||||||
return {
|
return {
|
||||||
type: DRAFT_CREATE_EMPTY,
|
type: DRAFT_CREATE_EMPTY,
|
||||||
payload: entry,
|
payload: entry,
|
||||||
@ -141,6 +142,13 @@ export function changeDraft(entry) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function changeDraftField(field, value, metadata) {
|
||||||
|
return {
|
||||||
|
type: DRAFT_CHANGE_FIELD,
|
||||||
|
payload: { field, value, metadata },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Exported Thunk Action Creators
|
* Exported Thunk Action Creators
|
||||||
*/
|
*/
|
||||||
@ -180,7 +188,7 @@ export function createEmptyDraft(collection) {
|
|||||||
dataFields[field.get('name')] = field.get('default', null);
|
dataFields[field.get('name')] = field.get('default', null);
|
||||||
});
|
});
|
||||||
const newEntry = createEntry(collection.get('name'), '', '', { data: dataFields });
|
const newEntry = createEntry(collection.get('name'), '', '', { data: dataFields });
|
||||||
dispatch(emmptyDraftCreated(newEntry));
|
dispatch(emptyDraftCreated(newEntry));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,9 +9,9 @@ export const SEARCH_ENTRIES_REQUEST = 'SEARCH_ENTRIES_REQUEST';
|
|||||||
export const SEARCH_ENTRIES_SUCCESS = 'SEARCH_ENTRIES_SUCCESS';
|
export const SEARCH_ENTRIES_SUCCESS = 'SEARCH_ENTRIES_SUCCESS';
|
||||||
export const SEARCH_ENTRIES_FAILURE = 'SEARCH_ENTRIES_FAILURE';
|
export const SEARCH_ENTRIES_FAILURE = 'SEARCH_ENTRIES_FAILURE';
|
||||||
|
|
||||||
export const QUERY_REQUEST = 'QUERY_REQUEST';
|
export const QUERY_REQUEST = 'INIT_QUERY';
|
||||||
export const QUERY_SUCCESS = 'QUERY_SUCCESS';
|
export const QUERY_SUCCESS = 'QUERY_OK';
|
||||||
export const QUERY_FAILURE = 'QUERY_FAILURE';
|
export const QUERY_FAILURE = 'QUERY_ERROR';
|
||||||
|
|
||||||
export const SEARCH_CLEAR = 'SEARCH_CLEAR';
|
export const SEARCH_CLEAR = 'SEARCH_CLEAR';
|
||||||
|
|
||||||
@ -47,10 +47,11 @@ export function searchFailure(searchTerm, error) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function querying(collection, searchFields, searchTerm) {
|
export function querying(namespace, collection, searchFields, searchTerm) {
|
||||||
return {
|
return {
|
||||||
type: QUERY_REQUEST,
|
type: QUERY_REQUEST,
|
||||||
payload: {
|
payload: {
|
||||||
|
namespace,
|
||||||
collection,
|
collection,
|
||||||
searchFields,
|
searchFields,
|
||||||
searchTerm,
|
searchTerm,
|
||||||
@ -58,10 +59,11 @@ export function querying(collection, searchFields, searchTerm) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function querySuccess(collection, searchFields, searchTerm, response) {
|
export function querySuccess(namespace, collection, searchFields, searchTerm, response) {
|
||||||
return {
|
return {
|
||||||
type: QUERY_SUCCESS,
|
type: QUERY_SUCCESS,
|
||||||
payload: {
|
payload: {
|
||||||
|
namespace,
|
||||||
collection,
|
collection,
|
||||||
searchFields,
|
searchFields,
|
||||||
searchTerm,
|
searchTerm,
|
||||||
@ -70,10 +72,11 @@ export function querySuccess(collection, searchFields, searchTerm, response) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function queryFailure(collection, searchFields, searchTerm, error) {
|
export function queryFailure(namespace, collection, searchFields, searchTerm, error) {
|
||||||
return {
|
return {
|
||||||
type: QUERY_SUCCESS,
|
type: QUERY_SUCCESS,
|
||||||
payload: {
|
payload: {
|
||||||
|
namespace,
|
||||||
collection,
|
collection,
|
||||||
searchFields,
|
searchFields,
|
||||||
searchTerm,
|
searchTerm,
|
||||||
@ -118,20 +121,20 @@ export function searchEntries(searchTerm, page = 0) {
|
|||||||
|
|
||||||
// Instead of searching for complete entries, query will search for specific fields
|
// Instead of searching for complete entries, query will search for specific fields
|
||||||
// in specific collections and return raw data (no entries).
|
// in specific collections and return raw data (no entries).
|
||||||
export function query(collection, searchFields, searchTerm) {
|
export function query(namespace, collection, searchFields, searchTerm) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const integration = selectIntegration(state, collection, 'search');
|
const integration = selectIntegration(state, collection, 'search');
|
||||||
if (!integration) {
|
if (!integration) {
|
||||||
dispatch(searchFailure(searchTerm, 'Search integration is not configured.'));
|
dispatch(searchFailure(namespace, searchTerm, 'Search integration is not configured.'));
|
||||||
}
|
}
|
||||||
const provider = integration ?
|
const provider = integration ?
|
||||||
getIntegrationProvider(state.integrations, integration)
|
getIntegrationProvider(state.integrations, integration)
|
||||||
: currentBackend(state.config);
|
: currentBackend(state.config);
|
||||||
dispatch(querying(collection, searchFields, searchTerm));
|
dispatch(querying(namespace, collection, searchFields, searchTerm));
|
||||||
provider.searchBy(searchFields, collection, searchTerm).then(
|
provider.searchBy(searchFields, collection, searchTerm).then(
|
||||||
response => dispatch(querySuccess(collection, searchFields, searchTerm, response)),
|
response => dispatch(querySuccess(namespace, collection, searchFields, searchTerm, response)),
|
||||||
error => dispatch(queryFailure(collection, searchFields, searchTerm, error))
|
error => dispatch(queryFailure(namespace, collection, searchFields, searchTerm, error))
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -10,10 +10,11 @@ function isHidden(field) {
|
|||||||
export default class ControlPane extends Component {
|
export default class ControlPane extends Component {
|
||||||
|
|
||||||
controlFor(field) {
|
controlFor(field) {
|
||||||
const { entry, getMedia, onChange, onAddMedia, onRemoveMedia } = this.props;
|
const { entry, fieldsMetaData, getMedia, onChange, onAddMedia, onRemoveMedia } = this.props;
|
||||||
const widget = resolveWidget(field.get('widget'));
|
const widget = resolveWidget(field.get('widget'));
|
||||||
const fieldName = field.get('name');
|
const fieldName = field.get('name');
|
||||||
const value = entry.getIn(['data', fieldName]);
|
const value = entry.getIn(['data', fieldName]);
|
||||||
|
const metadata = fieldsMetaData.get(fieldName);
|
||||||
if (entry.size === 0 || entry.get('partial') === true) return null;
|
if (entry.size === 0 || entry.get('partial') === true) return null;
|
||||||
return (
|
return (
|
||||||
<div className={styles.control}>
|
<div className={styles.control}>
|
||||||
@ -22,7 +23,8 @@ export default class ControlPane extends Component {
|
|||||||
React.createElement(widget.control, {
|
React.createElement(widget.control, {
|
||||||
field,
|
field,
|
||||||
value,
|
value,
|
||||||
onChange: val => onChange(entry.setIn(['data', fieldName], val)),
|
metadata,
|
||||||
|
onChange: (newValue, newMetadata) => onChange(fieldName, newValue, newMetadata),
|
||||||
onAddMedia,
|
onAddMedia,
|
||||||
onRemoveMedia,
|
onRemoveMedia,
|
||||||
getMedia,
|
getMedia,
|
||||||
@ -57,6 +59,7 @@ ControlPane.propTypes = {
|
|||||||
collection: ImmutablePropTypes.map.isRequired,
|
collection: ImmutablePropTypes.map.isRequired,
|
||||||
entry: ImmutablePropTypes.map.isRequired,
|
entry: ImmutablePropTypes.map.isRequired,
|
||||||
fields: ImmutablePropTypes.list.isRequired,
|
fields: ImmutablePropTypes.list.isRequired,
|
||||||
|
fieldsMetaData: ImmutablePropTypes.map.isRequired,
|
||||||
getMedia: PropTypes.func.isRequired,
|
getMedia: PropTypes.func.isRequired,
|
||||||
onAddMedia: PropTypes.func.isRequired,
|
onAddMedia: PropTypes.func.isRequired,
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,
|
||||||
|
@ -25,6 +25,7 @@ class EntryEditor extends Component {
|
|||||||
collection,
|
collection,
|
||||||
entry,
|
entry,
|
||||||
fields,
|
fields,
|
||||||
|
fieldsMetaData,
|
||||||
getMedia,
|
getMedia,
|
||||||
onChange,
|
onChange,
|
||||||
onAddMedia,
|
onAddMedia,
|
||||||
@ -51,6 +52,7 @@ class EntryEditor extends Component {
|
|||||||
collection={collection}
|
collection={collection}
|
||||||
entry={entry}
|
entry={entry}
|
||||||
fields={fields}
|
fields={fields}
|
||||||
|
fieldsMetaData={fieldsMetaData}
|
||||||
getMedia={getMedia}
|
getMedia={getMedia}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
onAddMedia={onAddMedia}
|
onAddMedia={onAddMedia}
|
||||||
@ -64,6 +66,7 @@ class EntryEditor extends Component {
|
|||||||
collection={collection}
|
collection={collection}
|
||||||
entry={entry}
|
entry={entry}
|
||||||
fields={fields}
|
fields={fields}
|
||||||
|
fieldsMetaData={fieldsMetaData}
|
||||||
getMedia={getMedia}
|
getMedia={getMedia}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -87,6 +90,7 @@ EntryEditor.propTypes = {
|
|||||||
collection: ImmutablePropTypes.map.isRequired,
|
collection: ImmutablePropTypes.map.isRequired,
|
||||||
entry: ImmutablePropTypes.map.isRequired,
|
entry: ImmutablePropTypes.map.isRequired,
|
||||||
fields: ImmutablePropTypes.list.isRequired,
|
fields: ImmutablePropTypes.list.isRequired,
|
||||||
|
fieldsMetaData: ImmutablePropTypes.map.isRequired,
|
||||||
getMedia: PropTypes.func.isRequired,
|
getMedia: PropTypes.func.isRequired,
|
||||||
onAddMedia: PropTypes.func.isRequired,
|
onAddMedia: PropTypes.func.isRequired,
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,
|
||||||
|
@ -29,20 +29,23 @@ export default class PreviewPane extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
widgetFor = (name) => {
|
widgetFor = (name) => {
|
||||||
const { fields, entry, getMedia } = this.props;
|
const { fields, entry, fieldsMetaData, getMedia } = this.props;
|
||||||
const field = fields.find(f => f.get('name') === name);
|
const field = fields.find(f => f.get('name') === name);
|
||||||
let value = entry.getIn(['data', field.get('name')]);
|
let value = entry.getIn(['data', field.get('name')]);
|
||||||
|
const metadata = fieldsMetaData.get(field.get('name'));
|
||||||
const labelledWidgets = ['string', 'text', 'number'];
|
const labelledWidgets = ['string', 'text', 'number'];
|
||||||
if (Object.keys(this.inferedFields).indexOf(name) !== -1) {
|
if (Object.keys(this.inferedFields).indexOf(name) !== -1) {
|
||||||
value = this.inferedFields[name].defaultPreview(value);
|
value = this.inferedFields[name].defaultPreview(value);
|
||||||
} else if (value && labelledWidgets.indexOf(field.get('widget')) !== -1 && value.toString().length < 50) {
|
} else if (value && labelledWidgets.indexOf(field.get('widget')) !== -1 && value.toString().length < 50) {
|
||||||
value = <div><strong>{field.get('label')}:</strong> {value}</div>;
|
value = <div><strong>{field.get('label')}:</strong> {value}</div>;
|
||||||
}
|
}
|
||||||
|
if (!value) return null;
|
||||||
const widget = resolveWidget(field.get('widget'));
|
const widget = resolveWidget(field.get('widget'));
|
||||||
return React.createElement(widget.preview, {
|
return React.createElement(widget.preview, {
|
||||||
key: field.get('name'),
|
key: field.get('name'),
|
||||||
value,
|
value,
|
||||||
field,
|
field,
|
||||||
|
metadata,
|
||||||
getMedia,
|
getMedia,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -100,5 +103,6 @@ PreviewPane.propTypes = {
|
|||||||
collection: ImmutablePropTypes.map.isRequired,
|
collection: ImmutablePropTypes.map.isRequired,
|
||||||
fields: ImmutablePropTypes.list.isRequired,
|
fields: ImmutablePropTypes.list.isRequired,
|
||||||
entry: ImmutablePropTypes.map.isRequired,
|
entry: ImmutablePropTypes.map.isRequired,
|
||||||
|
fieldsMetaData: ImmutablePropTypes.map.isRequired,
|
||||||
getMedia: PropTypes.func.isRequired,
|
getMedia: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
@ -83,10 +83,10 @@ export default class ListControl extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
handleChangeFor(index) {
|
handleChangeFor(index) {
|
||||||
return (newValue) => {
|
return (newValue, newMetadata) => {
|
||||||
const { value, onChange } = this.props;
|
const { value, onChange } = this.props;
|
||||||
const parsedValue = (this.valueType === valueTypes.SINGLE) ? newValue.first() : newValue;
|
const parsedValue = (this.valueType === valueTypes.SINGLE) ? newValue.first() : newValue;
|
||||||
onChange(value.set(index, parsedValue));
|
onChange(value.set(index, parsedValue), newMetadata);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,8 +25,8 @@ export default class ObjectControl extends Component {
|
|||||||
React.createElement(widget.control, {
|
React.createElement(widget.control, {
|
||||||
field,
|
field,
|
||||||
value: fieldValue,
|
value: fieldValue,
|
||||||
onChange: (val) => {
|
onChange: (val, metadata) => {
|
||||||
onChange((value || Map()).set(field.get('name'), val));
|
onChange((value || Map()).set(field.get('name'), val), metadata);
|
||||||
},
|
},
|
||||||
onAddMedia,
|
onAddMedia,
|
||||||
onRemoveMedia,
|
onRemoveMedia,
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import Autosuggest from 'react-autosuggest';
|
import Autosuggest from 'react-autosuggest';
|
||||||
|
import uuid from 'uuid';
|
||||||
|
import { Map } from 'immutable';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
import { Loader } from '../../components/UI/index';
|
import { Loader } from '../../components/UI/index';
|
||||||
@ -15,22 +17,57 @@ class RelationControl extends Component {
|
|||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,
|
||||||
value: PropTypes.node,
|
value: PropTypes.node,
|
||||||
field: PropTypes.node,
|
field: PropTypes.node,
|
||||||
isFetching: PropTypes.bool,
|
isFetching: PropTypes.node,
|
||||||
query: PropTypes.func.isRequired,
|
query: PropTypes.func.isRequired,
|
||||||
clearSearch: PropTypes.func.isRequired,
|
clearSearch: PropTypes.func.isRequired,
|
||||||
queryHits: PropTypes.array, // eslint-disable-line
|
queryHits: PropTypes.oneOfType([
|
||||||
|
PropTypes.array,
|
||||||
|
PropTypes.object,
|
||||||
|
]),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constructor(props, ctx) {
|
||||||
|
super(props, ctx);
|
||||||
|
this.controlID = uuid.v4();
|
||||||
|
this.didInitialSearch = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const { value, field } = this.props;
|
||||||
|
if (value) {
|
||||||
|
const collection = field.get('collection');
|
||||||
|
const searchFields = field.get('searchFields').map(f => `data.${ f }`).toJS();
|
||||||
|
this.props.query(this.controlID, collection, searchFields, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
if (this.didInitialSearch) return;
|
||||||
|
if (nextProps.queryHits !== this.props.queryHits && nextProps.queryHits.get && nextProps.queryHits.get(this.controlID)) {
|
||||||
|
this.didInitialSearch = true;
|
||||||
|
const suggestion = nextProps.queryHits.get(this.controlID);
|
||||||
|
if (suggestion && suggestion.length === 1) {
|
||||||
|
const val = this.getSuggestionValue(suggestion[0]);
|
||||||
|
this.props.onChange(val, { [nextProps.field.get('collection')]: { [val]: suggestion[0].data } });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onChange = (event, { newValue }) => {
|
onChange = (event, { newValue }) => {
|
||||||
this.props.onChange(newValue);
|
this.props.onChange(newValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onSuggestionSelected = (event, { suggestion }) => {
|
||||||
|
const value = this.getSuggestionValue(suggestion);
|
||||||
|
this.props.onChange(value, { [this.props.field.get('collection')]: { [value]: suggestion.data } });
|
||||||
|
};
|
||||||
|
|
||||||
onSuggestionsFetchRequested = debounce(({ value }) => {
|
onSuggestionsFetchRequested = debounce(({ value }) => {
|
||||||
if (value.length < 3) return;
|
if (value.length < 2) return;
|
||||||
const { field } = this.props;
|
const { field } = this.props;
|
||||||
const collection = field.get('collection');
|
const collection = field.get('collection');
|
||||||
const searchFields = field.get('searchFields').map(f => `data.${ f }`).toJS();
|
const searchFields = field.get('searchFields').map(f => `data.${ f }`).toJS();
|
||||||
this.props.query(collection, searchFields, value);
|
this.props.query(this.controlID, collection, searchFields, value);
|
||||||
}, 80);
|
}, 80);
|
||||||
|
|
||||||
onSuggestionsClearRequested = () => {
|
onSuggestionsClearRequested = () => {
|
||||||
@ -46,8 +83,7 @@ class RelationControl extends Component {
|
|||||||
if (escapedValue === '') {
|
if (escapedValue === '') {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
return queryHits.get(this.controlID).filter((hit) => {
|
||||||
return queryHits.filter((hit) => {
|
|
||||||
let testResult = false;
|
let testResult = false;
|
||||||
searchFields.forEach((f) => {
|
searchFields.forEach((f) => {
|
||||||
testResult = testResult || regex.test(hit.data[f]);
|
testResult = testResult || regex.test(hit.data[f]);
|
||||||
@ -77,17 +113,20 @@ class RelationControl extends Component {
|
|||||||
onChange: this.onChange,
|
onChange: this.onChange,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const suggestions = (queryHits.get) ? queryHits.get(this.controlID, []) : [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Autosuggest
|
<Autosuggest
|
||||||
suggestions={queryHits}
|
suggestions={suggestions}
|
||||||
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested} // eslint-disable-line
|
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
|
||||||
onSuggestionsClearRequested={this.onSuggestionsClearRequested} // eslint-disable-line
|
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
|
||||||
|
onSuggestionSelected={this.onSuggestionSelected}
|
||||||
getSuggestionValue={this.getSuggestionValue}
|
getSuggestionValue={this.getSuggestionValue}
|
||||||
renderSuggestion={this.renderSuggestion}
|
renderSuggestion={this.renderSuggestion}
|
||||||
inputProps={inputProps}
|
inputProps={inputProps}
|
||||||
/>
|
/>
|
||||||
<Loader active={isFetching} />
|
<Loader active={isFetching === this.controlID} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import {
|
|||||||
createDraftFromEntry,
|
createDraftFromEntry,
|
||||||
createEmptyDraft,
|
createEmptyDraft,
|
||||||
discardDraft,
|
discardDraft,
|
||||||
changeDraft,
|
changeDraftField,
|
||||||
persistEntry,
|
persistEntry,
|
||||||
} from '../actions/entries';
|
} from '../actions/entries';
|
||||||
import { cancelEdit } from '../actions/editor';
|
import { cancelEdit } from '../actions/editor';
|
||||||
@ -22,7 +22,7 @@ class EntryPage extends React.Component {
|
|||||||
static propTypes = {
|
static propTypes = {
|
||||||
addMedia: PropTypes.func.isRequired,
|
addMedia: PropTypes.func.isRequired,
|
||||||
boundGetMedia: PropTypes.func.isRequired,
|
boundGetMedia: PropTypes.func.isRequired,
|
||||||
changeDraft: PropTypes.func.isRequired,
|
changeDraftField: PropTypes.func.isRequired,
|
||||||
collection: ImmutablePropTypes.map.isRequired,
|
collection: ImmutablePropTypes.map.isRequired,
|
||||||
createDraftFromEntry: PropTypes.func.isRequired,
|
createDraftFromEntry: PropTypes.func.isRequired,
|
||||||
createEmptyDraft: PropTypes.func.isRequired,
|
createEmptyDraft: PropTypes.func.isRequired,
|
||||||
@ -79,7 +79,7 @@ class EntryPage extends React.Component {
|
|||||||
fields,
|
fields,
|
||||||
boundGetMedia,
|
boundGetMedia,
|
||||||
collection,
|
collection,
|
||||||
changeDraft,
|
changeDraftField,
|
||||||
addMedia,
|
addMedia,
|
||||||
removeMedia,
|
removeMedia,
|
||||||
cancelEdit,
|
cancelEdit,
|
||||||
@ -97,7 +97,8 @@ class EntryPage extends React.Component {
|
|||||||
getMedia={boundGetMedia}
|
getMedia={boundGetMedia}
|
||||||
collection={collection}
|
collection={collection}
|
||||||
fields={fields}
|
fields={fields}
|
||||||
onChange={changeDraft}
|
fieldsMetaData={entryDraft.get('fieldsMetaData')}
|
||||||
|
onChange={changeDraftField}
|
||||||
onAddMedia={addMedia}
|
onAddMedia={addMedia}
|
||||||
onRemoveMedia={removeMedia}
|
onRemoveMedia={removeMedia}
|
||||||
onPersist={this.handlePersistEntry}
|
onPersist={this.handlePersistEntry}
|
||||||
@ -130,7 +131,7 @@ function mapStateToProps(state, ownProps) {
|
|||||||
export default connect(
|
export default connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
{
|
{
|
||||||
changeDraft,
|
changeDraftField,
|
||||||
addMedia,
|
addMedia,
|
||||||
removeMedia,
|
removeMedia,
|
||||||
loadEntry,
|
loadEntry,
|
||||||
|
@ -2,7 +2,7 @@ import { Map, List, fromJS } from 'immutable';
|
|||||||
import * as actions from '../../actions/entries';
|
import * as actions from '../../actions/entries';
|
||||||
import reducer from '../entryDraft';
|
import reducer from '../entryDraft';
|
||||||
|
|
||||||
let initialState = Map({ entry: Map(), mediaFiles: List() });
|
let initialState = Map({ entry: Map(), mediaFiles: List(), fieldsMetaData: Map() });
|
||||||
|
|
||||||
const entry = {
|
const entry = {
|
||||||
collection: 'posts',
|
collection: 'posts',
|
||||||
@ -29,6 +29,7 @@ describe('entryDraft reducer', () => {
|
|||||||
newRecord: false,
|
newRecord: false,
|
||||||
},
|
},
|
||||||
mediaFiles: [],
|
mediaFiles: [],
|
||||||
|
fieldsMetaData: Map(),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -39,7 +40,7 @@ describe('entryDraft reducer', () => {
|
|||||||
expect(
|
expect(
|
||||||
reducer(
|
reducer(
|
||||||
initialState,
|
initialState,
|
||||||
actions.emmptyDraftCreated(fromJS(entry))
|
actions.emptyDraftCreated(fromJS(entry))
|
||||||
)
|
)
|
||||||
).toEqual(
|
).toEqual(
|
||||||
fromJS({
|
fromJS({
|
||||||
@ -48,6 +49,7 @@ describe('entryDraft reducer', () => {
|
|||||||
newRecord: true,
|
newRecord: true,
|
||||||
},
|
},
|
||||||
mediaFiles: [],
|
mediaFiles: [],
|
||||||
|
fieldsMetaData: Map(),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -3,7 +3,7 @@ import {
|
|||||||
DRAFT_CREATE_FROM_ENTRY,
|
DRAFT_CREATE_FROM_ENTRY,
|
||||||
DRAFT_CREATE_EMPTY,
|
DRAFT_CREATE_EMPTY,
|
||||||
DRAFT_DISCARD,
|
DRAFT_DISCARD,
|
||||||
DRAFT_CHANGE,
|
DRAFT_CHANGE_FIELD,
|
||||||
ENTRY_PERSIST_REQUEST,
|
ENTRY_PERSIST_REQUEST,
|
||||||
ENTRY_PERSIST_SUCCESS,
|
ENTRY_PERSIST_SUCCESS,
|
||||||
ENTRY_PERSIST_FAILURE,
|
ENTRY_PERSIST_FAILURE,
|
||||||
@ -13,7 +13,7 @@ import {
|
|||||||
REMOVE_MEDIA,
|
REMOVE_MEDIA,
|
||||||
} from '../actions/media';
|
} from '../actions/media';
|
||||||
|
|
||||||
const initialState = Map({ entry: Map(), mediaFiles: List() });
|
const initialState = Map({ entry: Map(), mediaFiles: List(), fieldsMetaData: Map() });
|
||||||
|
|
||||||
const entryDraftReducer = (state = Map(), action) => {
|
const entryDraftReducer = (state = Map(), action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
@ -23,6 +23,7 @@ const entryDraftReducer = (state = Map(), action) => {
|
|||||||
state.set('entry', action.payload);
|
state.set('entry', action.payload);
|
||||||
state.setIn(['entry', 'newRecord'], false);
|
state.setIn(['entry', 'newRecord'], false);
|
||||||
state.set('mediaFiles', List());
|
state.set('mediaFiles', List());
|
||||||
|
state.set('fieldsMetaData', Map());
|
||||||
});
|
});
|
||||||
case DRAFT_CREATE_EMPTY:
|
case DRAFT_CREATE_EMPTY:
|
||||||
// New Entry
|
// New Entry
|
||||||
@ -30,12 +31,15 @@ const entryDraftReducer = (state = Map(), action) => {
|
|||||||
state.set('entry', fromJS(action.payload));
|
state.set('entry', fromJS(action.payload));
|
||||||
state.setIn(['entry', 'newRecord'], true);
|
state.setIn(['entry', 'newRecord'], true);
|
||||||
state.set('mediaFiles', List());
|
state.set('mediaFiles', List());
|
||||||
|
state.set('fieldsMetaData', Map());
|
||||||
});
|
});
|
||||||
case DRAFT_DISCARD:
|
case DRAFT_DISCARD:
|
||||||
return initialState;
|
return initialState;
|
||||||
case DRAFT_CHANGE:
|
case DRAFT_CHANGE_FIELD:
|
||||||
return state.set('entry', action.payload);
|
return state.withMutations((state) => {
|
||||||
|
state.setIn(['entry', 'data', action.payload.field], action.payload.value);
|
||||||
|
state.mergeIn(['fieldsMetaData'], fromJS(action.payload.metadata));
|
||||||
|
});
|
||||||
case ENTRY_PERSIST_REQUEST: {
|
case ENTRY_PERSIST_REQUEST: {
|
||||||
return state.setIn(['entry', 'isPersisting'], true);
|
return state.setIn(['entry', 'isPersisting'], true);
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ const entries = (state = defaultState, action) => {
|
|||||||
case QUERY_REQUEST:
|
case QUERY_REQUEST:
|
||||||
if (action.payload.searchTerm !== state.get('term')) {
|
if (action.payload.searchTerm !== state.get('term')) {
|
||||||
return state.withMutations((map) => {
|
return state.withMutations((map) => {
|
||||||
map.set('isFetching', true);
|
map.set('isFetching', action.payload.namespace);
|
||||||
map.set('term', action.payload.searchTerm);
|
map.set('term', action.payload.searchTerm);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -56,7 +56,7 @@ const entries = (state = defaultState, action) => {
|
|||||||
return state.withMutations((map) => {
|
return state.withMutations((map) => {
|
||||||
map.set('isFetching', false);
|
map.set('isFetching', false);
|
||||||
map.set('term', searchTerm);
|
map.set('term', searchTerm);
|
||||||
map.set('queryHits', response.hits);
|
map.set('queryHits', Map({ [action.payload.namespace]: response.hits }));
|
||||||
});
|
});
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user