diff --git a/app/src/frontend/building/building-edit.tsx b/app/src/frontend/building/building-edit.tsx
deleted file mode 100644
index 2c4f7ab5..00000000
--- a/app/src/frontend/building/building-edit.tsx
+++ /dev/null
@@ -1,344 +0,0 @@
-import React, { Component, Fragment } from 'react';
-import { NavLink } from 'react-router-dom';
-import PropTypes from 'prop-types';
-
-import Tooltip from '../components/tooltip';
-
-const TextInput = (props) => (
-
-
-
-
-);
-
-TextInput.propTypes = {
- slug: PropTypes.string,
- cat: PropTypes.string,
- title: PropTypes.string,
- tooltip: PropTypes.string,
- value: PropTypes.string,
- max_length: PropTypes.number,
- disabled: PropTypes.bool,
- placeholder: PropTypes.string,
- handleChange: PropTypes.func
-}
-
-const LongTextInput = (props) => (
-
-
-
-
-)
-
-LongTextInput.propTypes = {
- slug: PropTypes.string,
- title: PropTypes.string,
- tooltip: PropTypes.string,
- value: PropTypes.string,
- disabled: PropTypes.bool,
- placeholder: PropTypes.string,
- handleChange: PropTypes.func
-}
-
-class MultiTextInput extends Component { // TODO: add proper types
- static propTypes = { // TODO: generate propTypes from TS
- slug: PropTypes.string,
- title: PropTypes.string,
- tooltip: PropTypes.string,
- value: PropTypes.arrayOf(PropTypes.string),
- placeholder: PropTypes.string,
- disabled: PropTypes.bool,
- handleChange: PropTypes.func,
- copy: PropTypes.bool,
- toggleCopyAttribute: PropTypes.func,
- copying: PropTypes.bool
- };
-
- constructor(props) {
- super(props);
- this.edit = this.edit.bind(this);
- this.add = this.add.bind(this);
- this.remove = this.remove.bind(this);
- this.getValues = this.getValues.bind(this);
- }
-
- getValues() {
- return (this.props.value && this.props.value.length)? this.props.value : [null];
- }
-
- edit(event) {
- const editIndex = +event.target.dataset.index;
- const editItem = event.target.value;
- const oldValues = this.getValues();
- const values = oldValues.map((item, i) => {
- return i === editIndex ? editItem : item;
- });
- this.props.handleChange(this.props.slug, values);
- }
-
- add(event) {
- event.preventDefault();
- const values = this.getValues().concat('');
- this.props.handleChange(this.props.slug, values);
- }
-
- remove(event){
- const removeIndex = +event.target.dataset.index;
- const values = this.getValues().filter((_, i) => {
- return i !== removeIndex;
- });
- this.props.handleChange(this.props.slug, values);
- }
-
- render() {
- const values = this.getValues();
- return (
-
-
- {
- values.map((item, i) => (
-
- ))
- }
- +
-
- )
- }
-}
-
-const TextListInput = (props) => (
-
-
-
- Select a source
- {
- props.options.map(option => (
- {option}
- ))
- }
-
-
-)
-
-TextListInput.propTypes = {
- slug: PropTypes.string,
- cat: PropTypes.string,
- title: PropTypes.string,
- tooltip: PropTypes.string,
- options: PropTypes.arrayOf(PropTypes.string),
- value: PropTypes.string,
- disabled: PropTypes.bool,
- handleChange: PropTypes.func
-}
-
-const NumberInput = (props) => (
-
-
-
-
-);
-
-NumberInput.propTypes = {
- slug: PropTypes.string,
- cat: PropTypes.string,
- title: PropTypes.string,
- tooltip: PropTypes.string,
- step: PropTypes.number,
- value: PropTypes.number,
- disabled: PropTypes.bool,
- handleChange: PropTypes.func
-}
-
-class YearEstimator extends Component { // TODO: add proper types
- static propTypes = { // TODO: generate propTypes from TS
- slug: PropTypes.string,
- title: PropTypes.string,
- tooltip: PropTypes.string,
- date_year: PropTypes.number,
- date_upper: PropTypes.number,
- date_lower: PropTypes.number,
- value: PropTypes.number,
- disabled: PropTypes.bool,
- handleChange: PropTypes.func,
- copy: PropTypes.bool,
- toggleCopyAttribute: PropTypes.func,
- copying: PropTypes.bool
- };
-
- constructor(props) {
- super(props);
- this.state = {
- year: props.date_year,
- upper: props.date_upper,
- lower: props.date_lower,
- decade: Math.floor(props.date_year / 10) * 10,
- century: Math.floor(props.date_year / 100) * 100
- }
- }
- // TODO add dropdown for decade, century
- // TODO roll in first/last year estimate
- // TODO handle changes internally, reporting out date_year, date_upper, date_lower
- render() {
- return (
-
- )
- }
-}
-
-const CheckboxInput = (props) => (
-
-
-
-
-
- {props.title}
- { props.tooltip? : null }
-
-
-
-)
-
-CheckboxInput.propTypes = {
- slug: PropTypes.string,
- title: PropTypes.string,
- tooltip: PropTypes.string,
- value: PropTypes.bool,
- disabled: PropTypes.bool,
- handleChange: PropTypes.func
-}
-
-const LikeButton = (props) => (
-
- {(props.value)? props.value : 0} likes
-
-
-
- I like this building and think it contributes to the city!
- { props.tooltip? : null }
-
-
-
-
- Like more buildings
-
-
-
-);
-
-LikeButton.propTypes = {
- slug: PropTypes.string,
- cat: PropTypes.string,
- title: PropTypes.string,
- tooltip: PropTypes.string,
- value: PropTypes.number,
- building_like: PropTypes.bool,
- disabled: PropTypes.bool,
- handleLike: PropTypes.func
-}
-
-const Label: React.FunctionComponent = (props) => { // TODO: remove any
- return (
-
- {props.title}
- { (props.copying && props.cat && props.slug && !props.disabled)?
-
-
- Copy
- props.toggleCopyAttribute(props.slug)}/>
-
-
: null
- }
- { props.tooltip? : null }
-
- );
-}
-
-Label.propTypes = {
- slug: PropTypes.string,
- cat: PropTypes.string,
- title: PropTypes.string,
- disabled: PropTypes.bool,
- tooltip: PropTypes.string
-}
-
-export default BuildingEdit;
diff --git a/app/src/frontend/building/building-view.tsx b/app/src/frontend/building/building-view.tsx
index ab953c75..480f5645 100644
--- a/app/src/frontend/building/building-view.tsx
+++ b/app/src/frontend/building/building-view.tsx
@@ -21,10 +21,15 @@ import LikeContainer from './data-containers/like';
* @param props
*/
const BuildingView = (props) => {
+ if (typeof(props.building) === "undefined"){
+ return
+ }
+
switch (props.cat) {
case 'location':
return {
case 'use':
return {
case 'type':
return {
case 'age':
return {
case 'size':
return {
case 'construction':
return {
case 'team':
return {
case 'sustainability':
return {
case 'greenery':
return {
case 'community':
return {
case 'planning':
return {
case 'like':
return = (props) => { // TODO: remove any
+ return (
+
+
+
+
+
+ {props.title}
+
+
+
+ );
+}
+
+DataEntry.propTypes = {
+ title: PropTypes.string,
+ slug: PropTypes.string,
+ tooltip: PropTypes.string,
+ disabled: PropTypes.bool,
+ value: PropTypes.any,
+ placeholder: PropTypes.string,
+ maxLength: PropTypes.number,
+ onChange: PropTypes.func,
+ copy: PropTypes.shape({
+ copying: PropTypes.bool,
+ copyingKey: PropTypes.func,
+ toggleCopyAttribute: PropTypes.func
+ })
+}
+
+export default DataEntry;
diff --git a/app/src/frontend/building/data-components/data-entry.tsx b/app/src/frontend/building/data-components/data-entry.tsx
index 4163619a..d4c84964 100644
--- a/app/src/frontend/building/data-components/data-entry.tsx
+++ b/app/src/frontend/building/data-components/data-entry.tsx
@@ -13,15 +13,15 @@ const DataEntry: React.FunctionComponent = (props) => { // TODO: remove any
disabled={props.disabled}
copy={props.copy}
/>
-
- {
- (props.value != null && props.value !== '')?
- (typeof(props.value) === 'boolean')?
- (props.value)? 'Yes' : 'No'
- : props.value
- : '\u00A0'
- }
-
+
);
}
@@ -32,6 +32,9 @@ DataEntry.propTypes = {
tooltip: PropTypes.string,
disabled: PropTypes.bool,
value: PropTypes.any,
+ placeholder: PropTypes.string,
+ maxLength: PropTypes.number,
+ onChange: PropTypes.func,
copy: PropTypes.shape({
copying: PropTypes.bool,
copyingKey: PropTypes.func,
diff --git a/app/src/frontend/building/data-components/data-title.tsx b/app/src/frontend/building/data-components/data-title.tsx
index 38acaced..f36022c3 100644
--- a/app/src/frontend/building/data-components/data-title.tsx
+++ b/app/src/frontend/building/data-components/data-title.tsx
@@ -19,10 +19,9 @@ DataTitle.propTypes = {
const DataTitleCopyable: React.FunctionComponent = (props) => { // TODO: remove any
return (
-
- { props.title }
+
{ props.tooltip?
: null }
- { (props.copy.copying && props.slug && !props.disabled)?
+ { (props.copy && props.copy.copying && props.slug && !props.disabled)?
Copy
@@ -34,7 +33,10 @@ const DataTitleCopyable: React.FunctionComponent = (props) => { // TODO: re
: null
}
-
+
+ { props.title }
+
+
);
}
diff --git a/app/src/frontend/building/data-components/like-data-entry.tsx b/app/src/frontend/building/data-components/like-data-entry.tsx
index b0f17fac..6316926c 100644
--- a/app/src/frontend/building/data-components/like-data-entry.tsx
+++ b/app/src/frontend/building/data-components/like-data-entry.tsx
@@ -8,29 +8,35 @@ const LikeDataEntry: React.FunctionComponent = (props) => { // TODO: remove
const data_string = JSON.stringify({like: true});
return (
-
- Number of likes
+
- Copy
+ className="icon-button like">
+ Like more
-
-
+ Number of likes
+
+
{
(props.value != null)?
(props.value === 1)?
`${props.value} person likes this building`
: `${props.value} people like this building`
- : '\u00A0'
+ : "0 people like this building so far - you could be the first!"
}
-
- {
- (props.user_building_like)?
…including you! : ''
- }
+
+
+
+ I like this building and think it contributes to the city!
+
);
}
diff --git a/app/src/frontend/building/data-components/multi-data-entry.tsx b/app/src/frontend/building/data-components/multi-data-entry.tsx
index f62e3127..c932bd7a 100644
--- a/app/src/frontend/building/data-components/multi-data-entry.tsx
+++ b/app/src/frontend/building/data-components/multi-data-entry.tsx
@@ -1,38 +1,111 @@
-import React, { Fragment } from 'react';
+import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import { sanitiseURL } from '../../helpers';
import { DataTitleCopyable } from './data-title';
-const MultiDataEntry: React.FunctionComponent = (props) => ( // TODO: remove any
-
-
-
- {
- (props.value && props.value.length)?
-
- {
- props.value.map((item, index) => {
- return {item}
- })
- }
-
- :'\u00A0'
- }
-
-
-);
+class MultiDataEntry extends Component { // TODO: add proper types
+ static propTypes = { // TODO: generate propTypes from TS
+ slug: PropTypes.string,
+ title: PropTypes.string,
+ tooltip: PropTypes.string,
+ value: PropTypes.arrayOf(PropTypes.string),
+ placeholder: PropTypes.string,
+ disabled: PropTypes.bool,
+ handleChange: PropTypes.func,
+ copy: PropTypes.bool,
+ toggleCopyAttribute: PropTypes.func,
+ copying: PropTypes.bool
+ };
-MultiDataEntry.propTypes = {
- title: PropTypes.string,
- tooltip: PropTypes.string,
- value: PropTypes.arrayOf(PropTypes.string)
+ constructor(props) {
+ super(props);
+ this.edit = this.edit.bind(this);
+ this.add = this.add.bind(this);
+ this.remove = this.remove.bind(this);
+ this.getValues = this.getValues.bind(this);
+ }
+
+ getValues() {
+ return (this.props.value && this.props.value.length)? this.props.value : [null];
+ }
+
+ edit(event) {
+ const editIndex = +event.target.dataset.index;
+ const editItem = event.target.value;
+ const oldValues = this.getValues();
+ const values = oldValues.map((item, i) => {
+ return i === editIndex ? editItem : item;
+ });
+ this.props.onChange(this.props.slug, values);
+ }
+
+ add(event) {
+ event.preventDefault();
+ const values = this.getValues().concat('');
+ this.props.onChange(this.props.slug, values);
+ }
+
+ remove(event){
+ const removeIndex = +event.target.dataset.index;
+ const values = this.getValues().filter((_, i) => {
+ return i !== removeIndex;
+ });
+ this.props.onChange(this.props.slug, values);
+ }
+
+ render() {
+ const values = this.getValues();
+ const props = this.props;
+ return
+
+ {
+ (props.mode === 'view')?
+ (props.value && props.value.length)?
+
+ {
+ props.value.map((item, index) => {
+ return
+ {item}
+
+ })
+ }
+
+ :'\u00A0'
+ : values.map((item, i) => (
+
+ ))
+ }
+ +
+
+ }
}
export default MultiDataEntry;
diff --git a/app/src/frontend/building/data-components/numeric-data-entry.tsx b/app/src/frontend/building/data-components/numeric-data-entry.tsx
new file mode 100644
index 00000000..9d6423ac
--- /dev/null
+++ b/app/src/frontend/building/data-components/numeric-data-entry.tsx
@@ -0,0 +1,51 @@
+import React, { Fragment } from 'react';
+import PropTypes from 'prop-types';
+
+import { DataTitleCopyable } from './data-title';
+
+const NumericDataEntry: React.FunctionComponent = (props) => { // TODO: remove any
+ return (
+
+
+
+
+ );
+}
+
+NumericDataEntry.propTypes = {
+ title: PropTypes.string,
+ slug: PropTypes.string,
+ tooltip: PropTypes.string,
+ disabled: PropTypes.bool,
+ value: PropTypes.any,
+ placeholder: PropTypes.string,
+ max: PropTypes.number,
+ min: PropTypes.number,
+ step: PropTypes.number,
+ onChange: PropTypes.func,
+ copy: PropTypes.shape({
+ copying: PropTypes.bool,
+ copyingKey: PropTypes.func,
+ toggleCopyAttribute: PropTypes.func
+ })
+}
+
+export default NumericDataEntry;
diff --git a/app/src/frontend/building/data-components/select-data-entry.tsx b/app/src/frontend/building/data-components/select-data-entry.tsx
new file mode 100644
index 00000000..2e70f81b
--- /dev/null
+++ b/app/src/frontend/building/data-components/select-data-entry.tsx
@@ -0,0 +1,48 @@
+import React, { Fragment } from 'react';
+import PropTypes from 'prop-types';
+
+import { DataTitleCopyable } from './data-title';
+
+const SelectDataEntry: React.FunctionComponent = (props) => { // TODO: remove any
+ return (
+
+
+
+ {props.placeholder}
+ {
+ props.options.map(option => (
+ {option}
+ ))
+ }
+
+
+ );
+}
+
+SelectDataEntry.propTypes = {
+ title: PropTypes.string,
+ slug: PropTypes.string,
+ tooltip: PropTypes.string,
+ disabled: PropTypes.bool,
+ value: PropTypes.any,
+ placeholder: PropTypes.string,
+ options: PropTypes.arrayOf(PropTypes.string),
+ onChange: PropTypes.func,
+ copy: PropTypes.shape({
+ copying: PropTypes.bool,
+ copyingKey: PropTypes.func,
+ toggleCopyAttribute: PropTypes.func
+ })
+}
+
+export default SelectDataEntry;
diff --git a/app/src/frontend/building/data-components/textbox-data-entry.tsx b/app/src/frontend/building/data-components/textbox-data-entry.tsx
new file mode 100644
index 00000000..ea906db8
--- /dev/null
+++ b/app/src/frontend/building/data-components/textbox-data-entry.tsx
@@ -0,0 +1,47 @@
+import React, { Fragment } from 'react';
+import PropTypes from 'prop-types';
+
+import { DataTitleCopyable } from './data-title';
+
+const TextboxDataEntry: React.FunctionComponent = (props) => { // TODO: remove any
+ return (
+
+
+
+
+ );
+}
+
+TextboxDataEntry.propTypes = {
+ title: PropTypes.string,
+ slug: PropTypes.string,
+ tooltip: PropTypes.string,
+ disabled: PropTypes.bool,
+ value: PropTypes.any,
+ placeholder: PropTypes.string,
+ maxLength: PropTypes.number,
+ onChange: PropTypes.func,
+ copy: PropTypes.shape({
+ copying: PropTypes.bool,
+ copyingKey: PropTypes.func,
+ toggleCopyAttribute: PropTypes.func
+ })
+}
+
+export default TextboxDataEntry;
diff --git a/app/src/frontend/building/data-components/year-data-entry.tsx b/app/src/frontend/building/data-components/year-data-entry.tsx
new file mode 100644
index 00000000..c2634708
--- /dev/null
+++ b/app/src/frontend/building/data-components/year-data-entry.tsx
@@ -0,0 +1,71 @@
+import React, { Component, Fragment } from 'react';
+import PropTypes from 'prop-types';
+
+import NumericDataEntry from './numeric-data-entry';
+
+class YearDataEntry extends Component { // TODO: add proper types
+ static propTypes = { // TODO: generate propTypes from TS
+ year: PropTypes.number,
+ upper: PropTypes.number,
+ lower: PropTypes.number,
+ mode: PropTypes.string,
+ onChange: PropTypes.func,
+ copy: PropTypes.shape({
+ copying: PropTypes.bool,
+ copyingKey: PropTypes.func,
+ toggleCopyAttribute: PropTypes.func
+ })
+ };
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ year: props.year,
+ upper: props.upper,
+ lower: props.lower,
+ decade: Math.floor(props.year / 10) * 10,
+ century: Math.floor(props.year / 100) * 100
+ }
+ }
+ // TODO add dropdown for decade, century
+ // TODO roll in first/last year estimate
+ // TODO handle changes internally, reporting out date_year, date_upper, date_lower
+ render() {
+ const props = this.props;
+ return (
+
+
+
+
+
+ )
+ }
+}
+
+export default YearDataEntry;
diff --git a/app/src/frontend/building/data-container.tsx b/app/src/frontend/building/data-container.tsx
index 407c1a58..6ef2e8e7 100644
--- a/app/src/frontend/building/data-container.tsx
+++ b/app/src/frontend/building/data-container.tsx
@@ -31,18 +31,12 @@ const withCopyEdit = (WrappedComponent) => {
constructor(props) {
super(props);
- // create object and spread into state to avoid TS complaining about modifying readonly state
- let fieldsObj = {};
- for (const field of props.fields) {
- fieldsObj[field.slug] = props[field.slug];
- }
-
this.state = {
error: this.props.error || undefined,
like: this.props.like || undefined,
copying: false,
keys_to_copy: {},
- ...fieldsObj
+ building: this.props.building
};
this.handleChange = this.handleChange.bind(this);
@@ -81,11 +75,20 @@ const withCopyEdit = (WrappedComponent) => {
})
}
+ updateBuildingState(key, value) {
+ const building = {...this.state.building};
+ building[key] = value;
+
+ this.setState({
+ building: building
+ });
+ }
+
/**
* Handle changes on typical inputs
* - e.g. input[type=text], radio, select, textare
*
- * @param {DocumentEvent} event
+ * @param {*} event
*/
handleChange(event) {
const target = event.target;
@@ -96,25 +99,21 @@ const withCopyEdit = (WrappedComponent) => {
if (name === 'location_postcode' && value !== null) {
value = value.toUpperCase();
}
- this.setState({
- [name]: value
- });
+ this.updateBuildingState(name, value);
}
/**
* Handle changes on checkboxes
* - e.g. input[type=checkbox]
*
- * @param {DocumentEvent} event
+ * @param {*} event
*/
handleCheck(event) {
const target = event.target;
const value = target.checked;
const name = target.name;
- this.setState({
- [name]: value
- });
+ this.updateBuildingState(name, value);
}
/**
@@ -124,23 +123,21 @@ const withCopyEdit = (WrappedComponent) => {
* @param {String} key
* @param {*} value
*/
- handleUpdate(key, value) {
- this.setState({
- [key]: value
- });
+ handleUpdate(key: string, value: any) {
+ this.updateBuildingState(name, value);
}
/**
* Handle likes separately
* - like/love reaction is limited to set/unset per user
*
- * @param {DocumentEvent} event
+ * @param {*} event
*/
handleLike(event) {
event.preventDefault();
const like = event.target.checked;
- fetch(`/building/${this.props.building_id}/like.json`, {
+ fetch(`/api/buildings/${this.props.building.building_id}/like.json`, {
method: 'POST',
headers:{
'Content-Type': 'application/json'
@@ -154,9 +151,7 @@ const withCopyEdit = (WrappedComponent) => {
this.setState({error: res.error})
} else {
this.props.selectBuilding(res);
- this.setState({
- likes_total: res.likes_total
- })
+ this.updateBuildingState('likes_total', res.likes_total);
}
}.bind(this)).catch(
(err) => this.setState({error: err})
@@ -167,9 +162,9 @@ const withCopyEdit = (WrappedComponent) => {
event.preventDefault();
this.setState({error: undefined})
- fetch(`/building/${this.props.building_id}.json`, {
+ fetch(`/api/buildings/${this.props.building.building_id}.json`, {
method: 'POST',
- body: JSON.stringify(this.state),
+ body: JSON.stringify(this.state.building),
headers:{
'Content-Type': 'application/json'
},
@@ -194,29 +189,29 @@ const withCopyEdit = (WrappedComponent) => {
const values_to_copy = {}
for (const key of Object.keys(this.state.keys_to_copy)) {
- values_to_copy[key] = this.state[key]
+ values_to_copy[key] = this.state.building[key]
}
const data_string = JSON.stringify(values_to_copy);
const copy = {
copying: this.state.copying,
toggleCopying: this.toggleCopying,
toggleCopyAttribute: this.toggleCopyAttribute,
- copyingKey: (key) => Object.keys(this.state.values_to_copy).includes(key)
+ copyingKey: (key) => this.state.keys_to_copy[key]
}
return this.props.building?