Include all categories and form sections

- coming soon if inactive [Closes #30]
- update section titles [Closes #52]
- add size fields [Closes #39]
This commit is contained in:
Tom Russell 2018-10-01 12:45:33 +01:00
parent f8201da283
commit 46d630711f
6 changed files with 240 additions and 212 deletions

View File

@ -1,37 +1,89 @@
import React, { Component } from 'react';
import { Link, Redirect } from 'react-router-dom';
import React, { Component, Fragment } from 'react';
import { Link, NavLink, Redirect } from 'react-router-dom';
import ErrorBox from './error-box';
import InfoBox from './info-box';
import Sidebar from './sidebar';
const CONFIG = [
{
title: "Location", slug: "location", fields: [
{ title: "Building name", slug: "location_name", type: "text" },
{ title: "Building number", slug: "location_number", type: "text" },
{ title: "Street", slug: "location_street", type: "text" },
{ title: "Address line 2", slug: "location_line_two", type: "text" },
{ title: "Town", slug: "location_town", type: "text" },
{ title: "Postcode", slug: "location_postcode", type: "text" },
]
},
{ title: "Use", slug: "use", inactive: true, fields: [] },
{ title: "Type", slug: "type", inactive: true, fields: [] },
{
title: "Age", slug: "age", fields: [
{ title: "Year built (best estimate)", slug: "date_year", type: "number", step: 1 },
{ title: "Year built (upper estimate)", slug: "date_upper", type: "number", step: 1 },
{ title: "Year built (lower estimate)", slug: "date_lower", type: "number", step: 1 },
{ title: "Facade date", slug: "date_facade", type: "number", step: 1 },
{ title: "Source", slug: "date_source", type: "text" },
]
},
{
title: "Size", slug: "size", fields: [
{ title: "Attic storeys", slug: "size_storeys_attic", type: "number", step: 1 },
{ title: "Core storeys", slug: "size_storeys_core", type: "number", step: 1 },
{ title: "Basement storeys", slug: "size_storeys_basement", type: "number", step: 1 },
{ title: "Height to apex (m)", slug: "size_height_apex", type: "number", step: 0.1 },
{ title: "Ground floor area (m²)", slug: "size_floor_area_ground", type: "number", step: 0.1 },
{ title: "Total floor area (m²)", slug: "size_floor_area_total", type: "number", step: 0.1 },
{ title: "Frontage Width (m)", slug: "size_width_frontage", type: "number", step: 0.1 },
]
},
{ title: "Shape & Position", slug: "form", inactive: true, fields: [] },
{ title: "Build Team", slug: "build-team", inactive: true, fields: [] },
{ title: "Construction & Design", slug: "construction", inactive: true, fields: [] },
{ title: "Energy", slug: "energy", inactive: true, fields: [] },
{ title: "Greenery", slug: "greenery", inactive: true, fields: [] },
{ title: "Planning & Protection", slug: "planning", inactive: true, fields: [] },
{ title: "Demolition", slug: "demolition", inactive: true, fields: [] },
{
title: "Like Me!", slug: "like", fields: [
{ title: "Like", slug: "like", type: "like" }
]
},
];
class BuildingEdit extends Component {
render() {
if (!this.props.user){
return <Redirect to="/sign-up.html" />
}
if (!this.props.building_id){
return (
<Sidebar title="Building Not Found">
<InfoBox msg="We can't find that one anywhere - try the map again?" />
<div className="buttons-container">
<Link to="/map/date_year.html" className="btn btn-secondary">Back to maps</Link>
</div>
</Sidebar>
);
}
return (
<Sidebar title={`Edit Building`}>
{
CONFIG.map((props) => {
return <EditForm {...props} {...this.props} key={props.slug} />
})
}
</Sidebar>
);
}
}
class EditForm extends Component {
constructor(props) {
super(props);
this.state = {
error: undefined,
building_id: props.building_id,
revision_id: props.revision_id,
geometry_id: props.geometry_id,
location_name: props.location_name,
location_number: props.location_number,
location_line_two: props.location_line_two,
location_street: props.location_street,
location_postcode: props.location_postcode,
date_year: props.date_year,
date_lower: props.date_lower,
date_upper: props.date_upper,
date_source: props.date_source,
facade_year: props.facade_year,
facade_upper: props.facade_upper,
facade_lower: props.facade_lower,
facade_source: props.facade_source,
size_storeys_attic: props.size_storeys_attic,
size_storeys_core: props.size_storeys_core,
size_storeys_basement: props.size_storeys_basement,
likes_total: props.likes_total,
liked: props.liked
};
this.state = {...props}
this.state.error = this.state.error || undefined;
this.handleChange = this.handleChange.bind(this);
this.handleLike = this.handleLike.bind(this);
@ -50,18 +102,9 @@ class BuildingEdit extends Component {
handleLike(event) {
const liked = event.target.checked;
const likes = this.state.likes || [];
if (liked) {
this.setState({
likes: likes.concat([this.props.user.id]),
liked: true
});
} else {
this.setState({
likes: likes.filter(id => id !== this.props.user.id),
liked: false
});
}
this.setState({
like: liked
});
}
handleSubmit(event) {
@ -81,7 +124,7 @@ class BuildingEdit extends Component {
if (res.error) {
this.setState({error: res.error})
} else {
this.props.selectBuilding(this.state); // could use server response?
this.props.selectBuilding(res);
this.props.history.push(`/building/${this.props.building_id}.html`);
}
}.bind(this)).catch(
@ -90,160 +133,88 @@ class BuildingEdit extends Component {
}
render() {
if (!this.props.user){
return <Redirect to="/sign-up.html" />
}
if (!this.props.building_id){
return (
<Sidebar title="Building Not Found">
<InfoBox msg="We can't find that one anywhere - try the map again?" />
<div className="buttons-container">
<Link to="/map/date_year.html" className="btn btn-secondary">Back to maps</Link>
</div>
</Sidebar>
);
}
const match = true;
return (
<Sidebar title={`Edit Building`}>
<form action="building-view.html" method="GET" onSubmit={this.handleSubmit}>
<section className={(this.props.inactive)? "data-section inactive": "data-section"}>
<NavLink className="bullet-prefix" to={(match)? '#': `#${this.props.slug}`}
isActive={() => match}>
<h3 className="h3">{this.props.title}</h3>
</NavLink>
<form action={`/building/${this.props.building_id}.html#${this.props.slug}`}
method="GET" onSubmit={this.handleSubmit}>
<ErrorBox msg={this.state.error} />
<fieldset className="data-section">
<legend className="h3 bullet-prefix location toggled-on">Location</legend>
<div id="data-list-location" className="data-list">
<label htmlFor="location_name">Building name</label>
<input className="form-control" type="text"
id="location_name" name="location_name"
value={this.state.location_name}
onChange={this.handleChange}
/>
<label htmlFor="location_number">Building number</label>
<input className="form-control" type="text"
id="location_number" name="location_number"
value={this.state.location_number}
onChange={this.handleChange}
/>
<label htmlFor="location_street">Street</label>
<input className="form-control" type="text"
id="location_street" name="location_street"
value={this.state.location_street}
onChange={this.handleChange}
/>
<label htmlFor="location_line_two">Address line 2</label>
<input className="form-control" type="text"
id="location_line_two" name="location_line_two"
value={this.state.location_line_two}
onChange={this.handleChange}
/>
<label htmlFor="location_town">Town</label>
<input className="form-control" type="text"
id="location_town" name="location_town"
value={this.state.location_town}
onChange={this.handleChange}
/>
<label htmlFor="location_postcode">Postcode</label>
<input className="form-control" type="text"
id="location_postcode" name="location_postcode"
value={this.state.location_postcode}
onChange={this.handleChange}
/>
</div>
</fieldset>
<fieldset className="data-section">
<legend className="h3 bullet-prefix age">Age</legend>
<div id="data-list-age" className="data-list">
<label htmlFor="date_year">Year built (best estimate)</label>
<input className="form-control" type="number" step="1"
id="date_year" name="date_year"
value={this.state.date_year}
onChange={this.handleChange}
/>
<label htmlFor="date_upper">Year built (upper estimate)</label>
<input className="form-control" type="number" step="1"
id="date_upper" name="date_upper"
value={this.state.date_upper}
onChange={this.handleChange}
/>
<label htmlFor="date_lower">Year built (lower estimate)</label>
<input className="form-control" type="number" step="1"
id="date_lower" name="date_lower"
value={this.state.date_lower}
onChange={this.handleChange}
/>
<label htmlFor="date_facade">Facade date</label>
<input className="form-control" type="number" step="1"
id="date_facade" name="date_facade"
value={this.state.date_facade}
onChange={this.handleChange}
/>
<label htmlFor="date_source">Source</label>
<input className="form-control" type="text"
id="date_source" name="date_source"
value={this.state.date_source}
onChange={this.handleChange}
/>
</div>
</fieldset>
<fieldset className="data-section">
<legend className="h3 bullet-prefix size">Size</legend>
<div id="data-list-size" className="data-list">
<label htmlFor="size_storeys_attic">Attic storeys</label>
<input className="form-control" type="number" step="1"
id="size_storeys_attic" name="size_storeys_attic"
value={this.state.size_storeys_attic}
onChange={this.handleChange}
/>
<label htmlFor="size_storeys_core">Core storeys</label>
<input className="form-control" type="number" step="1"
id="size_storeys_core" name="size_storeys_core"
value={this.state.size_storeys_core}
onChange={this.handleChange}
/>
<label htmlFor="size_storeys_basement">Basement storeys</label>
<input className="form-control" type="number" step="1"
id="size_storeys_basement" name="size_storeys_basement"
value={this.state.size_storeys_basement}
onChange={this.handleChange}
/>
</div>
</fieldset>
<fieldset className="data-section">
<legend className="h3 bullet-prefix like">Like Me!</legend>
<div id="data-list-like" className="data-list">
<label htmlFor="likes">Like this building?</label>
<div className="form-check">
<input className="form-check-input position-static"
type="checkbox"
checked={this.state.liked}
onChange={this.handleLike}
/>
<Fragment>{
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 "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
})
}</Fragment>
<Fragment>{
(this.props.inactive)?
null : (
<div className="buttons-container">
<Link to={`/building/${this.props.building_id}.html#${this.props.slug}`}
className="btn btn-secondary">Cancel</Link>
<button type="submit" className="btn btn-primary">Save</button>
</div>
</div>
</fieldset>
<div className="buttons-container">
<Link to={`/building/${this.props.building_id}.html`} className="btn btn-secondary">Cancel</Link>
<button type="submit" className="btn btn-primary">Save</button>
</div>
)
}</Fragment>
</form>
</Sidebar>
);
</section>
)
}
}
const TextInput = (props) => (
<Fragment>
<label htmlFor={props.slug}>{props.title}</label>
<input className="form-control" type="text"
id={props.slug} name={props.slug}
value={props.value || ""}
onChange={props.handleChange}
/>
</Fragment>
);
const NumberInput = (props) => (
<Fragment>
<label htmlFor={props.slug}>{props.title}</label>
<input className="form-control" type="number" step={props.step}
id={props.slug} name={props.slug}
value={props.value || ""}
onChange={props.handleChange}
/>
</Fragment>
);
const LikeButton = (props) => (
<Fragment>
<label htmlFor="likes">Like this building?</label>
<div className="form-check">
<input className="form-check-input position-static"
type="checkbox"
checked={props.value}
onChange={props.handleLike}
/>
</div>
</Fragment>
);
export default BuildingEdit;

View File

@ -1,4 +1,4 @@
import React from 'react';
import React, { Fragment } from 'react';
import { Link, NavLink } from 'react-router-dom';
import Sidebar from './sidebar';
@ -9,12 +9,20 @@ import InfoBox from './info-box';
const DataSection = function(props){
const match = props.hash && props.slug.match(props.hash);
return (
<section className="data-section">
<section className={(props.inactive)? "data-section inactive": "data-section"}>
<NavLink className="bullet-prefix" to={(match)? '#': `#${props.slug}`}
isActive={() => match}>
<h3 className="h3">{props.title}</h3>
</NavLink>
{ (match)? props.children : null }
<Fragment>{ (match)? props.children : null }</Fragment>
<Fragment>{
(match && !props.inactive)?
<div className="buttons-container with-space">
<Link to="/map/date_year.html" className="btn btn-secondary">Back to maps</Link>
<Link to={`/building/${props.building_id}/edit.html`} className="btn btn-primary">Edit data</Link>
</div>
: null
}</Fragment>
</section>
);
}
@ -24,7 +32,7 @@ const BuildingView = function(props){
return (
<Sidebar title="Building Not Found">
<InfoBox msg="We can't find that one anywhere - try the map again?" />
<div className="buttons-container">
<div className="buttons-container with-space">
<Link to="/map/date_year.html" className="btn btn-secondary">Back to maps</Link>
</div>
</Sidebar>
@ -59,6 +67,12 @@ const BuildingView = function(props){
<dd>{props.location_postcode ? props.location_postcode : '-'}</dd>
</dl>
</DataSection>
<DataSection inactive={true} title="Use" slug="use" hash={hash}>
<p className="data-intro">Coming soon&hellip;</p>
</DataSection>
<DataSection inactive={true} title="Type" slug="type" hash={hash}>
<p className="data-intro">Coming soon&hellip;</p>
</DataSection>
<DataSection title="Age" slug="age" hash={hash}>
<dl className="data-list">
<dt>Year built (best estimate)</dt>
@ -75,13 +89,44 @@ const BuildingView = function(props){
</DataSection>
<DataSection title="Size" slug="size" hash={hash}>
<dl className="data-list">
<dt>Total storeys</dt>
<dd>{(props.size_storeys_attic + props.size_storeys_basement + props.size_storeys_core)}</dd>
<dt>Attic storeys</dt>
<dd>{props.size_storeys_attic? props.size_storeys_attic : '-'}</dd>
<dt>Core storeys</dt>
<dd>{props.size_storeys_core? props.size_storeys_core : '-'}</dd>
<dt>Basement storeys</dt>
<dd>{props.size_storeys_basement? props.size_storeys_basement : '-'}</dd>
</dl>
<dl className="data-list">
<dt>Height to apex (m)</dt>
<dd>{props.size_height_apex? props.size_height_apex : '-'}</dd>
<dt>Ground floor area ()</dt>
<dd>{props.size_floor_area_ground? props.size_floor_area_ground : '-'}</dd>
<dt>Total floor area ()</dt>
<dd>{props.size_floor_area_total? props.size_floor_area_total : '-'}</dd>
<dt>Frontage Width (m)</dt>
<dd>{props.size_width_frontage? props.size_width_frontage : '-'}</dd>
</dl>
</DataSection>
<DataSection inactive={true} title="Shape &amp; Position" slug="form" hash={hash}>
<p className="data-intro">Coming soon&hellip;</p>
</DataSection>
<DataSection inactive={true} title="Build Team" slug="build-team" hash={hash}>
<p className="data-intro">Coming soon&hellip;</p>
</DataSection>
<DataSection inactive={true} title="Construction &amp; Design" slug="construction" hash={hash}>
<p className="data-intro">Coming soon&hellip;</p>
</DataSection>
<DataSection inactive={true} title="Energy" slug="energy" hash={hash}>
<p className="data-intro">Coming soon&hellip;</p>
</DataSection>
<DataSection inactive={true} title="Greenery" slug="greenery" hash={hash}>
<p className="data-intro">Coming soon&hellip;</p>
</DataSection>
<DataSection inactive={true} title="Planning &amp; Protection" slug="planning" hash={hash}>
<p className="data-intro">Coming soon&hellip;</p>
</DataSection>
<DataSection inactive={true} title="Demolition" slug="demolition" hash={hash}>
<p className="data-intro">Coming soon&hellip;</p>
</DataSection>
<DataSection title="Like Me!" slug="like" hash={hash}>
<dl className="data-list">
@ -89,10 +134,6 @@ const BuildingView = function(props){
<dd>{props.likes ? props.likes.length : 0}</dd>
</dl>
</DataSection>
<div className="buttons-container">
<Link to="/map/date_year.html" className="btn btn-secondary">Back to maps</Link>
<Link to={`/building/${props.building_id}/edit.html`} className="btn btn-primary">Edit data</Link>
</div>
</Sidebar>
);
}

View File

@ -77,7 +77,7 @@ const LegendSection = (props) => (
);
const LegendGroup = (props) => (
<div className="data-section">
<div className="data-section legend">
<NavLink className={`bullet-prefix ${ props.slug }`} to={`/map/${ props.slug }.html`}>
<h3 className="h3">{ props.label }</h3>
</NavLink>

View File

@ -14,7 +14,7 @@
.bullet-prefix {
display: block;
position: relative;
padding: 0.5rem 0.5rem 0.5rem 2.25rem;
padding: 0.6rem 0.5rem 0.5rem 2.25rem;
cursor: pointer;
text-decoration: none;
color: #222;
@ -32,9 +32,9 @@
.bullet-prefix::before {
display: inline-block;
position: absolute;
left: 0.75rem;
top: 0.25rem;
width: 1rem;
left: 0.55rem;
top: 0.3rem;
width: 1.2rem;
height: 1rem;
text-align: center;
vertical-align: middle;
@ -56,17 +56,30 @@
.data-section {
border-top: 1px solid #ffffff;
}
.data-section.inactive {
opacity: 0.7;
}
.data-section.inactive .bullet-prefix::before {
opacity: 0.4;
}
.data-section .h3 {
margin: 0;
}
.data-intro {
padding-left: 1.5rem;
padding-left: 0.75rem;
font-size: 0.8333rem;
margin-top: 0.25rem;
}
.data-list {
margin: 0;
padding-left: 0.75rem;
}
.legend .data-list {
padding-left: 2.25rem;
}
.data-section form {
padding: 0 0.75rem;
}
.data-list a {
color: #555;
}
@ -83,8 +96,7 @@
text-transform: uppercase;
color: #555;
}
.data-list dd,
.data-list input {
.data-list dd {
margin: 0 0 0.5rem;
line-height: 1.5;
}

View File

@ -6,7 +6,7 @@
bottom: 3rem;
padding: 0.25em 0em;
background: #fff;
background-color: rgba(255,255,255,0.95);
background-color: rgba(255,255,255,0.98);
z-index: 1000;
overflow-y: auto;
}
@ -16,11 +16,11 @@
margin-left: -0.1em;
padding: 0 0.75rem;
}
.leaflet-container .leaflet-control-attribution {
#root .leaflet-container .leaflet-control-attribution {
width: 100%;
height: 3rem;
background: #fff;
background: rgba(255, 255, 255, 0.95);
background: rgba(255, 255, 255, 0.7);
}
.leaflet-right{
left: 0;
@ -29,7 +29,7 @@
.info-container {
bottom: 2rem;
}
.leaflet-container .leaflet-control-attribution {
#root .leaflet-container .leaflet-control-attribution {
height: 2rem;
}
}
@ -43,7 +43,7 @@
.leaflet-right{
left: 20rem;
}
.leaflet-container .leaflet-control-attribution {
#root .leaflet-container .leaflet-control-attribution {
height: auto;
}
}

View File

@ -12,6 +12,9 @@ input[type="email"] {
input[type="number"] {
padding-right: 0.25rem;
}
form .alert {
margin: 0.5rem 0;
}
.form-check-input {
margin-top: 0.6rem;
}
@ -21,7 +24,8 @@ label {
.buttons-container {
margin-bottom: 0.75rem;
}
.info-container .buttons-container {
.buttons-container.with-space {
margin-top: 1rem;
padding: 0 0.75rem;
}
.buttons-container .btn {