application/components/custom-content/channel.js
import React from 'react';
import {connect} from 'react-redux';
import Immutable from 'immutable';
import uuid from 'uuid';
import Paper from 'material-ui/Paper';
import Subheader from 'material-ui/Subheader';
import Divider from 'material-ui/Divider';
import FlatButton from 'material-ui/FlatButton';
import RaisedButton from 'material-ui/RaisedButton';
import IconButton from 'material-ui/IconButton';
import FontIcon from 'material-ui/FontIcon';
import TextField from 'material-ui/TextField';
import CheckBox from 'material-ui/Checkbox';
import DeleteButton from './../common/delete-button';
import {browserHistory, Link} from 'react-router';
import {Row, Col} from './../flexbox';
import Config from './../../config';
import CurrentUser from './../../current-user';
import EntryField from './field';
import {sluggify} from './../../util/strings';
import {
channelsFetchOne,
channelCreate,
channelUpdate,
channelsRemove
} from './../../redux/actions/channel-actions';
import {
snackbarShowMessage
} from './../../redux/actions/snackbar-actions';
class CustomContentChannel extends React.Component {
constructor(props) {
super(props);
this.state = {
'channel': Immutable.fromJS({
'channelMeta': []
}),
'isReady': false,
'newFields': Immutable.fromJS([]),
'newFieldElements': []
};
}
getRouter() {
return this.props.router;
}
/**
* Return the requested UID, starts with UUID property,
* then goes to the URL property.
* @return {string}
*/
getUuid() {
if (this.props.uuid) {
return this.props.uuid;
}
if (this.props.params && this.props.params.id) {
return this.props.params.id;
}
return false;
}
isNew() {
if (!this.getUuid() || this.getUuid() === 'new') {
return true;
}
return false;
}
componentWillMount() {
const {dispatch} = this.props;
if (!this.isNew()) {
dispatch(channelsFetchOne(this.getUuid()))
.then(() => {
const channel = this.props.channels.items.find((item) => item.get('uuid') == this.getUuid());
this.setState({
channel: channel,
isReady: true
});
});
} else {
this.setState({
channel: Immutable.fromJS({
channelMeta: []
}),
isReady: true
});
}
}
componentDidMount() {
}
handleSave(e) {
let data = {
'title': this.state.channel.get('title'),
'slug': this.state.channel.get('slug'),
'is_routable': this.state.channel.get('is_routable') ? 1 : 0,
'channelMeta': []
};
// process the existing fields
this.state.channel.get('channelMeta').map((item) => {
let fieldData = {
'uuid': item.get('uuid'),
'type': item.get('type'),
'label': item.get('label'),
'description': item.get('description')
};
data.channelMeta.push(fieldData);
});
const {dispatch} = this.props;
if (!this.isNew()) {
dispatch(channelUpdate(this.getUuid(), data))
.then(() => {
dispatch(snackbarShowMessage('Channel saved'));
browserHistory.push('/ceo/redirect?next=custom/channel/' + this.getUuid());
});
} else {
dispatch(channelCreate(data))
.then((resp) => {
dispatch(snackbarShowMessage('Channel created'));
const uuid = resp.payload.channel.get('uuid');
browserHistory.push('/ceo/redirect?next=custom/channel/' + uuid);
});
}
}
handleDelete() {
const {dispatch} = this.props;
dispatch(channelsRemove(this.getUuid()))
.then(() => {
browserHistory.push('/ceo/custom');
dispatch(snackbarShowMessage('Channel removed'));
});
}
handleTextChange(e) {
const key = e.target.name;
let channel = this.state.channel.asMutable();
switch(key) {
case 'slug':
channel.set(key, sluggify(e.target.value, true));
break;
case 'title':
channel.set('slug', sluggify(e.target.value, true));
channel.set(key, e.target.value);
break;
default:
channel.set(key, e.target.value);
break;
}
this.setState({'channel': channel.asImmutable()});
}
handleUpdateRoutable(e, checked) {
let channel = this.state.channel.asMutable();
channel.set('is_routable', checked ? 1 : 0);
this.setState({'channel': channel.asImmutable()});
}
handleAddField(e) {
const field = {
'uuid': uuid.v4(),
'type': null,
'label': null,
'description': null,
'isNew': true
};
const newFieldData = Immutable.fromJS(field);
this.setState({
channel: this.state.channel.set(
'channelMeta', this.state.channel.get('channelMeta').push(newFieldData)
)
});
}
handleFieldsChanged(field) {
const uuid = field.uuid;
const index = this.state.channel.get('channelMeta').findIndex((item) => item.get('uuid') == uuid);
const newFieldData = Immutable.fromJS(field);
this.setState({
channel: this.state.channel.set('channelMeta', this.state.channel.get('channelMeta').set(index, newFieldData))
});
}
handleRemoveField(field) {
const uuid = field.uuid;
let index = this.state.channel.get('channelMeta').findIndex((item) => item.get('uuid') == uuid);
if (index == -1) {
return;
}
let channel = this.state.channel;
const newMeta = channel.get('channelMeta').delete(index);
const newChannel = channel.set('channelMeta', newMeta);
this.setState({
channel: newChannel
});
}
render() {
return (
<div className="channel-item-root">
<Row>
<Col xs={8} offsetXs={2}>
<Paper className='toolbar' style={{ paddingTop:'10px', paddingBottom:'10px' }}>
<Row middle='xs'>
<Col xs={1}>
<IconButton
containerElement={<Link to="/ceo/custom" />}
>
<FontIcon className="mui-icons">keyboard_arrow_left</FontIcon>
</IconButton>
</Col>
<Col xs={6}>
<FlatButton
label="Save Channel"
primary={true}
onClick={this.handleSave.bind(this)}
/>
</Col>
<Col xs={5} end='xs'>
<DeleteButton
disabled={this.isNew()}
onDelete={this.handleDelete.bind(this)}
/>
</Col>
</Row>
</Paper>
<Row top='xs'>
<Col xs={12}>
<Paper className='padded clear-bottom'>
<p>Channels are the basic container for custom content. Here you can define the fields for each custom channel type.</p>
<p>A publicly routable channel is one that can be accessed using its slug as the URL on your site.</p>
<TextField
name='title'
fullWidth={true}
floatingLabelText='Channel Title'
value={this.state.channel.get('title')}
onChange={this.handleTextChange.bind(this)}
/>
<TextField
name='slug'
fullWidth={true}
floatingLabelText='Slug'
value={this.state.channel.get('slug')}
onChange={this.handleTextChange.bind(this)}
/>
<CheckBox
name='is_routable'
label='Channel is publicly routable'
checked={parseInt(this.state.channel.get('is_routable')) ? true : false}
onCheck={this.handleUpdateRoutable.bind(this)}
/>
</Paper>
{this.state.channel.get('channelMeta').map((item) => {
return <Paper className="padded clear-bottom"><EntryField field={item} onRemove={this.handleRemoveField.bind(this)} onChange={this.handleFieldsChanged.bind(this)} /></Paper>
})}
<RaisedButton
secondary={true}
label="Add Field"
onClick={this.handleAddField.bind(this)}
/>
</Col>
</Row>
</Col>
</Row>
</div>
);
}
}
const mapStateToProps = state => {
return {
channels: state.channels
};
}
export default connect(mapStateToProps)(CustomContentChannel);