Declarative categories list
This commit is contained in:
parent
43f239c853
commit
1c265b828d
@ -11,7 +11,7 @@ import BuildingView from './building/building-view';
|
||||
import ColouringMap from './map/map';
|
||||
import Header from './header';
|
||||
import MultiEdit from './building/multi-edit';
|
||||
import Overview from './building/overview';
|
||||
import Categories from './building/categories';
|
||||
|
||||
import AboutPage from './pages/about';
|
||||
import ContributorAgreementPage from './pages/contributor-agreement';
|
||||
@ -195,6 +195,9 @@ class App extends React.Component<any, any> { // TODO: add proper types
|
||||
}
|
||||
|
||||
render() {
|
||||
const building_id = (this.state.building)?
|
||||
this.state.building.building_id
|
||||
: 2503371 // Default to UCL main building. TODO use last selected if any
|
||||
return (
|
||||
<Fragment>
|
||||
<Header user={this.state.user} />
|
||||
@ -203,18 +206,18 @@ class App extends React.Component<any, any> { // TODO: add proper types
|
||||
<Route exact path="/">
|
||||
<Welcome />
|
||||
</Route>
|
||||
<Route exact path="/view/:cat.html" render={(props) => (
|
||||
<Overview
|
||||
{...props}
|
||||
mode='view' user={this.state.user}
|
||||
<Route exact path="/view/categories.html">
|
||||
<Categories
|
||||
mode="view"
|
||||
building_id={building_id}
|
||||
/>
|
||||
) } />
|
||||
<Route exact path="/edit/:cat.html" render={(props) => (
|
||||
<Overview
|
||||
{...props}
|
||||
mode='edit' user={this.state.user}
|
||||
</Route>
|
||||
<Route exact path="/edit/categories.html">
|
||||
<Categories
|
||||
mode="edit"
|
||||
building_id={building_id}
|
||||
/>
|
||||
) } />
|
||||
</Route>
|
||||
<Route exact path="/multi-edit/:cat.html" render={(props) => (
|
||||
<MultiEdit
|
||||
{...props}
|
||||
|
147
app/src/frontend/building/categories.tsx
Normal file
147
app/src/frontend/building/categories.tsx
Normal file
@ -0,0 +1,147 @@
|
||||
import React from 'react';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import Sidebar from './sidebar';
|
||||
|
||||
const Categories = (props) => (
|
||||
<Sidebar>
|
||||
<Category
|
||||
title="Location"
|
||||
slug="location"
|
||||
help="https://pages.colouring.london/location"
|
||||
inactive={false}
|
||||
mode={props.mode}
|
||||
building_id={props.building_id}
|
||||
/>
|
||||
<Category
|
||||
title="Land Use"
|
||||
slug="use"
|
||||
help="https://pages.colouring.london/use"
|
||||
inactive={true}
|
||||
mode={props.mode}
|
||||
building_id={props.building_id}
|
||||
/>
|
||||
<Category
|
||||
title="Type"
|
||||
slug="type"
|
||||
help="https://pages.colouring.london/buildingtypology"
|
||||
inactive={true}
|
||||
mode={props.mode}
|
||||
building_id={props.building_id}
|
||||
/>
|
||||
<Category
|
||||
title="Age"
|
||||
slug="age"
|
||||
help="https://pages.colouring.london/age"
|
||||
inactive={false}
|
||||
mode={props.mode}
|
||||
building_id={props.building_id}
|
||||
/>
|
||||
<Category
|
||||
title="Size & Shape"
|
||||
slug="size"
|
||||
help="https://pages.colouring.london/shapeandsize"
|
||||
inactive={false}
|
||||
mode={props.mode}
|
||||
building_id={props.building_id}
|
||||
/>
|
||||
<Category
|
||||
title="Construction"
|
||||
slug="construction"
|
||||
help="https://pages.colouring.london/construction"
|
||||
inactive={true}
|
||||
mode={props.mode}
|
||||
building_id={props.building_id}
|
||||
/>
|
||||
<Category
|
||||
title="Team"
|
||||
slug="team"
|
||||
help="https://pages.colouring.london/team"
|
||||
inactive={true}
|
||||
mode={props.mode}
|
||||
building_id={props.building_id}
|
||||
/>
|
||||
<Category
|
||||
title="Sustainability"
|
||||
slug="sustainability"
|
||||
help="https://pages.colouring.london/sustainability"
|
||||
inactive={true}
|
||||
mode={props.mode}
|
||||
building_id={props.building_id}
|
||||
/>
|
||||
<Category
|
||||
title="Greenery"
|
||||
slug="greenery"
|
||||
help="https://pages.colouring.london/greenery"
|
||||
inactive={true}
|
||||
mode={props.mode}
|
||||
building_id={props.building_id}
|
||||
/>
|
||||
<Category
|
||||
title="Community"
|
||||
slug="community"
|
||||
help="https://pages.colouring.london/community"
|
||||
inactive={false}
|
||||
mode={props.mode}
|
||||
building_id={props.building_id}
|
||||
/>
|
||||
<Category
|
||||
title="Planning"
|
||||
slug="planning"
|
||||
help="https://pages.colouring.london/planning"
|
||||
inactive={true}
|
||||
mode={props.mode}
|
||||
building_id={props.building_id}
|
||||
/>
|
||||
<Category
|
||||
title="Like Me!"
|
||||
slug="like"
|
||||
help="https://pages.colouring.london/likeme"
|
||||
inactive={false}
|
||||
mode={props.mode}
|
||||
building_id={props.building_id}
|
||||
/>
|
||||
</Sidebar>
|
||||
)
|
||||
|
||||
Categories.propTypes = {
|
||||
mode: PropTypes.string,
|
||||
building_id: PropTypes.number
|
||||
}
|
||||
|
||||
const Category = (props) => (
|
||||
<section className={(props.inactive? 'inactive ': '') + 'data-section legend'}>
|
||||
<header className={`section-header ${props.mode} ${props.slug}`}>
|
||||
<NavLink
|
||||
to={`/${props.mode}/${props.slug}/building/${props.building_id}.html`}
|
||||
title={
|
||||
(props.inactive)?
|
||||
'Coming soon… Click the ? for more info.'
|
||||
: 'Show on map'
|
||||
}>
|
||||
<h3 className="h3">{props.title}</h3>
|
||||
</NavLink>
|
||||
<nav className="icon-buttons">
|
||||
{
|
||||
props.help?
|
||||
<a className="icon-button help" href={props.help}>
|
||||
Info
|
||||
</a>
|
||||
: null
|
||||
}
|
||||
</nav>
|
||||
</header>
|
||||
</section>
|
||||
)
|
||||
|
||||
Category.propTypes = {
|
||||
title: PropTypes.string,
|
||||
slug: PropTypes.string,
|
||||
help: PropTypes.string,
|
||||
inactive: PropTypes.bool,
|
||||
mode: PropTypes.string,
|
||||
building_id: PropTypes.number
|
||||
}
|
||||
|
||||
export default Categories;
|
@ -1,107 +0,0 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { NavLink, Redirect } from 'react-router-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import Sidebar from './sidebar';
|
||||
import { EditIcon } from '../components/icons';
|
||||
import CONFIG from './fields-config.json';
|
||||
|
||||
const Overview = (props) => {
|
||||
var dataLayer = 'age'; // always default
|
||||
if (props.match && props.match.params && props.match.params.cat) {
|
||||
dataLayer = props.match.params.cat;
|
||||
}
|
||||
|
||||
if (props.mode === 'edit' && !props.user){
|
||||
return <Redirect to="/sign-up.html" />
|
||||
}
|
||||
|
||||
const title = (props.mode === 'view')? 'View maps' : 'Add or edit data';
|
||||
const back = (props.mode === 'edit')? `/view/${dataLayer}.html` : undefined;
|
||||
|
||||
return (
|
||||
<Sidebar title={title} back={back}>
|
||||
{
|
||||
CONFIG.map((dataGroup) => (
|
||||
<OverviewSection {...dataGroup}
|
||||
dataLayer={dataLayer} key={dataGroup.slug} mode={props.mode} />
|
||||
))
|
||||
}
|
||||
</Sidebar>
|
||||
);
|
||||
}
|
||||
|
||||
Overview.propTypes = {
|
||||
match: PropTypes.object,
|
||||
mode: PropTypes.string,
|
||||
user: PropTypes.object
|
||||
}
|
||||
|
||||
const OverviewSection = (props) => {
|
||||
const match = props.dataLayer === props.slug;
|
||||
const inactive = props.inactive;
|
||||
|
||||
return (
|
||||
<section className={(inactive? 'inactive ': '') + 'data-section legend'}>
|
||||
<header className={`section-header ${props.mode} ${props.slug} ${(match? 'active' : '')}`}>
|
||||
<NavLink
|
||||
to={`/${props.mode}/${props.slug}.html`}
|
||||
isActive={() => match}
|
||||
title={(inactive)? 'Coming soon… Click the ? for more info.' :
|
||||
(match)? '' : 'Show on map'}>
|
||||
<h3 className="h3">{props.title}</h3>
|
||||
</NavLink>
|
||||
<nav className="icon-buttons">
|
||||
{
|
||||
props.help?
|
||||
<a className="icon-button help" href={props.help}>
|
||||
Info
|
||||
</a>
|
||||
: null
|
||||
}
|
||||
{
|
||||
props.mode === 'view'?
|
||||
<NavLink className="icon-button edit" title="Edit data"
|
||||
to={`/edit/${props.slug}.html`}>
|
||||
Edit
|
||||
<EditIcon />
|
||||
</NavLink>
|
||||
: null
|
||||
}
|
||||
</nav>
|
||||
</header>
|
||||
{
|
||||
(match && props.intro)?
|
||||
(
|
||||
<Fragment>
|
||||
<p className="data-intro">{props.intro}</p>
|
||||
<ul>
|
||||
{
|
||||
props.fields.map((field) => {
|
||||
return (<li key={field.slug}>{field.title}</li>)
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
</Fragment>
|
||||
)
|
||||
: null
|
||||
}
|
||||
</section>
|
||||
)
|
||||
};
|
||||
|
||||
OverviewSection.propTypes = {
|
||||
title: PropTypes.string,
|
||||
slug: PropTypes.string,
|
||||
intro: PropTypes.string,
|
||||
help: PropTypes.string,
|
||||
dataLayer: PropTypes.string,
|
||||
mode: PropTypes.string,
|
||||
inactive: PropTypes.bool,
|
||||
fields: PropTypes.arrayOf(PropTypes.shape({
|
||||
title: PropTypes.string,
|
||||
slug: PropTypes.string
|
||||
}))
|
||||
}
|
||||
|
||||
export default Overview;
|
@ -53,24 +53,6 @@
|
||||
/**
|
||||
* Sidebar main header
|
||||
*/
|
||||
.sidebar-header {
|
||||
border-bottom: 6px solid;
|
||||
border-image: linear-gradient(
|
||||
to right,
|
||||
#edc40b 0%, #edc40b 8.3%,
|
||||
#f0ee0c 8.3%, #f0ee0c 16.6%,
|
||||
#ff9100 16.6%, #ff9100 25%,
|
||||
#ee5f63 25%, #ee5f63 33.3%,
|
||||
#ee91bf 33.3%, #ee91bf 41.6%,
|
||||
#aa7fa7 41.6%, #aa7fa7 50%,
|
||||
#6f879c 50%, #6f879c 58.3%,
|
||||
#5ec232 58.3%, #5ec232 66.6%,
|
||||
#6dbb8b 66.6%, #6dbb8b 75%,
|
||||
#65b7ff 75%, #65b7ff 83.3%,
|
||||
#a1a3a9 83.3%, #a1a3a9 91.6%,
|
||||
#9c896d 91.6%, #9c896d 100%
|
||||
) 1;
|
||||
}
|
||||
.sidebar-header h2 {
|
||||
margin: 0.45rem 0 0.6rem;
|
||||
display: inline-block;
|
||||
|
@ -6,18 +6,24 @@ import './sidebar.css';
|
||||
import { BackIcon } from '../components/icons';
|
||||
|
||||
const Sidebar = (props) => (
|
||||
<div id="legend" className="info-container">
|
||||
<header className="sidebar-header">
|
||||
{
|
||||
props.back?
|
||||
<Link className="icon-button back" to={props.back}>
|
||||
<BackIcon />
|
||||
</Link>
|
||||
: null
|
||||
}
|
||||
<h2 className="h2">{props.title}</h2>
|
||||
</header>
|
||||
{props.children}
|
||||
<div id="sidebar" className="info-container">
|
||||
{
|
||||
props.title?
|
||||
<header className="sidebar-header">
|
||||
{
|
||||
props.back?
|
||||
<Link className="icon-button back" to={props.back}>
|
||||
<BackIcon />
|
||||
</Link>
|
||||
: null
|
||||
}
|
||||
<h2 className="h2">{props.title}</h2>
|
||||
</header>
|
||||
: null
|
||||
}
|
||||
{
|
||||
props.children
|
||||
}
|
||||
</div>
|
||||
);
|
||||
|
||||
|
@ -51,13 +51,8 @@ class Header extends React.Component<any, any> { // TODO: add proper types
|
||||
</a>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<NavLink to="/view/age.html" className="nav-link">
|
||||
View Maps
|
||||
</NavLink>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<NavLink to="/edit/age.html" className="nav-link">
|
||||
Add/Edit Data
|
||||
<NavLink to="/view/categories.html" className="nav-link">
|
||||
View/Edit Maps
|
||||
</NavLink>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
|
@ -17,7 +17,7 @@ const Welcome = () => (
|
||||
volunteers of all ages and abilities to test and provide feedback on the site as we
|
||||
build it.
|
||||
</p>
|
||||
<Link to="/view/age.html"
|
||||
<Link to="/view/categories.html"
|
||||
className="btn btn-outline-dark btn-lg btn-block">
|
||||
Start Colouring Here!
|
||||
</Link>
|
||||
|
Loading…
Reference in New Issue
Block a user