colouring-montreal/app/src/frontend/building-view.js

249 lines
8.4 KiB
JavaScript
Raw Normal View History

2019-01-19 13:47:08 -05:00
import urlapi from 'url';
import React, { Fragment } from 'react';
import { Link, NavLink } from 'react-router-dom';
2018-09-11 15:59:59 -04:00
import Sidebar from './sidebar';
import Tooltip from './tooltip';
2018-09-13 15:41:42 -04:00
import InfoBox from './info-box';
2018-11-13 05:45:35 -05:00
import { EditIcon } from './icons';
2018-11-30 04:26:28 -05:00
import { parseCategoryURL } from '../parse';
2018-09-11 15:59:59 -04:00
import CONFIG from './fields-config.json';
const BuildingView = (props) => {
if (!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 with-space">
2018-11-29 17:00:53 -05:00
<Link to="/view/age.html" className="btn btn-secondary">Back to maps</Link>
</div>
</Sidebar>
);
}
2018-11-30 04:26:28 -05:00
const cat = parseCategoryURL(props.match.url);
return (
2019-05-27 11:31:48 -04:00
<Sidebar title={'Data available for this building'} back={`/view/${cat}.html`}>
{
CONFIG.map(section_props => (
<DataSection
2018-11-29 17:00:53 -05:00
key={section_props.slug} cat={cat}
building_id={props.building_id}
2018-10-04 17:50:33 -04:00
{...section_props}>
{
section_props.fields.map(field_props => {
2019-01-19 13:47:08 -05:00
switch (field_props.type) {
2019-05-27 11:39:16 -04:00
case 'uprn_list':
return <UPRNsDataEntry
key={field_props.slug}
title={field_props.title}
value={props.uprns}
tooltip={field_props.tooltip} />
case 'text_multi':
return <MultiDataEntry
key={field_props.slug}
title={field_props.title}
value={props[field_props.slug]}
tooltip={field_props.tooltip} />
case 'like':
return <LikeDataEntry
key={field_props.slug}
title={field_props.title}
value={props[field_props.slug]}
user_building_like={props.building_like}
tooltip={field_props.tooltip} />
default:
return <DataEntry
key={field_props.slug}
title={field_props.title}
value={props[field_props.slug]}
tooltip={field_props.tooltip} />
2019-01-19 13:47:08 -05:00
}
})
}
</DataSection>
))
}
</Sidebar>
);
}
const DataSection = (props) => {
2018-11-29 17:00:53 -05:00
const match = props.cat === props.slug;
return (
2019-05-27 11:31:48 -04:00
<section id={props.slug} className={(props.inactive)? 'data-section inactive': 'data-section'}>
<header className={`section-header view ${props.slug} ${(match? 'active' : '')}`}>
2018-10-04 17:50:33 -04:00
<NavLink
2018-11-29 17:00:53 -05:00
to={`/view/${props.slug}/building/${props.building_id}.html`}
title={(props.inactive)? 'Coming soon… Click the ? for more info.' :
(match)? 'Hide details' : 'Show details'}
2018-10-04 17:50:33 -04:00
isActive={() => match}>
2018-10-01 11:59:39 -04:00
<h3 className="h3">{props.title}</h3>
</NavLink>
2018-10-05 04:10:20 -04:00
<nav className="icon-buttons">
2019-05-27 11:39:16 -04:00
{
props.help?
<a className="icon-button help" title="Find out more" href={props.help}>
2019-01-22 14:39:16 -05:00
Info
2019-05-27 11:39:16 -04:00
</a>
: null
}
{
!props.inactive?
<NavLink className="icon-button edit" title="Edit data"
to={`/edit/${props.slug}/building/${props.building_id}.html`}>
Edit
2019-05-27 11:39:16 -04:00
<EditIcon />
</NavLink>
: null
}
2018-10-05 04:10:20 -04:00
</nav>
2018-10-01 11:59:39 -04:00
</header>
2019-01-22 14:39:16 -05:00
{
match?
!props.inactive?
2019-05-27 11:39:16 -04:00
<dl className="data-list">{props.children}</dl>
: <p className="data-intro">{props.intro}</p>
: null
2019-01-22 14:39:16 -05:00
}
</section>
);
}
const DataEntry = (props) => (
<Fragment>
<dt>
{ props.title }
{ props.tooltip? <Tooltip text={ props.tooltip } /> : null }
</dt>
<dd>{
2019-05-27 11:31:48 -04:00
(props.value != null && props.value !== '')?
(typeof(props.value) === 'boolean')?
(props.value)? 'Yes' : 'No'
: props.value
: '\u00A0'}</dd>
</Fragment>
);
const LikeDataEntry = (props) => (
<Fragment>
<dt>
{ props.title }
{ props.tooltip? <Tooltip text={ props.tooltip } /> : null }
</dt>
<dd>
2019-05-27 11:39:16 -04:00
{
(props.value != null)?
(props.value === 1)?
`${props.value} person likes this building`
: `${props.value} people like this building`
: '\u00A0'
}
</dd>
{
(props.user_building_like)? <dd>&hellip;including you!</dd> : null
}
</Fragment>
);
2019-01-19 13:47:08 -05:00
const MultiDataEntry = (props) => {
let content;
if (props.value && props.value.length) {
content = <ul>{
props.value.map((item, index) => {
return <li key={index}><a href={sanitise_url(item)}>{item}</a></li>
})
}</ul>
} else {
content = '\u00A0'
}
return (
<Fragment>
<dt>
{ props.title }
{ props.tooltip? <Tooltip text={ props.tooltip } /> : null }
</dt>
<dd>{ content }</dd>
</Fragment>
);
}
function sanitise_url(string){
let url_
// http or https
if (!(string.substring(0, 7) === 'http://' || string.substring(0, 8) === 'https://')){
return null
}
try {
url_ = document.createElement('a')
url_.href = string
} catch (error) {
try {
url_ = urlapi.parse(string)
} catch (error) {
return null
}
}
// required (www.example.com)
if (!url_.hostname || url_.hostname === '' || url_.hostname === 'localhost'){
return null
}
// optional (/some/path)
// url_.pathname;
// optional (?name=value)
// url_.search;
// optional (#anchor)
// url_.hash;
return `${url_.protocol}//${url_.hostname}${url_.pathname || ''}${url_.search || ''}${url_.hash || ''}`
}
const UPRNsDataEntry = (props) => {
const uprns = props.value || [];
const no_parent = uprns.filter(uprn => uprn.parent_uprn == null);
const with_parent = uprns.filter(uprn => uprn.parent_uprn != null);
return (
2019-05-27 11:39:16 -04:00
<Fragment>
<dt>
{ props.title }
{ props.tooltip? <Tooltip text={ props.tooltip } /> : null }
</dt>
<dd><ul className="uprn-list">
<Fragment>{
no_parent.length?
no_parent.map(uprn => (
<li key={uprn.uprn}>{uprn.uprn}</li>
))
2019-05-27 11:39:16 -04:00
: '\u00A0'
}</Fragment>
{
with_parent.length?
<details>
<summary>Children</summary>
{
with_parent.map(uprn => (
<li key={uprn.uprn}>{uprn.uprn} (child of {uprn.parent_uprn})</li>
))
}
</details>
: null
}
</ul></dd>
</Fragment>
)
}
export default BuildingView;