react-hackathon-board
Version:
Hackathon board with React/node/mongo
396 lines (367 loc) • 11.9 kB
JavaScript
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import { createStore, combineReducers } from 'redux';
import { Provider } from 'react-redux';
import { modelReducer, formReducer, Field, Form, actions} from 'react-redux-form';
import { actions as hackActions } from '../../redux/modules/hack';
import classes from './HacksView.scss';
import ReactDOM from 'react-dom';
import {Button, Card, Content, Header, Column, Image, Reveal, Segment, Icon, Label} from 'react-semantify';
import DropzoneSingleImageComponent from './DropzoneComponent';
import ReactMarkdown from 'react-markdown';
import moment from 'moment';
type
Props = {
hack: Object,
hackathons: Object,
fetchFromServer: Function,
fetchHackathonsFromServer: Function,
updateToSever: Function,
reset: Function
};
/* I prefer to extend React.Component
But creating Classes for nested elements to use both
Differences are minor between the two, but we might want to stick with React.Component going forward
*/
var TitleInput = React.createClass ({
getInitialState: function() {
return { value: this.props.hack.title || '' };
},
handleChange: function(event) {
this.props.hack.title = event.target.value;
this.setState({value: event.target.value});
},
render: function() {
return (
<div className="field">
<label>Title</label>
<input type="text" name="title" value={ this.state.value }
onChange={this.handleChange}>
</input>
</div>
);
}
});
var ShortDescriptionInput = React.createClass ({
getInitialState: function() {
return {value: this.props.hack.shortDescription || '' };
},
handleChange: function(event) {
this.props.hack.shortDescription = event.target.value;
this.setState({value: event.target.value});
},
render: function() {
return (
<div className="field">
<label>Short Description</label>
<textarea value={ this.state.value } name="shortDescription" rows="2"
onChange={this.handleChange}>
</textarea>
</div>
);
}
});
var DescriptionInput = React.createClass ({
getInitialState: function() {
return {value: this.props.hack.description || '' };
},
handleChange: function(event) {
this.props.hack.description = event.target.value;
this.setState({value: event.target.value});
},
render: function() {
return (
<div className="fields">
<div className="eight wide field">
<label>Description (markdown)</label>
<textarea value={ this.state.value } name="description"
onChange={this.handleChange}>
</textarea>
</div>
<div className="eight wide field">
<label>Description Preview</label>
<ReactMarkdown source={this.props.hack.description || ''}/>
</div>
</div>
);
}
});
var LocationInput = React.createClass ({
getInitialState: function() {
return {value: this.props.hack.location || '' };
},
handleChange: function(event) {
this.props.hack.location = event.target.value;
this.setState({value: event.target.value});
},
render: function() {
return (
<div className="field">
<label>Location</label>
<input type="text" name="location" value={ this.state.value }
onChange={this.handleChange}>
</input>
</div>
);
}
});
var OpenInput = React.createClass ({
getInitialState: function() {
return {value: this.props.hack.open || false };
},
handleChange: function(event) {
this.props.hack.open = !this.props.hack.open;
this.setState({ value: this.props.hack.open });
},
render: function() {
return (
<div className="field">
<div className="ui checkbox">
<input type="checkbox" checked={this.state.value}
onChange={this.handleChange}/>
<label>
Open
</label>
</div>
<span className="ui" data-position="top left" data-tooltip="Check this box if the hack is open to other participants" data-inverted="">
<i className="help icon"></i>
</span>
</div>
);
}
});
var CompletedInput = React.createClass ({
getInitialState: function() {
return {value: this.props.hack.completed || false };
},
handleChange: function(event) {
this.props.hack.completed = !this.props.hack.completed;
this.setState({ value: this.props.hack.completed });
},
render: function() {
return (
<div className="field">
<div className="ui checkbox">
<input type="checkbox" checked={this.state.value}
onChange={this.handleChange}/>
<label>Completed</label>
</div>
<span className="ui" data-position="top left" data-tooltip="Check this box once the hack is completed" data-inverted="">
<i className="help icon"></i>
</span>
</div>
);
}
});
var ScienceInput = React.createClass ({
getInitialState: function() {
return {value: this.props.hack.science || false };
},
handleChange: function(event) {
this.props.hack.science = !this.props.hack.science;
this.setState({ value: this.props.hack.science });
},
render: function() {
return (
<div className="field">
<div className="ui checkbox">
<input type="checkbox" checked={this.state.value}
onChange={this.handleChange}/>
<label>Science</label>
</div>
<span className="ui" data-position="top left" data-tooltip="Check this box if this is science project" data-inverted="">
<i className="help icon"></i>
</span>
</div>
);
}
});
var HackathonInput = React.createClass ({
getInitialState: function() {
if (this.props.hack.hackathon) {
return { value: this.props.hack.hackathon._id };
} else {
if(this.props.selectedHackathon._id && this.props.selectedHackathon.open === true) {
this.props.hack.hackathon = this.props.selectedHackathon._id;
return {value: this.props.hack.hackathon}
} else {
for (var hackathon in this.props.hackathons) {
if(hackathon.active === true && hackathon.open === true){
this.props.hack.hackathon = hackathon._id;
return { value: hackathon._id};
}
}
return { value: -1};
}
}
},
handleChange: function(event) {
this.setState({ value: event.target.value });
this.props.hack.hackathon = event.target.value;
},
render: function() {
if (!this.props.hackathons || !this.props.hackathons.hackathons) {
return (
<option key="-1" value="-1">Loading...</option>
);
}
var hackathons = this.props.hackathons.hackathons.map(function (hackathon) {
var date = (moment(hackathon.startDate).month() !== moment(hackathon.endDate).month() ?
moment(hackathon.startDate).format('Do MMM YY') :
moment(hackathon.startDate).format('Do'))
+ " - "
+ moment(hackathon.endDate).format('Do MMM YY');
if(hackathon.open === true) {
return (
<option key={hackathon._id} value={hackathon._id}>
{hackathon.title} { date }
</option>
);
} else {
return(
<option key={hackathon._id} value={hackathon._id} disabled>
{hackathon.title} { date }
</option>
);
}
});
return (
<div className="field">
<select className="ui dropdown"
value={this.props.hack.hackathon}
onChange={this.handleChange}>
{hackathons}
</select>
</div>
);
}
});
export class HackViewComponent extends React.Component {
static propTypes = {
hack: PropTypes.object,
hackathons: PropTypes.object.isRequired,
fetchFromServer: PropTypes.func.isRequired,
fetchHackathonsFromServer: PropTypes.func.isRequired,
updateToSever: PropTypes.func.isRequired,
reset: PropTypes.func.isRequired
};
componentWillMount() {
if (this.props.params.id) {
this.props.fetchFromServer(this.props.params.id);
} else {
this.props.reset();
}
this.props.fetchHackathonsFromServer();
}
componentDidMount() {
$('.ui.form')
.form({
fields: {
title: {
identifier: 'title',
rules: [
{
type : 'empty',
prompt : 'Please enter a title'
}
]
},
location: {
identifier: 'location',
rules: [
{
type : 'empty',
prompt : 'Please enter a location'
}
]
},
shortDescription: {
identifier: 'shortDescription',
rules: [
{
type : 'empty',
prompt : 'Please enter a short description'
}
]
},
description: {
identifier: 'description',
rules: [
{
type : 'empty',
prompt : 'Please enter a description'
}
]
}
}
});
}
componentWillUnmount () {
this.props.reset();
}
handleSubmit(val) {
if(!val.hackathon && this.props.selectedHackathon._id && this.props.selectedHackathon.open === true) {
val.hackathon = this.props.selectedHackathon._id;
}
if (!val.title || !val.hackathon) {
return;
}
this.props.updateToSever(this.props.hack.hack ? this.props.hack.hack._id : null, val);
// TODO - We should use react-router's history
//this.props.history.push('#/hacks'); // deprecated?
window.location = '#/hacks';
}
render() {
if(!this.props.hack) {
return (
<div className="ui basic segment loading-height">
<div className="ui active inverted dimmer row">
<div className="ui medium inverted text loader">Loading</div>
</div>
<p></p>
<p></p>
</div>
);
}
if (!this.props.hack.hack) {
this.props.hack.hack = new Object();
}
var hack = this.props.hack.hack;
return (
// The key is important for the component to be reset properly
<Segment key={ hack._id }>
<div className="ui form">
<h1>{ hack.title }</h1>
<div className="fields">
<div className="eight wide field">
<TitleInput hack={ this.props.hack.hack } />
</div>
<div className="eight wide field">
<LocationInput hack={ this.props.hack.hack } />
</div>
</div>
<ShortDescriptionInput hack={ this.props.hack.hack } />
<DescriptionInput hack={ this.props.hack.hack } />
<OpenInput hack={ this.props.hack.hack } />
<ScienceInput hack={ this.props.hack.hack } />
<CompletedInput hack={ this.props.hack.hack } />
<HackathonInput hack={ this.props.hack.hack } hackathons= {this.props.hackathons} selectedHackathon={this.props.selectedHackathon}/>
<DropzoneSingleImageComponent hack={ this.props.hack.hack } />
<p>
<button className="ui submit tiny teal button"
onClick={(hack) => this.handleSubmit(this.props.hack.hack)}>
Save
</button>
</p>
<div className="ui error message"></div>
</div>
</Segment>
);
}
}
const mapStateToProps = (state) => ({
hack: state.hack,
hackathons: state.hackathons,
selectedHackathon: state.selectedHackathon
});
export default connect(mapStateToProps, hackActions)(HackViewComponent);