sipp11
7 years ago
20 changed files with 841 additions and 66 deletions
@ -0,0 +1,5 @@
|
||||
{ |
||||
"todo-tree.flat": true, |
||||
"todo-tree.grouped": false, |
||||
"todo-tree.expanded": true |
||||
} |
@ -0,0 +1,64 @@
|
||||
import { RSAA } from 'redux-api-middleware' |
||||
|
||||
import * as types from '../constants/ActionTypes' |
||||
import { RSAAHeaders } from '../utils/ApiClient' |
||||
import { API_URL } from '../constants/Api' |
||||
|
||||
|
||||
export const getRoute = (query) => ({ |
||||
[RSAA]: { |
||||
endpoint: `${API_URL}/route/?${query || ''}`, |
||||
method: 'GET', |
||||
headers: RSAAHeaders, |
||||
bailout: (state) => state.route.fetching, |
||||
types: [ |
||||
types.ROUTE_REQUEST, |
||||
types.ROUTE_SUCCESS, |
||||
types.ROUTE_FAILURE, |
||||
] |
||||
} |
||||
}) |
||||
|
||||
export const updateRoute = (id, body) => ({ |
||||
[RSAA]: { |
||||
endpoint: `${API_URL}/route/${id}/`, |
||||
body: JSON.stringify(body), |
||||
method: 'PATCH', |
||||
headers: RSAAHeaders, |
||||
types: [ |
||||
types.ROUTE_REQUEST, |
||||
types.ROUTE_UPDATE, |
||||
types.ROUTE_FAILURE, |
||||
] |
||||
} |
||||
}) |
||||
|
||||
export const createRoute = (body) => ({ |
||||
[RSAA]: { |
||||
endpoint: `${API_URL}/route/`, |
||||
body: JSON.stringify(body), |
||||
method: 'POST', |
||||
headers: RSAAHeaders, |
||||
types: [ |
||||
types.ROUTE_REQUEST, |
||||
types.ROUTE_CREATE, |
||||
types.ROUTE_FAILURE, |
||||
] |
||||
} |
||||
}) |
||||
|
||||
export const deleteRoute = (id) => ({ |
||||
[RSAA]: { |
||||
endpoint: `${API_URL}/route/${id}/`, |
||||
method: 'DELETE', |
||||
headers: RSAAHeaders, |
||||
types: [ |
||||
types.ROUTE_REQUEST, |
||||
{ |
||||
type: types.ROUTE_DELETE, |
||||
meta: { id } |
||||
}, |
||||
types.ROUTE_FAILURE, |
||||
] |
||||
} |
||||
}) |
@ -0,0 +1,64 @@
|
||||
import { RSAA } from 'redux-api-middleware' |
||||
|
||||
import * as types from '../constants/ActionTypes' |
||||
import { RSAAHeaders } from '../utils/ApiClient' |
||||
import { API_URL } from '../constants/Api' |
||||
|
||||
|
||||
export const getStop = (query) => ({ |
||||
[RSAA]: { |
||||
endpoint: `${API_URL}/stop/?${query || ''}`, |
||||
method: 'GET', |
||||
headers: RSAAHeaders, |
||||
bailout: (state) => state.stop.fetching, |
||||
types: [ |
||||
types.STOP_REQUEST, |
||||
types.STOP_SUCCESS, |
||||
types.STOP_FAILURE, |
||||
] |
||||
} |
||||
}) |
||||
|
||||
export const updateStop = (id, body) => ({ |
||||
[RSAA]: { |
||||
endpoint: `${API_URL}/stop/${id}/`, |
||||
body: JSON.stringify(body), |
||||
method: 'PATCH', |
||||
headers: RSAAHeaders, |
||||
types: [ |
||||
types.STOP_REQUEST, |
||||
types.STOP_UPDATE, |
||||
types.STOP_FAILURE, |
||||
] |
||||
} |
||||
}) |
||||
|
||||
export const createStop = (body) => ({ |
||||
[RSAA]: { |
||||
endpoint: `${API_URL}/stop/`, |
||||
body: JSON.stringify(body), |
||||
method: 'POST', |
||||
headers: RSAAHeaders, |
||||
types: [ |
||||
types.STOP_REQUEST, |
||||
types.STOP_CREATE, |
||||
types.STOP_FAILURE, |
||||
] |
||||
} |
||||
}) |
||||
|
||||
export const deleteStop = (id) => ({ |
||||
[RSAA]: { |
||||
endpoint: `${API_URL}/stop/${id}/`, |
||||
method: 'DELETE', |
||||
headers: RSAAHeaders, |
||||
types: [ |
||||
types.STOP_REQUEST, |
||||
{ |
||||
type: types.STOP_DELETE, |
||||
meta: { id } |
||||
}, |
||||
types.STOP_FAILURE, |
||||
] |
||||
} |
||||
}) |
@ -0,0 +1,208 @@
|
||||
import React, { Component } from 'react' |
||||
import styled from 'styled-components' |
||||
import { connect } from 'react-redux' |
||||
import { Redirect, Link } from 'react-router-dom' |
||||
|
||||
import HorizontalInput from './parts/HorizontalInput' |
||||
import { updateRoute, createRoute, deleteRoute } from '../actions/route'; |
||||
import store from '../store' |
||||
|
||||
const StyledRouteForm = styled.div` |
||||
padding: 1rem; |
||||
background: #fafafa; |
||||
` |
||||
|
||||
// TODO: need to deal with shapes
|
||||
|
||||
class RouteForm extends Component { |
||||
|
||||
state = { |
||||
id: null, |
||||
agency: null, |
||||
route_id: "", |
||||
short_name: "", |
||||
long_name: "", |
||||
desc: "", |
||||
route_type: "2", |
||||
route_url: "", |
||||
route_color: "", |
||||
route_text_color: "", |
||||
route_sort_order: "0", |
||||
} |
||||
|
||||
constructor() { |
||||
super() |
||||
this.handleChange = this.handleChange.bind(this) |
||||
this.handleSubmit = this.handleSubmit.bind(this) |
||||
this.handleDelete = this.handleDelete.bind(this) |
||||
this.renderForm = this.renderForm.bind(this) |
||||
} |
||||
|
||||
handleChange(evt) { |
||||
let updated = {} |
||||
updated[evt.target.name] = evt.target.value |
||||
this.setState(updated) |
||||
} |
||||
|
||||
handleSubmit() { |
||||
const { id } = this.state |
||||
let body = { ...this.state } |
||||
delete body.id |
||||
delete body.farerule_set |
||||
delete body.trip_set |
||||
delete body.geojson |
||||
if (id !== null) { |
||||
store.dispatch(updateRoute(id, body)) |
||||
} else { |
||||
store.dispatch(createRoute(body)) |
||||
} |
||||
this.setState({justSubmit: true}) |
||||
} |
||||
|
||||
handleDelete() { |
||||
const { id } = this.state |
||||
store.dispatch(deleteRoute(id)) |
||||
this.setState({justSubmit: true}) |
||||
} |
||||
|
||||
componentWillMount() { |
||||
const { props } = this |
||||
const { agencyId, routeId } = props.match.params |
||||
const { results } = props.route |
||||
const ones = results.filter(ele => ele.route_id === routeId) |
||||
if (ones.length > 0) { |
||||
this.setState(ones[0]) |
||||
props.updateBreadcrumb({ agencyId, routeId }) |
||||
} else { |
||||
const agencies = props.agency.results.filter(ele => ele.agency_id === agencyId) |
||||
if (agencies.length > 0) { |
||||
let d = {} |
||||
d["agency_id"] = agencies[0].id |
||||
this.setState(d) |
||||
props.updateBreadcrumb({ agencyId, routeId: 'new' }) |
||||
} |
||||
} |
||||
} |
||||
|
||||
renderForm() { |
||||
const one = this.state |
||||
const { agencyId, routeId } = this.props.match.params |
||||
const { agency } = this.props |
||||
const agencies = agency.results.filter(ele => ele.agency_id === agencyId) |
||||
const { fetching } = this.props.route |
||||
return ( |
||||
<StyledRouteForm> |
||||
<h1 className="title">{one.route_id || 'New Route'} </h1> |
||||
<div className="content"> |
||||
<HorizontalInput |
||||
label="Route ID" |
||||
type="text" |
||||
fieldName="route_id" |
||||
value={one.route_id || ''} |
||||
handleChange={this.handleChange} /> |
||||
|
||||
<HorizontalInput |
||||
label="Short Name" |
||||
type="text" |
||||
fieldName="short_name" |
||||
value={one.short_name || ''} |
||||
handleChange={this.handleChange} /> |
||||
|
||||
<HorizontalInput |
||||
label="Long Name" |
||||
type="text" |
||||
fieldName="long_name" |
||||
value={one.long_name || ''} |
||||
handleChange={this.handleChange} /> |
||||
|
||||
<HorizontalInput |
||||
label="Description" |
||||
type="text" |
||||
fieldName="desc" |
||||
value={one.desc || ''} |
||||
handleChange={this.handleChange} /> |
||||
|
||||
<HorizontalInput |
||||
label="Route Type" |
||||
type="text" |
||||
fieldName="route_type" |
||||
value={one.route_type || ''} |
||||
handleChange={this.handleChange} /> |
||||
|
||||
<HorizontalInput |
||||
label="Route URL" |
||||
type="text" |
||||
fieldName="route_url" |
||||
value={one.route_url || ''} |
||||
handleChange={this.handleChange} /> |
||||
|
||||
<HorizontalInput |
||||
label="Route Color" |
||||
type="text" |
||||
fieldName="route_color" |
||||
value={one.route_color || ''} |
||||
handleChange={this.handleChange} /> |
||||
|
||||
<HorizontalInput |
||||
label="Route Text Color" |
||||
type="text" |
||||
fieldName="route_text_color" |
||||
value={one.route_text_color || ''} |
||||
handleChange={this.handleChange} /> |
||||
|
||||
<HorizontalInput |
||||
label="Route Sort Order" |
||||
type="text" |
||||
fieldName="route_sort_order" |
||||
defaultValue="0" |
||||
value={one.route_sort_order || ''} |
||||
handleChange={this.handleChange} /> |
||||
</div> |
||||
|
||||
<div className="field is-grouped"> |
||||
<div className="control"> |
||||
<button className="button is-link" |
||||
onClick={this.handleSubmit} |
||||
disabled={fetching}> |
||||
Save</button> |
||||
</div> |
||||
{one.id !== null && <div className="control"> |
||||
<button className="button is-danger" |
||||
onClick={this.handleDelete} |
||||
disabled={fetching}> |
||||
DELETE</button> |
||||
</div>} |
||||
<div className="control"> |
||||
{agencies[0] && |
||||
<Link to={`/map/${agencies[0].agency_id}/${routeId ? `route/${routeId}` : ''}`} |
||||
className="button is-text">Cancel</Link>} |
||||
</div> |
||||
</div> |
||||
</StyledRouteForm> |
||||
) |
||||
} |
||||
|
||||
render () { |
||||
const one = this.state |
||||
const { fetching } = this.props.agency |
||||
// redirect to view page if no data
|
||||
const { agencyId } = this.props.match.params |
||||
// redirect to agency list if submitted
|
||||
if (one.justSubmit === true && !fetching) { |
||||
return <Redirect to={`/map/${agencyId}/route/${one.route_id || ''}`} /> |
||||
} |
||||
return this.renderForm() |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
const mapStateToProps = state => ({ |
||||
agency: state.agency, |
||||
route: state.route, |
||||
}) |
||||
|
||||
const connectRouteForm = connect( |
||||
mapStateToProps, |
||||
)(RouteForm) |
||||
export default connectRouteForm |
@ -0,0 +1,198 @@
|
||||
import React, { Component } from 'react' |
||||
import styled from 'styled-components' |
||||
import { connect } from 'react-redux' |
||||
import { Redirect, Link } from 'react-router-dom' |
||||
|
||||
import HorizontalInput from './parts/HorizontalInput' |
||||
import { mapCenterUpdate } from '../actions' |
||||
import { updateStop, createStop, deleteStop } from '../actions/stop' |
||||
import store from '../store' |
||||
|
||||
const StyledStopForm = styled.div` |
||||
padding: 1rem; |
||||
background: #fafafa; |
||||
` |
||||
|
||||
// TODO: need to deal with shapes
|
||||
|
||||
class StopForm extends Component { |
||||
|
||||
state = { |
||||
id: null, |
||||
stop_id: null, |
||||
name: '', |
||||
stop_code: '', |
||||
stop_desc: '', |
||||
zone_id: '', |
||||
location_type: '0', |
||||
stop_timezone: 'Asia/Bangkok', |
||||
wheelchair_boarding: '0', |
||||
// parent_station: null,
|
||||
} |
||||
|
||||
constructor() { |
||||
super() |
||||
this.handleChange = this.handleChange.bind(this) |
||||
this.handleSubmit = this.handleSubmit.bind(this) |
||||
this.handleDelete = this.handleDelete.bind(this) |
||||
this.renderForm = this.renderForm.bind(this) |
||||
} |
||||
|
||||
handleChange(evt) { |
||||
let updated = {} |
||||
updated[evt.target.name] = evt.target.value |
||||
this.setState(updated) |
||||
} |
||||
|
||||
handleSubmit() { |
||||
const { id } = this.state |
||||
let body = { ...this.state } |
||||
delete body.id |
||||
delete body.geojson |
||||
if (id !== null) { |
||||
store.dispatch(updateStop(id, body)) |
||||
} else { |
||||
store.dispatch(createStop(body)) |
||||
} |
||||
this.setState({justSubmit: true}) |
||||
} |
||||
|
||||
handleDelete() { |
||||
const { id } = this.state |
||||
store.dispatch(deleteStop(id)) |
||||
this.setState({justSubmit: true}) |
||||
} |
||||
|
||||
componentWillMount() { |
||||
const { props } = this |
||||
const { stopId } = props.match.params |
||||
const { results } = props.stop |
||||
const ones = results.filter(ele => ele.stop_id === stopId) |
||||
if (ones.length > 0) { |
||||
this.setState(ones[0]) |
||||
props.updateBreadcrumb({ stopId }) |
||||
const { coordinates } = ones[0].geojson |
||||
store.dispatch(mapCenterUpdate(coordinates[1], coordinates[0])) |
||||
} |
||||
} |
||||
|
||||
renderForm() { |
||||
const one = this.state |
||||
const { stopId } = this.props.match.params |
||||
const { results, fetching } = this.props.stop |
||||
const stops = results.filter(ele => ele.stop_id === stopId) |
||||
return ( |
||||
<StyledStopForm> |
||||
<h1 className="title">{one.stop_id || 'New Stop'} </h1> |
||||
<div className="content"> |
||||
<HorizontalInput |
||||
label="Stop ID" |
||||
type="text" |
||||
fieldName="stop_id" |
||||
value={one.stop_id || ''} |
||||
handleChange={this.handleChange} /> |
||||
|
||||
<HorizontalInput |
||||
label="Name" |
||||
type="text" |
||||
fieldName="name" |
||||
value={one.name || ''} |
||||
handleChange={this.handleChange} /> |
||||
|
||||
<HorizontalInput |
||||
label="Stop code" |
||||
type="text" |
||||
fieldName="stop_code" |
||||
value={one.stop_code || ''} |
||||
handleChange={this.handleChange} /> |
||||
|
||||
<HorizontalInput |
||||
label="Description" |
||||
type="text" |
||||
fieldName="stop_desc" |
||||
value={one.stop_desc || ''} |
||||
handleChange={this.handleChange} /> |
||||
|
||||
<HorizontalInput |
||||
label="Zone ID" |
||||
type="text" |
||||
fieldName="zone_id" |
||||
value={one.zone_id || ''} |
||||
handleChange={this.handleChange} /> |
||||
|
||||
<HorizontalInput |
||||
label="Location Type" |
||||
type="text" |
||||
fieldName="location_type" |
||||
value={one.location_type || ''} |
||||
handleChange={this.handleChange} /> |
||||
|
||||
<HorizontalInput |
||||
label="Timezone" |
||||
type="text" |
||||
fieldName="stop_timezone" |
||||
value={one.stop_timezone || ''} |
||||
handleChange={this.handleChange} /> |
||||
|
||||
<HorizontalInput |
||||
label="Wheelchair" |
||||
type="text" |
||||
fieldName="wheelchair_boarding" |
||||
value={one.wheelchair_boarding || ''} |
||||
handleChange={this.handleChange} /> |
||||
|
||||
{/* <HorizontalInput |
||||
label="Parent Station" |
||||
type="text" |
||||
fieldName="parent_station" |
||||
defaultValue="0" |
||||
value={one.parent_station || ''} |
||||
handleChange={this.handleChange} /> */} |
||||
</div> |
||||
|
||||
<div className="field is-grouped"> |
||||
<div className="control"> |
||||
<button className="button is-link" |
||||
onClick={this.handleSubmit} |
||||
disabled={fetching}> |
||||
Save</button> |
||||
</div> |
||||
{one.id !== null && <div className="control"> |
||||
<button className="button is-danger" |
||||
onClick={this.handleDelete} |
||||
disabled={fetching}> |
||||
DELETE</button> |
||||
</div>} |
||||
<div className="control"> |
||||
{results && |
||||
<Link to={`/map/stop`} |
||||
className="button is-text">Cancel</Link>} |
||||
</div> |
||||
</div> |
||||
</StyledStopForm> |
||||
) |
||||
} |
||||
|
||||
render () { |
||||
const one = this.state |
||||
const { fetching } = this.props.stop |
||||
// redirect to view page if no data
|
||||
const { stopId } = this.props.match.params |
||||
// redirect to agency list if submitted
|
||||
if (one.justSubmit === true && !fetching) { |
||||
return <Redirect to={`/map/stop`} /> |
||||
} |
||||
return this.renderForm() |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
const mapStateToProps = state => ({ |
||||
stop: state.stop, |
||||
}) |
||||
|
||||
const connectStopForm = connect( |
||||
mapStateToProps, |
||||
)(StopForm) |
||||
export default connectStopForm |
@ -0,0 +1,67 @@
|
||||
import React, { Component } from 'react' |
||||
import styled from 'styled-components' |
||||
import { connect } from 'react-redux' |
||||
import { Link } from 'react-router-dom' |
||||
|
||||
import { getStop } from '../actions/stop' |
||||
import store from '../store' |
||||
|
||||
const StyledBox = styled.div` |
||||
background: #fafafa; |
||||
` |
||||
|
||||
|
||||
class StopList extends Component { |
||||
|
||||
componentWillMount() { |
||||
const { count } = this.props.stop |
||||
if (count === 0) |
||||
store.dispatch(getStop()) |
||||
} |
||||
|
||||
render() { |
||||
const { results } = this.props.stop |
||||
return ( |
||||
<StyledBox> |
||||
<nav className="panel"> |
||||
<p className="panel-heading"> |
||||
Stop |
||||
</p> |
||||
<p className="panel-tabs"> |
||||
<Link className="link is-info" to={`/map/stop/new`}> |
||||
<i className="fas fa-plus" /> New stop |
||||
</Link> |
||||
</p> |
||||
<div className="panel-block"> |
||||
<p className="control has-icons-left"> |
||||
<input className="input is-small" type="text" placeholder="search" /> |
||||
<span className="icon is-small is-left"> |
||||
<i className="fas fa-search" aria-hidden="true"></i> |
||||
</span> |
||||
</p> |
||||
</div> |
||||
{results.map(ele => ( |
||||
<span key={ele.id} className="panel-block"> |
||||
<Link to={`/map/stop/${ele.stop_id}`}>{ele.stop_id} - {ele.name}</Link> |
||||
</span> |
||||
))} |
||||
{this.props.stop.length === 0 && <span key="empty-farerule" className="panel-block"> |
||||
No stop yet</span>} |
||||
</nav> |
||||
</StyledBox> |
||||
) |
||||
} |
||||
} |
||||
|
||||
const mapStateToProps = state => ({ |
||||
stop: state.stop |
||||
}) |
||||
const connectStopList = connect( |
||||
mapStateToProps, |
||||
{}, |
||||
)(StopList) |
||||
|
||||
export default styled(connectStopList)` |
||||
color: palevioletred; |
||||
font-weight: bold; |
||||
` |
@ -0,0 +1,76 @@
|
||||
import { |
||||
STOP_CREATE, STOP_DELETE, STOP_UPDATE, |
||||
STOP_REQUEST, STOP_SUCCESS, STOP_FAILURE, |
||||
} from '../constants/ActionTypes' |
||||
|
||||
|
||||
const stopInitState = { |
||||
results: [], |
||||
next: null, |
||||
count: 0, |
||||
fetching: false, |
||||
} |
||||
const stop = (state = stopInitState, action) => { |
||||
switch(action.type) { |
||||
case STOP_REQUEST: |
||||
return { |
||||
...state, |
||||
fetching: true, |
||||
} |
||||
case STOP_SUCCESS: |
||||
const { count, next, prev, results } = action.payload |
||||
return { |
||||
...state, |
||||
fetching: false, |
||||
count, |
||||
next, |
||||
results: [ |
||||
...(prev ? state.results : []), |
||||
...results, |
||||
] |
||||
} |
||||
case STOP_UPDATE: |
||||
const { id } = action.payload |
||||
const oldResults = state.results |
||||
const targetInd = oldResults.findIndex(ele => ele.id === id) |
||||
return { |
||||
...state, |
||||
fetching: false, |
||||
results: [ |
||||
...oldResults.slice(0, targetInd), |
||||
action.payload, |
||||
...oldResults.slice(targetInd + 1) |
||||
] |
||||
} |
||||
case STOP_CREATE: |
||||
return { |
||||
...state, |
||||
fetching: false, |
||||
count: state.count + 1, |
||||
results: [ |
||||
...state.results, |
||||
action.payload, |
||||
] |
||||
} |
||||
case STOP_DELETE: |
||||
const deleteInd = state.results.findIndex(ele => ele.id === action.meta.id) |
||||
return { |
||||
...state, |
||||
count: state.count - 1, |
||||
fetching: false, |
||||
results: [ |
||||
...state.results.slice(0, deleteInd), |
||||
...state.results.slice(deleteInd + 1) |
||||
] |
||||
} |
||||
case STOP_FAILURE: |
||||
return { |
||||
...state, |
||||
fetching: false, |
||||
} |
||||
default: |
||||
return state; |
||||
} |
||||
} |
||||
|
||||
export default stop |
Loading…
Reference in new issue