@conveyal/commute
Version:
Commute analysis
366 lines (330 loc) • 12 kB
JavaScript
import copy from 'copy-to-clipboard'
import React, {Component, PropTypes} from 'react'
import { Grid, Row, Col,
Button, ButtonToolbar, Panel, Form, ControlLabel, FormGroup, FormControl } from 'react-bootstrap'
import ButtonLink from './util/button-link'
import Icon from './util/icon'
// default config for a new site: basic report with summary section and 2-hour transit access map
const defaultConfig = {
isPublic: false,
sections: [
{
type: 'summary'
},
{
type: 'map',
mode: 'TRANSIT',
cutoff: 7200
}
]
}
// default config for a newly added section: transit map w/ 2-hour cutoff
const defaultSection = {
type: 'map',
mode: 'TRANSIT',
cutoff: 7200
}
export default class SiteCreateReport extends Component {
static propTypes = {
isMultiSite: PropTypes.bool.isRequired,
multiSite: PropTypes.object,
site: PropTypes.object,
update: PropTypes.func
}
state = {
linkWasJustCopied: false
}
componentWillMount () {
const entity = this._getEntity()
if (!entity.reportConfig || !entity.reportConfig.sections) {
// add the default configuration if needed
entity.reportConfig = defaultConfig
this._save(entity)
} else if (!('isPublic' in entity.reportConfig)) {
entity.reportConfig.isPublic = false
this._save(entity)
}
}
_addSection = () => {
const entity = this._getEntity()
entity.reportConfig.sections.push(defaultSection)
this._save(entity)
}
_copyPublicLink = () => {
copy(this._getReportUrl(true))
this.setState({ linkWasJustCopied: true })
setTimeout(() => {
this.setState({ linkWasJustCopied: false })
}, 3000)
}
_deleteSection = (index) => {
const entity = this._getEntity()
const newSections = entity.reportConfig.sections.filter((e, i) => { return i !== index })
entity.reportConfig.sections = newSections
this._save(entity)
}
_getEntity () {
const { isMultiSite, multiSite, site } = this.props
return isMultiSite ? multiSite : site
}
_getReportUrl (publicUrl) {
const entity = this._getEntity()
let publicPrefix = ''
if (publicUrl) {
const {host, protocol} = window.location
publicPrefix = `${protocol}//${host}/public`
}
return `${publicPrefix}/${this.props.isMultiSite ? 'multi-site' : 'site'}/${entity._id}/report`
}
_save (entity) {
this.props.update({ entity, customRedirectionStrategy: 'none' })
}
_swapSections = (x, y) => {
const entity = this._getEntity()
const temp = entity.reportConfig.sections[y]
entity.reportConfig.sections[y] = entity.reportConfig.sections[x]
entity.reportConfig.sections[x] = temp
this._save(entity)
}
_togglePublic = (evt) => {
const entity = this._getEntity()
entity.reportConfig.isPublic = evt.target.checked
this._save(entity)
}
_updateSection = (index, config) => {
const entity = this._getEntity()
entity.reportConfig.sections[index] = config
this._save(entity)
}
render () {
const {isMultiSite} = this.props
const entity = this._getEntity()
const entityHasSections = !!(
entity.reportConfig &&
entity.reportConfig.sections &&
entity.reportConfig.sections.length > 0
)
return (
<Grid className='create-report'>
<Row ref='report'>
<Col xs={12}>
<h2 className='header'>Create Printable Report</h2>
<Row>
<Col xs={4}>
<p className='instruction'>
This page allows you to create a customized report for this site. Use the buttons in the Sections headers to reorder or delete sections. Click the "Add Section" button below to add a new section to the end of the report.
</p>
<div className='main-button'>
<Button
bsStyle='success'
onClick={this._addSection}
><Icon type='plus' /> Add Section</Button>
</div>
<p className='instruction'>
To generate the report, click the "View Report" button.
</p>
<div className='main-button'>
<ButtonLink
bsStyle='primary'
bsSize='large'
to={this._getReportUrl()}>
<Icon type='print' /> View Report
</ButtonLink>
</div>
<p className='instruction'>
It is possible to make this report viewable without having to login.
Checking the box below will enable this and create a page that will
allow anyone who knows the URL to be able to access the report.
</p>
<div className='main-button'>
<Panel>
<div className='checkbox'>
<label
htmlFor='create-report-checkbox'
style={{
fontWeight: 'bold'
}}
>
<input
checked={entity.reportConfig.isPublic}
id='create-report-checkbox'
onChange={this._togglePublic}
type='checkbox'
/>
Make Report Public
</label>
</div>
{entity.reportConfig &&
entity.reportConfig.isPublic && (
<div>
<p>This report is public!</p>
<p>
<a
href={this._getReportUrl(true)}
>
Click Here
</a> to go to the public report.
</p>
<Button
bsStyle='success'
onClick={this._copyPublicLink}
>
<Icon type='clipboard' />
Copy Link to the Clipboard
</Button>
{this.state.linkWasJustCopied &&
<p style={{ marginTop: '10px' }}>Successfully copied!</p>
}
</div>
)
}
</Panel>
</div>
</Col>
<Col xs={8}>
{!entityHasSections && (
<Panel className='empty-panel'>
<h3>This report does not have any sections yet.</h3>
Click the "Add Section" button to add the first section.
</Panel>
)}
{entityHasSections && entity.reportConfig.sections.map((section, k) => {
return (
<ReportSection
config={section}
index={k}
isMultiSite={isMultiSite}
length={entity.reportConfig.sections.length}
key={k}
deleteSection={this._deleteSection}
updateSection={this._updateSection}
swapSections={this._swapSections}
/>
)
})}
</Col>
</Row>
</Col>
</Row>
</Grid>
)
}
}
class ReportSection extends Component {
static propTypes = {
config: PropTypes.object.isRequired,
deleteSection: PropTypes.func.isRequired,
index: PropTypes.number.isRequired,
isMultiSite: PropTypes.bool.isRequired,
length: PropTypes.number.isRequired,
swapSections: PropTypes.func.isRequired,
updateSection: PropTypes.func.isRequired
}
_delete = () => {
const {deleteSection, index} = this.props
deleteSection(index)
}
_moveDown = () => {
const {index, swapSections} = this.props
swapSections(index, index + 1)
}
_moveUp = () => {
const {index, swapSections} = this.props
swapSections(index, index - 1)
}
_setCutoff = evt => { this._updateProperty('cutoff', evt.target.value) }
_setMode = evt => { this._updateProperty('mode', evt.target.value) }
_setType = evt => { this._updateProperty('type', evt.target.value) }
_updateProperty (prop, value) {
const { config, index, updateSection } = this.props
config[prop] = value
updateSection(index, config)
}
render () {
const { config, index, isMultiSite, length } = this.props
const header = <div>
<span className='section-header'>Section {index + 1}</span>
<ButtonToolbar className='pull-right'>
{index > 0 && (
<Button
bsSize='xsmall'
bsStyle='warning'
onClick={this._moveUp}
><Icon type='arrow-up' /> Move Up</Button>
)}
{index < length - 1 && (
<Button
bsSize='xsmall'
bsStyle='warning'
onClick={this._moveDown}
><Icon type='arrow-down' /> Move Down</Button>
)}
<Button
bsSize='xsmall'
bsStyle='danger'
onClick={this._delete}
><Icon type='trash' /> Delete</Button>
</ButtonToolbar>
</div>
return (
<Panel header={header}>
<Row>
<Col xs={6}>
<Form inline>
<FormGroup>
<ControlLabel>Section Type: </ControlLabel>
<FormControl
componentClass='select'
onChange={this._setType}
value={config.type}
>
<option value='summary'>Site Access Infographic</option>
{!isMultiSite && <option value='map'>Commuter Access Map</option>}
{isMultiSite && <option value='commuter-map'>Commuter Map</option>}
<option value='access-table'>Commuter Access Table</option>
<option value='ridematch-table'>Ridematching Table</option>
</FormControl>
</FormGroup>
</Form>
</Col>
<Col xs={6}>
{(config.type === 'map' || config.type === 'access-table') && (
<Form inline>
<FormGroup>
<ControlLabel>Access Mode: </ControlLabel>
<FormControl
componentClass='select'
onChange={this._setMode}
value={config.mode}
>
<option value='TRANSIT'>Transit</option>
<option value='WALK'>Walk</option>
<option value='BICYCLE'>Bike</option>
<option value='CAR'>Drive</option>
</FormControl>
</FormGroup>
{config.type === 'map' && (
<FormGroup className='cutoff-selector-container'>
<ControlLabel>Travel Time Cutoff: </ControlLabel>
<FormControl
componentClass='select'
onChange={this._setCutoff}
value={config.cutoff}
>
<option value={900}>15 minutes</option>
<option value={1800}>30 minutes</option>
<option value={2700}>45 minutes</option>
<option value={3600}>1 hour</option>
<option value={5400}>1 hour, 30 minutes</option>
<option value={7200}>2 hours</option>
</FormControl>
</FormGroup>
)}
</Form>
)}
</Col>
</Row>
</Panel>
)
}
}