diff --git a/app/src/frontend/app.js b/app/src/frontend/app.js index 884cd2fa..32a4c51c 100644 --- a/app/src/frontend/app.js +++ b/app/src/frontend/app.js @@ -10,12 +10,11 @@ import BuildingEdit from './building-edit'; import BuildingView from './building-view'; import ColouringMap from './map'; import Header from './header'; -import Legend from './legend'; +import Overview from './overview'; import Login from './login'; import MyAccountPage from './my-account'; import SignUp from './signup'; import Welcome from './welcome'; -import BuildingEditAny from './building-edit-any'; @@ -83,18 +82,26 @@ class App extends React.Component { - - - - - ( + ( + + ) } /> + ( + + ) } /> + ( ) } /> - ( + ( - ( + ( { - if (!props.user){ - return - } - return ( - - - - ); -} -export default BuildingEditAny; diff --git a/app/src/frontend/building-edit.js b/app/src/frontend/building-edit.js index b51726b6..8ae0a62b 100644 --- a/app/src/frontend/building-edit.js +++ b/app/src/frontend/building-edit.js @@ -1,6 +1,5 @@ import React, { Component, Fragment } from 'react'; import { Link, NavLink, Redirect } from 'react-router-dom'; -import queryString from 'query-string'; import ErrorBox from './error-box'; import InfoBox from './info-box'; @@ -26,23 +25,32 @@ const BuildingEdit = (props) => { ); } - const search = (props.location && props.location.search)? - queryString.parse(props.location.search): - {}; + const cat = get_cat(props.match.url); return ( - + { CONFIG.map((conf_props) => { return + cat={cat} key={conf_props.slug} /> }) } ); } +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 { constructor(props) { super(props); @@ -116,7 +124,7 @@ class EditForm extends Component { this.setState({error: res.error}) } else { this.props.selectBuilding(res); - const new_cat = this.props.search.cat; + const new_cat = this.props.cat; this.props.history.push(`/building/${res.building_id}.html?cat=${new_cat}`); } }.bind(this)).catch( @@ -125,14 +133,17 @@ class EditForm extends Component { } render() { - const match = this.props.search.cat === this.props.slug; - if (!match) { - return null - } + const match = this.props.cat === this.props.slug; return ( - {this.props.title} + match}> + {this.props.title} + { this.props.help? @@ -142,31 +153,34 @@ class EditForm extends Component { : null } { - (this.props.slug === 'like')? // special-case for likes + (match && this.props.slug === 'like')? // special-case for likes + to={`/edit/${this.props.slug}/building/${this.props.building_id}.html`}> Done : + match? ( + to={`/edit/${this.props.slug}/building/${this.props.building_id}.html`}> Save + to={`/view/${this.props.slug}/building/${this.props.building_id}.html`}> Cancel + ): null } - - { @@ -216,6 +230,8 @@ class EditForm extends Component { } + ) : null + } ) } diff --git a/app/src/frontend/building-view.js b/app/src/frontend/building-view.js index 5a8a0bb5..d3b3c8a3 100644 --- a/app/src/frontend/building-view.js +++ b/app/src/frontend/building-view.js @@ -1,6 +1,5 @@ import React, { Fragment } from 'react'; import { Link, NavLink } from 'react-router-dom'; -import queryString from 'query-string'; import Sidebar from './sidebar'; import Tooltip from './tooltip'; @@ -16,18 +15,18 @@ const BuildingView = (props) => { - Back to maps + Back to maps ); } - const search = (props.location && props.location.search)? queryString.parse(props.location.search): {}; + const cat = get_cat(props.match.url); return ( - + { CONFIG.map(section_props => ( { @@ -54,13 +53,23 @@ const BuildingView = (props) => { } +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; +} + + const DataSection = (props) => { - const match = props.search.cat === props.slug; + const match = props.cat === props.slug; return ( match}> @@ -77,7 +86,7 @@ const DataSection = (props) => { { !props.inactive? + to={`/edit/${props.slug}/building/${props.building_id}.html`}> Edit diff --git a/app/src/frontend/header.js b/app/src/frontend/header.js index 664bd633..e262b012 100644 --- a/app/src/frontend/header.js +++ b/app/src/frontend/header.js @@ -19,10 +19,10 @@ const Header = (props) => ( More about - View Maps + View Maps - Add/Edit Data + Add/Edit Data Building Categories diff --git a/app/src/frontend/legend.js b/app/src/frontend/legend.js index 7991f4d8..d622faab 100644 --- a/app/src/frontend/legend.js +++ b/app/src/frontend/legend.js @@ -1,163 +1,73 @@ -import React, { Fragment } from 'react'; -import { NavLink } from 'react-router-dom'; +import React from 'react'; -import Sidebar from './sidebar'; -import { EditIcon } from './icons'; import './legend.css'; -import CONFIG from './fields-config.json'; -import InfoBox from './info-box'; - const LEGEND_CONFIG = { location: [ - { - title: 'Location Information (number of data entries)', - slug: 'location', - elements: [ - { color: '#f0f9e8', text: '>5' }, - { color: '#bae4bc', text: '4' }, - { color: '#7bccc4', text: '3' }, - { color: '#43a2ca', text: '2' }, - { color: '#0868ac', text: '1' } - ] - } + { color: '#f0f9e8', text: '>5' }, + { color: '#bae4bc', text: '4' }, + { color: '#7bccc4', text: '3' }, + { color: '#43a2ca', text: '2' }, + { color: '#0868ac', text: '1' } ], age: [ - { - title: 'Year Built', - slug: 'date_year', - elements: [ - { color: '#f0eaba', text: '≥2000' }, - { color: '#fae269', text: '1980–2000' }, - { color: '#fbaf27', text: '1960–1980' }, - { color: '#e6711d', text: '1940–1960' }, - { color: '#d73d3a', text: '1920–1940' }, - { color: '#ba221c', text: '1900–1920' }, - { color: '#bb859b', text: '1880–1900' }, - { color: '#8b3654', text: '1860–1880' }, - { color: '#8f5385', text: '1840–1860' }, - { color: '#56619b', text: '1820–1840' }, - { color: '#6793b2', text: '1800–1820' }, - { color: '#83c3b3', text: '1780–1800' }, - { color: '#adc88f', text: '1760–1780' }, - { color: '#83a663', text: '1740–1760' }, - { color: '#77852d', text: '1720–1740' }, - { color: '#69814e', text: '1700–1720' }, - { color: '#d0c291', text: '1680–1700' }, - { color: '#918158', text: '1660–1680' }, - { color: '#7a5732', text: '<1660' }, - ] - } + { color: '#f0eaba', text: '≥2000' }, + { color: '#fae269', text: '1980–2000' }, + { color: '#fbaf27', text: '1960–1980' }, + { color: '#e6711d', text: '1940–1960' }, + { color: '#d73d3a', text: '1920–1940' }, + { color: '#ba221c', text: '1900–1920' }, + { color: '#bb859b', text: '1880–1900' }, + { color: '#8b3654', text: '1860–1880' }, + { color: '#8f5385', text: '1840–1860' }, + { color: '#56619b', text: '1820–1840' }, + { color: '#6793b2', text: '1800–1820' }, + { color: '#83c3b3', text: '1780–1800' }, + { color: '#adc88f', text: '1760–1780' }, + { color: '#83a663', text: '1740–1760' }, + { color: '#77852d', text: '1720–1740' }, + { color: '#69814e', text: '1700–1720' }, + { color: '#d0c291', text: '1680–1700' }, + { color: '#918158', text: '1660–1680' }, + { color: '#7a5732', text: '<1660' }, ], size: [ - { - title: 'Number of storeys', - slug: 'size_storeys', - elements: [ - { color: '#ffffcc', text: '≥20' }, - { color: '#ffeda0', text: '15-20' }, - { color: '#fed976', text: '10–15' }, - { color: '#feb24c', text: '6–10' }, - { color: '#fd8d3c', text: '5' }, - { color: '#fc4e2a', text: '4' }, - { color: '#e31a1c', text: '3' }, - { color: '#bd0026', text: '2' }, - { color: '#800026', text: '1' }, - ] - } + { color: '#ffffcc', text: '≥20' }, + { color: '#ffeda0', text: '15-20' }, + { color: '#fed976', text: '10–15' }, + { color: '#feb24c', text: '6–10' }, + { color: '#fd8d3c', text: '5' }, + { color: '#fc4e2a', text: '4' }, + { color: '#e31a1c', text: '3' }, + { color: '#bd0026', text: '2' }, + { color: '#800026', text: '1' }, ], like: [ - { - title: 'Which buildings do you like?', - slug: 'like', - elements: [ - { color: '#f65400', text: 'We like these buildings 👍 🎉 +1' }, - ] - } + { color: '#f65400', text: 'We like these buildings 👍 🎉 +1' }, ] }; - const Legend = (props) => { - var data_layer = undefined; - if (props.match && props.match.params && props.match.params.map) { - data_layer = props.match.params.map; - } - + let elements = LEGEND_CONFIG[props.slug]; return ( - - - { - CONFIG.map((data_group) => ( - - )) - } - + + + { props.title } + + + + { + elements.map((data_item) => ( + + )) + } + + + ); } -const LegendGroup = (props) => { - const match = props.data_layer === props.slug; - const inactive = props.inactive || !props.maps; - return ( - - - match} - title={(inactive)? 'Coming soon… Click the ? for more info.' : - (match)? '' : 'Show on map'}> - {props.title} - - - { - props.help? - - More info - - : null - } - - Edit - - - - - - { - (match && props.maps)? - props.maps.map((data_section) => ( - - { - data_section.elements.map((data_item) => ( - - )) - } - - )) - : null - } - - - ) -}; - -const LegendSection = (props) => ( - - - { props.title } - - - - { props.children } - - - -); - const LegendItem = (props) => ( - diff --git a/app/src/frontend/map.js b/app/src/frontend/map.js index b08095c6..f5a968ba 100644 --- a/app/src/frontend/map.js +++ b/app/src/frontend/map.js @@ -1,6 +1,5 @@ import React, { Component, Fragment } from 'react'; import { Map, TileLayer, ZoomControl, AttributionControl } from 'react-leaflet-universal'; -import queryString from 'query-string'; import '../../node_modules/leaflet/dist/leaflet.css' import './map.css' @@ -26,14 +25,12 @@ class ColouringMap extends Component { } handleClick(e) { - if (this.props.match.url.match('edit')){ - // don't navigate away from edit view - return - } - var lat = e.latlng.lat - var lng = e.latlng.lng - const is_building = /building/.test(this.props.match.url); - const new_cat = get_cat(is_building, this.props.location, this.props.match.url); + const is_edit = this.props.match.url.match('edit') + const mode = is_edit? 'edit': 'view'; + const lat = e.latlng.lat + const lng = e.latlng.lng + const new_cat = get_cat(this.props.match.url); + const map_cat = new_cat || 'age'; fetch( '/buildings/locate?lat='+lat+'&lng='+lng ).then( @@ -42,12 +39,11 @@ class ColouringMap extends Component { if (data && data.length){ const building = data[0]; this.props.selectBuilding(building); - this.props.history.push(`/building/${building.building_id}.html`); + this.props.history.push(`/${mode}/${map_cat}/building/${building.building_id}.html`); } else { // deselect but keep/return to expected colour theme this.props.selectBuilding(undefined); - const map_cat = new_cat || 'age'; - this.props.history.push(`/map/${map_cat}.html`); + this.props.history.push(`/${mode}/${map_cat}.html`); } }.bind(this)).catch( (err) => console.error(err) @@ -72,7 +68,7 @@ class ColouringMap extends Component { // colour-data tiles const is_building = /building/.test(this.props.match.url); - const cat = get_cat(is_building, this.props.location, this.props.match.url); + const cat = get_cat(this.props.match.url); const tileset_by_cat = { age: 'date_year', size: 'size_storeys', @@ -122,18 +118,12 @@ class ColouringMap extends Component { } }; -function get_cat(is_building, location, url) { +function get_cat(url) { if (url === "/") { return "age" } - const search = (location && location.search)? queryString.parse(location.search) : {}; - var cat, matches; - if (is_building) { - cat = search.cat; - } else { - matches = /\/map\/([^.]+).html/.exec(url); - cat = (matches && matches.length > 1)? matches[1] : ""; - } + const matches = /^\/(view|edit)\/([^\/.]+)/.exec(url); + const cat = (matches && matches.length > 2)? matches[2] : "age"; return cat; } diff --git a/app/src/frontend/overview.js b/app/src/frontend/overview.js new file mode 100644 index 00000000..5d56274f --- /dev/null +++ b/app/src/frontend/overview.js @@ -0,0 +1,68 @@ +import React from 'react'; +import { NavLink, Redirect } from 'react-router-dom'; + +import Sidebar from './sidebar'; +import { EditIcon } from './icons'; +import CONFIG from './fields-config.json'; + +const Overview = (props) => { + var data_layer = 'age'; // always default + if (props.match && props.match.params && props.match.params.cat) { + data_layer = props.match.params.cat; + } + + if (props.mode === 'edit' && !props.user){ + return + } + + let title = (props.mode === 'view')? 'View maps' : 'Add or edit data'; + + return ( + + { + CONFIG.map((data_group) => ( + + )) + } + + ); +} + +const OverviewSection = (props) => { + const match = props.data_layer === props.slug; + const inactive = props.inactive; + return ( + + + match} + title={(inactive)? 'Coming soon… Click the ? for more info.' : + (match)? '' : 'Show on map'}> + {props.title} + + + { + props.help? + + More info + + : null + } + { + props.mode === 'view'? + + Edit + + + : null + } + + + + ) +}; + +export default Overview; diff --git a/app/src/parse.js b/app/src/parse.js index b4c79479..04cc0efc 100644 --- a/app/src/parse.js +++ b/app/src/parse.js @@ -15,7 +15,7 @@ function strictParseInt(value) { function parseBuildingURL(url){ - const re = /^\/building\/([^\/]+)(\/edit)?.html/; + const re = /\/building\/([^\/]+).html/; const matches = re.exec(url); if (matches && matches.length >= 2) {