From fa785a726c0888a77bce761f1f37bf648e1cdc6b Mon Sep 17 00:00:00 2001
From: Tom Russell <tomalrussell@gmail.com>
Date: Wed, 2 Oct 2019 22:13:34 +0100
Subject: [PATCH 1/4] Add JSON.parse error-handling when parsing from URL

---
 app/src/frontend/building/multi-edit.tsx | 15 ++++++++++++++-
 app/src/frontend/map-app.tsx             | 12 +++++++++---
 2 files changed, 23 insertions(+), 4 deletions(-)

diff --git a/app/src/frontend/building/multi-edit.tsx b/app/src/frontend/building/multi-edit.tsx
index a408153b..81617bae 100644
--- a/app/src/frontend/building/multi-edit.tsx
+++ b/app/src/frontend/building/multi-edit.tsx
@@ -34,7 +34,20 @@ const MultiEdit = (props) => {
     }
 
     const q = parse(props.location.search);
-    const data = JSON.parse(q.data as string) // TODO: verify what happens when data is string[]
+
+    let data: object;
+    if (cat === 'like'){
+        data = { like: true }
+    } else {
+        try {
+            // TODO: verify what happens if data is string[]
+            data = JSON.parse(q.data as string);
+        } catch (error) {
+            console.error(error, q)
+            data = {}
+        }
+    }
+
     const title = sectionTitleFromCat(cat);
     return (
         <Sidebar>
diff --git a/app/src/frontend/map-app.tsx b/app/src/frontend/map-app.tsx
index 9f77ee99..5191f3fe 100644
--- a/app/src/frontend/map-app.tsx
+++ b/app/src/frontend/map-app.tsx
@@ -142,11 +142,17 @@ class MapApp extends React.Component<MapAppProps, MapAppState> {
     colourBuilding(building) {
         const cat = this.props.match.params.category;
         const q = parse(window.location.search);
-        const data = (cat === 'like') ? { like: true } : JSON.parse(q.data as string); // TODO: verify what happens if data is string[]
+
         if (cat === 'like') {
             this.likeBuilding(building.building_id)
         } else {
-            this.updateBuilding(building.building_id, data)
+            try {
+                // TODO: verify what happens if data is string[]
+                const data = JSON.parse(q.data as string);
+                this.updateBuilding(building.building_id, data)
+            } catch (error) {
+                console.error(error, q)
+            }
         }
     }
 
@@ -245,4 +251,4 @@ class MapApp extends React.Component<MapAppProps, MapAppState> {
     }
 }
 
-export default MapApp;
\ No newline at end of file
+export default MapApp;

From 7899588f874104ecb458fd0af0ca687cee598d11 Mon Sep 17 00:00:00 2001
From: Tom Russell <tomalrussell@gmail.com>
Date: Wed, 2 Oct 2019 22:17:37 +0100
Subject: [PATCH 2/4] Fix multi-edit shouldn't deselect building

If a user is in multi-edit mode and clicks where there is no building,
this should be a no-op.

Previous behaviour would clear the building and incidentally the
URL query string, losing selected attributes to copy, and triggering
URL-parsing error (caught as of fa785a7).
---
 app/src/frontend/map/map.tsx | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/app/src/frontend/map/map.tsx b/app/src/frontend/map/map.tsx
index 15f80cfc..ecb64c1a 100644
--- a/app/src/frontend/map/map.tsx
+++ b/app/src/frontend/map/map.tsx
@@ -83,8 +83,11 @@ class ColouringMap extends Component<ColouringMapProps, ColouringMapState> { //
                     this.props.selectBuilding(undefined);
                 }
             } else {
-                // deselect but keep/return to expected colour theme
-                this.props.selectBuilding(undefined);
+                if (mode !== 'multi-edit') {
+                    // deselect but keep/return to expected colour theme
+                    // except if in multi-edit (never select building, only colour on click)
+                    this.props.selectBuilding(undefined);
+                }
             }
         }.bind(this)).catch(
             (err) => console.error(err)
@@ -106,7 +109,7 @@ class ColouringMap extends Component<ColouringMapProps, ColouringMapState> { //
         const layer = (this.state.theme === 'light')? 'Light 3857' : 'Night 3857';
         const baseUrl = `https://api2.ordnancesurvey.co.uk/mapping_api/v1/service/zxy/${tilematrixSet}/${layer}/{z}/{x}/{y}.png?key=${key}`;
         const attribution = 'Building attribute data is © Colouring London contributors. Maps contain OS data © Crown copyright: OS Maps baselayers and building outlines.';
-        const baseLayer = <TileLayer 
+        const baseLayer = <TileLayer
             url={baseUrl}
             attribution={attribution}
         />;

From 173d5128ef307e70324e7d9c67dcb40eac44e551 Mon Sep 17 00:00:00 2001
From: Tom Russell <tomalrussell@gmail.com>
Date: Wed, 2 Oct 2019 22:33:42 +0100
Subject: [PATCH 3/4] Update multi-edit style

Drop unused code paths which relied on shared
config data structure.

Present simplified key/value data entries

Use category colour in header
---
 app/src/frontend/building/multi-edit.tsx | 94 ++++++------------------
 1 file changed, 22 insertions(+), 72 deletions(-)

diff --git a/app/src/frontend/building/multi-edit.tsx b/app/src/frontend/building/multi-edit.tsx
index 81617bae..28b675f6 100644
--- a/app/src/frontend/building/multi-edit.tsx
+++ b/app/src/frontend/building/multi-edit.tsx
@@ -5,9 +5,9 @@ import PropTypes from 'prop-types';
 
 import Sidebar from './sidebar';
 import InfoBox from '../components/info-box';
-import { sanitiseURL } from '../helpers';
+import { BackIcon }from '../components/icons';
+import DataEntry from './data-components/data-entry';
 
-const CONFIG = [];
 
 const MultiEdit = (props) => {
     if (!props.user){
@@ -19,8 +19,8 @@ const MultiEdit = (props) => {
         return (
             <Sidebar>
                 <section className='data-section'>
-                    <header className={`section-header view ${cat} active`}>
-                        <a><h3 className="h3">Like me!</h3></a>
+                    <header className={`section-header view ${cat} background-${cat}`}>
+                        <h2 className="h2">Like me!</h2>
                     </header>
                     <form className='buttons-container'>
                         <InfoBox msg='Click all the buildings that you like and think contribute to the city!' />
@@ -48,24 +48,33 @@ const MultiEdit = (props) => {
         }
     }
 
-    const title = sectionTitleFromCat(cat);
     return (
         <Sidebar>
             <section className='data-section'>
-                <header className={`section-header view ${cat} active`}>
-                    <a><h3 className="h3">{title}</h3></a>
+                <header className={`section-header view ${cat} background-${cat}`}>
+                    <Link
+                        className="icon-button back"
+                        to={`/edit/${cat}`}>
+                        <BackIcon />
+                    </Link>
+                    <h2 className="h2">Copy {cat} data</h2>
                 </header>
-                <Fragment>
+                <form>
+                    <InfoBox msg='Click buildings one at a time to colour using the data below' />
                 {
                     Object.keys(data).map((key => {
-                        const label = fieldTitleFromSlug(key);
-                        return <DataEntry key={key} label={label} value={data[key]}/>
+                        return (
+                        <DataEntry
+                            title={key}
+                            slug={key}
+                            disabled={true}
+                            value={data[key]}
+                            />
+                        )
                     }))
                 }
-                </Fragment>
+                </form>
                 <form className='buttons-container'>
-                    <InfoBox msg='Click buildings to colour using the data above' />
-
                     <Link to={`/view/${cat}`} className='btn btn-secondary'>Back to view</Link>
                     <Link to={`/edit/${cat}`} className='btn btn-secondary'>Back to edit</Link>
                 </form>
@@ -80,63 +89,4 @@ MultiEdit.propTypes = {
     location: PropTypes.object
 }
 
-const DataEntry = (props) => {
-    let content;
-
-    if (props.value != null && props.value !== '') {
-        if (typeof(props.value) === 'boolean') {
-            content = (props.value)? 'Yes' : 'No'
-        } else if (Array.isArray(props.value)) {
-            if (props.value.length) {
-                content = <ul>{
-                    props.value.map((item, index) => {
-                        return <li key={index}><a href={sanitiseURL(item)}>{item}</a></li>
-                    })
-                }</ul>
-            } else {
-                content = '\u00A0'
-            }
-        } else {
-            content = props.value
-        }
-    } else {
-        content = '\u00A0'
-    }
-
-    return (
-        <Fragment>
-            <dt>{props.label}</dt>
-            <dd>{content}</dd>
-        </Fragment>
-    )
-}
-
-function sectionTitleFromCat(cat) {
-    for (let index = 0; index < CONFIG.length; index++) {
-        const section = CONFIG[index];
-        if (section.slug === cat) {
-            return section.title
-        }
-    }
-    return undefined
-}
-
-function fieldTitleFromSlug(slug) {
-    const fields = CONFIG.reduce(
-        (prev, section) => {
-            const el = prev.concat(
-                section.fields.filter(
-                    (field: any) => field.slug === slug // TODO: remove any
-                )
-            )
-            return el
-        }, []
-    )
-    if (fields.length === 1 && fields[0].title) {
-        return fields[0].title
-    } else {
-        console.error('Expected single match, got', fields)
-    }
-}
-
 export default MultiEdit;

From 860bb0b237ca37a1e64a2ee5e11fe96278005f38 Mon Sep 17 00:00:00 2001
From: Tom Russell <tomalrussell@gmail.com>
Date: Wed, 2 Oct 2019 22:35:57 +0100
Subject: [PATCH 4/4] Enable copying dates

---
 app/src/frontend/building/data-containers/age.tsx | 1 +
 1 file changed, 1 insertion(+)

diff --git a/app/src/frontend/building/data-containers/age.tsx b/app/src/frontend/building/data-containers/age.tsx
index 7befa7a0..e7baa880 100644
--- a/app/src/frontend/building/data-containers/age.tsx
+++ b/app/src/frontend/building/data-containers/age.tsx
@@ -17,6 +17,7 @@ const AgeView = (props) => (
             upper={props.building.date_upper}
             lower={props.building.date_lower}
             mode={props.mode}
+            copy={props.copy}
             onChange={props.onChange}
             />
         <NumericDataEntry