import React, { Component, Fragment } from 'react'; import { Link, NavLink, Redirect } from 'react-router-dom'; import PropTypes from 'prop-types'; import ErrorBox from './error-box'; import InfoBox from './info-box'; import Sidebar from './sidebar'; import Tooltip from './tooltip'; import { SaveIcon } from './icons'; import CONFIG from './fields-config.json'; const BuildingEdit = (props) => { if (!props.user){ return } const cat = props.match.params.cat; if (!props.building_id){ return (
Back to maps
); } return ( { CONFIG.map((section) => { return }) } ); } BuildingEdit.propTypes = { user: PropTypes.object, match: PropTypes.object, building_id: PropTypes.number } class EditForm extends Component { constructor(props) { super(props); this.state = { error: this.props.error || undefined, like: this.props.like || undefined, copying: false, keys_to_copy: {} } for (const field of props.fields) { this.state[field.slug] = props[field.slug] } this.handleChange = this.handleChange.bind(this); this.handleCheck = this.handleCheck.bind(this); this.handleLike = this.handleLike.bind(this); this.handleSubmit = this.handleSubmit.bind(this); this.handleUpdate = this.handleUpdate.bind(this); this.toggleCopying = this.toggleCopying.bind(this); this.toggleCopyAttribute = this.toggleCopyAttribute.bind(this); } /** * Enter or exit "copying" state - allow user to select attributes to copy */ toggleCopying() { this.setState({ copying: !this.state.copying }) } /** * Keep track of data to copy (accumulate while in "copying" state) * * Note that we track keys only - values are already held in state * * @param {string} key */ toggleCopyAttribute(key) { const keys = this.state.keys_to_copy; if(this.state.keys_to_copy[key]){ delete keys[key]; } else { keys[key] = true; } this.setState({ keys_to_copy: keys }) } /** * Handle changes on typical inputs * - e.g. input[type=text], radio, select, textare * * @param {DocumentEvent} event */ handleChange(event) { const target = event.target; let value = (target.value === '')? null : target.value; const name = target.name; // special transform - consider something data driven before adding 'else if's if (name === 'location_postcode' && value !== null) { value = value.toUpperCase(); } this.setState({ [name]: value }); } /** * Handle changes on checkboxes * - e.g. input[type=checkbox] * * @param {DocumentEvent} event */ handleCheck(event) { const target = event.target; const value = target.checked; const name = target.name; this.setState({ [name]: value }); } /** * Handle update directly * - e.g. as callback from MultiTextInput where we set a list of strings * * @param {String} key * @param {*} value */ handleUpdate(key, value) { this.setState({ [key]: value }); } /** * Handle likes separately * - like/love reaction is limited to set/unset per user * * @param {DocumentEvent} event */ handleLike(event) { event.preventDefault(); const like = event.target.checked; fetch(`/building/${this.props.building_id}/like.json`, { method: 'POST', headers:{ 'Content-Type': 'application/json' }, credentials: 'same-origin', body: JSON.stringify({like: like}) }).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}) ); } handleSubmit(event) { event.preventDefault(); this.setState({error: undefined}) fetch(`/building/${this.props.building_id}.json`, { method: 'POST', body: JSON.stringify(this.state), 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); } }.bind(this)).catch( (err) => this.setState({error: err}) ); } render() { const match = this.props.cat === this.props.slug; const cat = this.props.cat; const buildingLike = this.props.building_like; const values_to_copy = {} for (const key of Object.keys(this.state.keys_to_copy)) { values_to_copy[key] = this.state[key] } const data_string = JSON.stringify(values_to_copy); return (
match}>

{this.props.title}

{ match? ( !this.props.inactive?
{ this.props.slug === 'location'? : null } { this.props.fields.map((props) => { switch (props.type) { case 'text': return case 'text_list': return case 'text_long': return case 'number': return case 'year_estimator': return case 'text_multi': return case 'checkbox': return case 'like': return default: return null } }) } { (this.props.slug === 'like')? // special-case for likes null :
} :
) : null }
) } } EditForm.propTypes = { title: PropTypes.string, slug: PropTypes.string, cat: PropTypes.string, help: PropTypes.string, error: PropTypes.object, like: PropTypes.bool, building_like: PropTypes.bool, selectBuilding: PropTypes.func, building_id: PropTypes.number, inactive: PropTypes.bool, fields: PropTypes.array } 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 { 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 ( ) } } MultiTextInput.propTypes = { 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 } const TextListInput = (props) => ( ) 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 { 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 ( ) } } YearEstimator.propTypes = { 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 } const CheckboxInput = (props) => ( ) 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

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 = (props) => { return ( ); } Label.propTypes = { slug: PropTypes.string, cat: PropTypes.string, title: PropTypes.string, disabled: PropTypes.bool, tooltip: PropTypes.string } export default BuildingEdit;