colouring-montreal/app/src/frontend/building-edit.js

299 lines
11 KiB
JavaScript
Raw Normal View History

import React, { Component, Fragment } from 'react';
import { Link, NavLink, Redirect } from 'react-router-dom';
2018-09-13 12:13:03 -04:00
import ErrorBox from './error-box';
2018-09-13 15:41:42 -04:00
import InfoBox from './info-box';
2018-09-11 15:59:59 -04:00
import Sidebar from './sidebar';
2018-10-05 13:41:12 -04:00
import Tooltip from './tooltip';
2018-11-13 05:47:53 -05:00
import { CloseIcon, SaveIcon } from './icons';
import CONFIG from './fields-config.json';
2018-10-04 17:50:33 -04:00
const BuildingEdit = (props) => {
if (!props.user){
return <Redirect to="/sign-up.html" />
}
if (!props.building_id){
return (
2018-10-05 17:19:26 -04:00
<Sidebar title="Building Not Found" back="/map/age.html">
2018-10-04 17:50:33 -04:00
<InfoBox msg="We can't find that one anywhere - try the map again?" />
<div className="buttons-container">
2018-10-05 17:19:26 -04:00
<Link to="/map/age.html" className="btn btn-secondary">Back to maps</Link>
2018-10-04 17:50:33 -04:00
</div>
</Sidebar>
);
}
2018-10-04 17:50:33 -04:00
2018-11-29 17:00:53 -05:00
const cat = get_cat(props.match.url);
2018-10-04 17:50:33 -04:00
return (
2018-11-29 17:00:53 -05:00
<Sidebar
key={props.building_id}
title={`You are editing`}
back={`/edit/${cat}.html`}>
2018-10-04 17:50:33 -04:00
{
CONFIG.map((conf_props) => {
return <EditForm
{...conf_props} {...props}
2018-11-29 17:00:53 -05:00
cat={cat} key={conf_props.slug} />
2018-10-04 17:50:33 -04:00
})
}
</Sidebar>
);
}
2018-11-29 17:00:53 -05:00
function get_cat(url) {
if (url === "/") {
return "age"
}
const matches = /^\/(view|edit)\/([^\/.]+)/.exec(url);
const cat = (matches && matches.length > 2)? matches[2] : "age";
return cat;
}
class EditForm extends Component {
2018-09-11 15:59:59 -04:00
constructor(props) {
super(props);
2018-10-20 10:35:52 -04:00
this.state = {}
for (let field of props.fields) {
this.state[field.slug] = props[field.slug]
}
this.state.error = this.props.error || undefined;
this.state.like = this.props.like || undefined;
2018-09-11 15:59:59 -04:00
this.handleChange = this.handleChange.bind(this);
this.handleLike = this.handleLike.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
const target = event.target;
2018-10-25 06:57:58 -04:00
let value = (target.value === '')? null : target.value;
2018-09-11 15:59:59 -04:00
const name = target.name;
2018-10-25 06:57:58 -04:00
// special transform - consider something data driven before adding 'else if's
if (name === 'location_postcode' && value !== null) {
value = value.toUpperCase();
}
2018-09-11 15:59:59 -04:00
this.setState({
[name]: value
});
}
handleLike(event) {
event.preventDefault();
fetch(`/building/like/${this.props.building_id}`, {
method: 'POST',
headers:{
'Content-Type': 'application/json'
},
credentials: 'same-origin'
}).then(
res => res.json()
).then(function(res){
if (res.error) {
this.setState({error: res.error})
} else {
this.props.selectBuilding(res);
this.setState({
likes_total: res.likes_total
})
}
}.bind(this)).catch(
(err) => this.setState({error: err})
);
2018-09-11 15:59:59 -04:00
}
handleSubmit(event) {
event.preventDefault();
2018-09-13 12:13:03 -04:00
this.setState({error: undefined})
2018-09-30 16:54:47 -04:00
fetch(`/building/${this.props.building_id}.json`, {
2018-09-11 15:59:59 -04:00
method: 'POST',
body: JSON.stringify(this.state),
headers:{
'Content-Type': 'application/json'
},
credentials: 'same-origin'
2018-09-11 15:59:59 -04:00
}).then(
res => res.json()
).then(function(res){
if (res.error) {
2018-09-13 12:13:03 -04:00
this.setState({error: res.error})
2018-09-11 15:59:59 -04:00
} else {
this.props.selectBuilding(res);
2018-11-29 17:00:53 -05:00
const new_cat = this.props.cat;
2018-10-05 17:19:26 -04:00
this.props.history.push(`/building/${res.building_id}.html?cat=${new_cat}`);
2018-09-11 15:59:59 -04:00
}
2018-09-13 12:13:03 -04:00
}.bind(this)).catch(
(err) => this.setState({error: err})
2018-09-11 15:59:59 -04:00
);
}
render() {
2018-11-29 17:00:53 -05:00
const match = this.props.cat === this.props.slug;
2018-09-11 15:59:59 -04:00
return (
<section className={(this.props.inactive)? "data-section inactive": "data-section"}>
<header className={(match? "active " : "") + " section-header edit"}>
2018-11-29 17:00:53 -05:00
<NavLink
to={`/edit/${this.props.slug}/building/${this.props.building_id}.html`}
title={(this.props.inactive)? 'Coming soon… Click the ? for more info.' :
(match)? 'Hide details' : 'Show details'}
isActive={() => match}>
<h3 className="h3">{this.props.title}</h3>
</NavLink>
2018-10-05 04:10:20 -04:00
<nav className="icon-buttons">
2018-10-04 17:50:33 -04:00
{
this.props.help?
<a className="icon-button help" title="Find out more" href={this.props.help}>
2018-11-13 05:47:53 -05:00
More info
2018-10-04 17:50:33 -04:00
</a>
: null
}
{
2018-11-29 17:00:53 -05:00
(match && this.props.slug === 'like')? // special-case for likes
<NavLink className="icon-button save" title="Done"
2018-11-29 17:00:53 -05:00
to={`/edit/${this.props.slug}/building/${this.props.building_id}.html`}>
Done
<SaveIcon />
</NavLink>
:
2018-11-29 17:00:53 -05:00
match? (
<Fragment>
2018-10-05 17:19:26 -04:00
<NavLink className="icon-button save" title="Save Changes"
onClick={this.handleSubmit}
2018-11-29 17:00:53 -05:00
to={`/edit/${this.props.slug}/building/${this.props.building_id}.html`}>
Save
2018-10-05 17:19:26 -04:00
<SaveIcon />
</NavLink>
<NavLink className="icon-button close-edit" title="Cancel"
2018-11-29 17:00:53 -05:00
to={`/view/${this.props.slug}/building/${this.props.building_id}.html`}>
Cancel
2018-10-05 17:19:26 -04:00
<CloseIcon />
</NavLink>
</Fragment>
2018-11-29 17:00:53 -05:00
): null
}
2018-10-05 04:10:20 -04:00
</nav>
2018-10-04 17:50:33 -04:00
</header>
2018-11-29 17:00:53 -05:00
{
match? (
<form action={`/edit/${this.props.slug}/building/${this.props.building_id}.html`}
2018-10-05 17:19:26 -04:00
method="GET" onSubmit={this.handleSubmit}>
<ErrorBox msg={this.state.error} />
{
this.props.fields.map((props) => {
var el;
switch (props.type) {
case "text":
el = <TextInput {...props} handleChange={this.handleChange}
value={this.state[props.slug]} key={props.slug} />
break;
case "text_list":
el = <TextListInput {...props} handleChange={this.handleChange}
value={this.state[props.slug]} key={props.slug} />
break;
case "number":
el = <NumberInput {...props} handleChange={this.handleChange}
value={this.state[props.slug]} key={props.slug} />
break;
case "like":
el = <LikeButton {...props} handleLike={this.handleLike}
value={this.state[props.slug]} key={props.slug} />
break;
default:
el = null
break;
}
return el
})
}
<p className="alert alert-info">
Colouring may take a few seconds - try zooming the map or
hitting refresh after saving (we're working on making this
smoother).</p>
{
(this.props.slug === 'like')? // special-case for likes
<div className="buttons-container">
<Link to={`/building/${this.props.building_id}.html?cat=${this.props.slug}`}
className="btn btn-secondary">Done</Link>
</div>
:
<div className="buttons-container">
<Link to={`/building/${this.props.building_id}.html?cat=${this.props.slug}`}
className="btn btn-secondary">Cancel</Link>
<button type="submit" className="btn btn-primary">Save</button>
</div>
}
2018-10-05 17:19:26 -04:00
</form>
2018-11-29 17:00:53 -05:00
) : null
}
</section>
)
2018-09-11 15:59:59 -04:00
}
}
const TextInput = (props) => (
<Fragment>
2018-10-05 13:41:12 -04:00
<Label slug={props.slug} title={props.title} tooltip={props.tooltip} />
<input className="form-control" type="text"
id={props.slug} name={props.slug}
value={props.value || ""}
2018-10-05 17:19:26 -04:00
disabled={props.disabled}
placeholder={props.placeholder}
onChange={props.handleChange}
/>
</Fragment>
);
2018-10-05 13:41:12 -04:00
const TextListInput = (props) => (
<Fragment>
<Label slug={props.slug} title={props.title} tooltip={props.tooltip} />
<select className="form-control"
id={props.slug} name={props.slug}
value={props.value || ""}
2018-10-05 17:19:26 -04:00
disabled={props.disabled}
2018-10-05 13:41:12 -04:00
list={`${props.slug}_suggestions`}
onChange={props.handleChange}>
<option value="">Select a source</option>
{
props.options.map(option => (
2018-10-05 17:19:26 -04:00
<option key={option} value={option}>{option}</option>
2018-10-05 13:41:12 -04:00
))
}
</select>
</Fragment>
)
const NumberInput = (props) => (
<Fragment>
2018-10-05 13:41:12 -04:00
<Label slug={props.slug} title={props.title} tooltip={props.tooltip} />
<input className="form-control" type="number" step={props.step}
id={props.slug} name={props.slug}
value={props.value || ""}
2018-10-05 17:19:26 -04:00
disabled={props.disabled}
onChange={props.handleChange}
/>
</Fragment>
);
const LikeButton = (props) => (
<Fragment>
2018-10-25 08:49:02 -04:00
<p className="likes">{(props.value)? props.value : 0} likes</p>
<button className="btn btn-success btn-like" onClick={props.handleLike}>Like this building!</button>
</Fragment>
);
2018-10-05 13:41:12 -04:00
const Label = (props) => (
<label htmlFor={props.slug}>
{props.title}
{ props.tooltip? <Tooltip text={ props.tooltip } /> : null }
</label>
)
export default BuildingEdit;