Type, simplify, fix data containers
This contains a couple fixes for minor bugs that were discovered after adding static types to the category data editing code. The other changes are mostly refactoring and styling
This commit is contained in:
parent
ce473cb453
commit
3a35f5dab5
@ -14,13 +14,24 @@ import StreetscapeContainer from './data-containers/streetscape';
|
|||||||
import CommunityContainer from './data-containers/community';
|
import CommunityContainer from './data-containers/community';
|
||||||
import PlanningContainer from './data-containers/planning';
|
import PlanningContainer from './data-containers/planning';
|
||||||
import LikeContainer from './data-containers/like';
|
import LikeContainer from './data-containers/like';
|
||||||
|
import { Building } from '../models/building';
|
||||||
|
|
||||||
|
|
||||||
|
interface BuildingViewProps {
|
||||||
|
cat: string;
|
||||||
|
mode: 'view' | 'edit' | 'multi-edit';
|
||||||
|
building: Building;
|
||||||
|
building_like: boolean;
|
||||||
|
user: any;
|
||||||
|
selectBuilding: (building:any) => void
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Top-level container for building view/edit form
|
* Top-level container for building view/edit form
|
||||||
*
|
*
|
||||||
* @param props
|
* @param props
|
||||||
*/
|
*/
|
||||||
const BuildingView = (props) => {
|
const BuildingView: React.FunctionComponent<BuildingViewProps> = (props) => {
|
||||||
switch (props.cat) {
|
switch (props.cat) {
|
||||||
case 'location':
|
case 'location':
|
||||||
return <LocationContainer
|
return <LocationContainer
|
||||||
|
@ -3,8 +3,18 @@ import { Link, NavLink } from 'react-router-dom';
|
|||||||
|
|
||||||
import { BackIcon, EditIcon, ViewIcon }from '../components/icons';
|
import { BackIcon, EditIcon, ViewIcon }from '../components/icons';
|
||||||
|
|
||||||
|
interface ContainerHeaderProps {
|
||||||
|
cat: string;
|
||||||
|
mode: 'view' | 'edit' | 'multi-edit';
|
||||||
|
building: any;
|
||||||
|
title: string;
|
||||||
|
copy: any;
|
||||||
|
inactive?: boolean;
|
||||||
|
data_string: string;
|
||||||
|
help: string;
|
||||||
|
}
|
||||||
|
|
||||||
const ContainerHeader: React.FunctionComponent<any> = (props) => (
|
const ContainerHeader: React.FunctionComponent<ContainerHeaderProps> = (props) => (
|
||||||
<header className={`section-header view ${props.cat} background-${props.cat}`}>
|
<header className={`section-header view ${props.cat} background-${props.cat}`}>
|
||||||
<Link className="icon-button back" to={`/${props.mode}/categories${props.building != undefined ? `/${props.building.building_id}` : ''}`}>
|
<Link className="icon-button back" to={`/${props.mode}/categories${props.building != undefined ? `/${props.building.building_id}` : ''}`}>
|
||||||
<BackIcon />
|
<BackIcon />
|
||||||
|
@ -5,6 +5,28 @@ import { Redirect } from 'react-router-dom';
|
|||||||
import ContainerHeader from './container-header';
|
import ContainerHeader from './container-header';
|
||||||
import ErrorBox from '../components/error-box';
|
import ErrorBox from '../components/error-box';
|
||||||
import InfoBox from '../components/info-box';
|
import InfoBox from '../components/info-box';
|
||||||
|
import { Building } from '../models/building';
|
||||||
|
import { User } from '../models/user';
|
||||||
|
|
||||||
|
interface DataContainerProps {
|
||||||
|
title: string;
|
||||||
|
cat: string;
|
||||||
|
intro: string;
|
||||||
|
help: string;
|
||||||
|
inactive?: boolean;
|
||||||
|
|
||||||
|
user: User;
|
||||||
|
mode: 'view' | 'edit' | 'multi-edit';
|
||||||
|
building: Building;
|
||||||
|
building_like: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DataContainerState {
|
||||||
|
error: string;
|
||||||
|
copying: boolean;
|
||||||
|
keys_to_copy: object;
|
||||||
|
building: Building
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shared functionality for view/edit forms
|
* Shared functionality for view/edit forms
|
||||||
@ -15,14 +37,15 @@ import InfoBox from '../components/info-box';
|
|||||||
* @param WrappedComponent
|
* @param WrappedComponent
|
||||||
*/
|
*/
|
||||||
const withCopyEdit = (WrappedComponent) => {
|
const withCopyEdit = (WrappedComponent) => {
|
||||||
return class extends React.Component<any, any> { // TODO: add proper types
|
return class DataContainer extends React.Component<DataContainerProps, DataContainerState> { // TODO: add proper types
|
||||||
|
static displayName = 'DataContainer';
|
||||||
|
|
||||||
static propTypes = { // TODO: generate propTypes from TS
|
static propTypes = { // TODO: generate propTypes from TS
|
||||||
title: PropTypes.string,
|
title: PropTypes.string,
|
||||||
slug: PropTypes.string,
|
slug: PropTypes.string,
|
||||||
intro: PropTypes.string,
|
intro: PropTypes.string,
|
||||||
help: PropTypes.string,
|
help: PropTypes.string,
|
||||||
inactive: PropTypes.bool,
|
inactive: PropTypes.bool,
|
||||||
building_id: PropTypes.number,
|
|
||||||
children: PropTypes.node
|
children: PropTypes.node
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -30,8 +53,7 @@ const withCopyEdit = (WrappedComponent) => {
|
|||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
error: this.props.error || undefined,
|
error: undefined,
|
||||||
like: this.props.like || undefined,
|
|
||||||
copying: false,
|
copying: false,
|
||||||
keys_to_copy: {},
|
keys_to_copy: {},
|
||||||
building: this.props.building
|
building: this.props.building
|
||||||
@ -62,7 +84,7 @@ const withCopyEdit = (WrappedComponent) => {
|
|||||||
* @param {string} key
|
* @param {string} key
|
||||||
*/
|
*/
|
||||||
toggleCopyAttribute(key: string) {
|
toggleCopyAttribute(key: string) {
|
||||||
const keys = this.state.keys_to_copy;
|
const keys = {...this.state.keys_to_copy};
|
||||||
if(this.state.keys_to_copy[key]){
|
if(this.state.keys_to_copy[key]){
|
||||||
delete keys[key];
|
delete keys[key];
|
||||||
} else {
|
} else {
|
||||||
@ -181,7 +203,7 @@ const withCopyEdit = (WrappedComponent) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.state.mode === 'edit' && !this.props.user){
|
if (this.props.mode === 'edit' && !this.props.user){
|
||||||
return <Redirect to="/sign-up.html" />
|
return <Redirect to="/sign-up.html" />
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,32 +220,42 @@ const withCopyEdit = (WrappedComponent) => {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
id={this.props.slug}
|
id={this.props.cat}
|
||||||
className="data-section">
|
className="data-section">
|
||||||
<ContainerHeader
|
<ContainerHeader
|
||||||
{...this.props}
|
{...this.props}
|
||||||
data_string={data_string}
|
data_string={data_string}
|
||||||
copy={copy}
|
copy={copy}
|
||||||
/>
|
/>
|
||||||
|
<div className="section-body">
|
||||||
{
|
{
|
||||||
this.props.building != undefined ?
|
this.props.inactive ?
|
||||||
<form
|
<Fragment>
|
||||||
action={`/edit/${this.props.slug}/${this.props.building.building_id}`}
|
|
||||||
method="POST"
|
|
||||||
onSubmit={this.handleSubmit}>
|
|
||||||
{
|
|
||||||
(this.props.inactive) ?
|
|
||||||
<InfoBox
|
<InfoBox
|
||||||
msg={`We're not collecting data on ${this.props.title.toLowerCase()} yet - check back soon.`}
|
msg={`We're not collecting data on ${this.props.title.toLowerCase()} yet - check back soon.`}
|
||||||
/>
|
/>
|
||||||
: null
|
<WrappedComponent
|
||||||
}
|
building={undefined}
|
||||||
|
building_like={undefined}
|
||||||
|
mode={this.props.mode}
|
||||||
|
copy={copy}
|
||||||
|
onChange={this.handleChange}
|
||||||
|
onCheck={this.handleCheck}
|
||||||
|
onLike={this.handleLike}
|
||||||
|
onUpdate={this.handleUpdate}
|
||||||
|
/>
|
||||||
|
</Fragment> :
|
||||||
|
this.props.building != undefined ?
|
||||||
|
<form
|
||||||
|
action={`/edit/${this.props.cat}/${this.props.building.building_id}`}
|
||||||
|
method="POST"
|
||||||
|
onSubmit={this.handleSubmit}>
|
||||||
{
|
{
|
||||||
(this.props.mode === 'edit' && !this.props.inactive) ?
|
(this.props.mode === 'edit' && !this.props.inactive) ?
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<ErrorBox msg={this.state.error} />
|
<ErrorBox msg={this.state.error} />
|
||||||
{
|
{
|
||||||
this.props.slug === 'like' ? // special-case for likes
|
this.props.cat === 'like' ? // special-case for likes
|
||||||
null :
|
null :
|
||||||
<div className="buttons-container with-space">
|
<div className="buttons-container with-space">
|
||||||
<button
|
<button
|
||||||
@ -246,31 +278,10 @@ const withCopyEdit = (WrappedComponent) => {
|
|||||||
onLike={this.handleLike}
|
onLike={this.handleLike}
|
||||||
onUpdate={this.handleUpdate}
|
onUpdate={this.handleUpdate}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form> :
|
||||||
:
|
|
||||||
<form>
|
|
||||||
{
|
|
||||||
(this.props.inactive)?
|
|
||||||
<Fragment>
|
|
||||||
<InfoBox
|
|
||||||
msg={`We're not collecting data on ${this.props.title.toLowerCase()} yet - check back soon.`}
|
|
||||||
/>
|
|
||||||
<WrappedComponent
|
|
||||||
building={undefined}
|
|
||||||
building_like={undefined}
|
|
||||||
mode={this.props.mode}
|
|
||||||
copy={copy}
|
|
||||||
onChange={this.handleChange}
|
|
||||||
onCheck={this.handleCheck}
|
|
||||||
onLike={this.handleLike}
|
|
||||||
onUpdate={this.handleUpdate}
|
|
||||||
/>
|
|
||||||
</Fragment>
|
|
||||||
:
|
|
||||||
<InfoBox msg="Select a building to view data"></InfoBox>
|
<InfoBox msg="Select a building to view data"></InfoBox>
|
||||||
}
|
}
|
||||||
</form>
|
</div>
|
||||||
}
|
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -139,6 +139,11 @@
|
|||||||
/**
|
/**
|
||||||
* Data list sections
|
* Data list sections
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
.section-body {
|
||||||
|
margin-top: 0.75em;
|
||||||
|
padding: 0 0.75em;
|
||||||
|
}
|
||||||
.data-section .h3 {
|
.data-section .h3 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
@ -162,9 +167,7 @@
|
|||||||
padding-left: 0.75rem;
|
padding-left: 0.75rem;
|
||||||
padding-right: 0.75rem;
|
padding-right: 0.75rem;
|
||||||
}
|
}
|
||||||
.data-section form {
|
|
||||||
padding: 0 0.75rem;
|
|
||||||
}
|
|
||||||
.data-list a {
|
.data-list a {
|
||||||
color: #555;
|
color: #555;
|
||||||
}
|
}
|
||||||
|
@ -199,7 +199,7 @@ class MapApp extends React.Component<MapAppProps, MapAppState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const mode = this.props.match.params.mode || 'basic';
|
const mode = this.props.match.params.mode;
|
||||||
|
|
||||||
let category = this.state.category || 'age';
|
let category = this.state.category || 'age';
|
||||||
|
|
||||||
@ -240,7 +240,7 @@ class MapApp extends React.Component<MapAppProps, MapAppState> {
|
|||||||
</Switch>
|
</Switch>
|
||||||
<ColouringMap
|
<ColouringMap
|
||||||
building={this.state.building}
|
building={this.state.building}
|
||||||
mode={mode}
|
mode={mode || 'basic'}
|
||||||
category={category}
|
category={category}
|
||||||
revision_id={this.state.revision_id}
|
revision_id={this.state.revision_id}
|
||||||
selectBuilding={this.selectBuilding}
|
selectBuilding={this.selectBuilding}
|
||||||
|
8
app/src/frontend/models/building.ts
Normal file
8
app/src/frontend/models/building.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
interface Building {
|
||||||
|
building_id: number;
|
||||||
|
// TODO: add other fields as needed
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
Building
|
||||||
|
};
|
8
app/src/frontend/models/user.ts
Normal file
8
app/src/frontend/models/user.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
interface User {
|
||||||
|
username: string;
|
||||||
|
// TODO: add other fields as needed
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
User
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user