diff --git a/src/actions/frequency.js b/src/actions/frequency.js
new file mode 100644
index 0000000..5731a6a
--- /dev/null
+++ b/src/actions/frequency.js
@@ -0,0 +1,67 @@
+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 getFrequency = (query) => ({
+ [RSAA]: {
+ endpoint: `${API_URL}/frequency/?${query || ''}`,
+ method: 'GET',
+ headers: RSAAHeaders,
+ bailout: (state) => state.frequency.fetching || state.frequency.query === query,
+ types: [
+ {
+ type: types.FREQUENCY_REQUEST,
+ meta: { query: query },
+ },
+ types.FREQUENCY_SUCCESS,
+ types.FREQUENCY_FAILURE,
+ ]
+ }
+})
+
+export const updateFrequency = (id, body) => ({
+ [RSAA]: {
+ endpoint: `${API_URL}/frequency/${id}/`,
+ body: JSON.stringify(body),
+ method: 'PATCH',
+ headers: RSAAHeaders,
+ types: [
+ types.FREQUENCY_REQUEST,
+ types.FREQUENCY_UPDATE,
+ types.FREQUENCY_FAILURE,
+ ]
+ }
+})
+
+export const createFrequency = (body) => ({
+ [RSAA]: {
+ endpoint: `${API_URL}/frequency/`,
+ body: JSON.stringify(body),
+ method: 'POST',
+ headers: RSAAHeaders,
+ types: [
+ types.FREQUENCY_REQUEST,
+ types.FREQUENCY_CREATE,
+ types.FREQUENCY_FAILURE,
+ ]
+ }
+})
+
+export const deleteFrequency = (id) => ({
+ [RSAA]: {
+ endpoint: `${API_URL}/frequency/${id}/`,
+ method: 'DELETE',
+ headers: RSAAHeaders,
+ types: [
+ types.FREQUENCY_REQUEST,
+ {
+ type: types.FREQUENCY_DELETE,
+ meta: { id }
+ },
+ types.FREQUENCY_FAILURE,
+ ]
+ }
+})
diff --git a/src/actions/index.js b/src/actions/index.js
index 889a7b8..7f2e8f6 100644
--- a/src/actions/index.js
+++ b/src/actions/index.js
@@ -97,11 +97,16 @@ export const polygonReset = () => {
}
}
-export const lastCenterUpdate = (lat, lon) => {
+export const lastCenterUpdate = (lat, lon, zoom) => {
// this only update current map center from map movement alone
+ let payload = {
+ lastCenter: [lat, lon]
+ }
+ if (zoom !== undefined)
+ payload['zoom'] = zoom
return {
type: types.GEO_LASTCENTER_UPDATE,
- payload: [lat, lon],
+ payload,
}
}
diff --git a/src/actions/stop.js b/src/actions/stop.js
index ad77118..5236450 100644
--- a/src/actions/stop.js
+++ b/src/actions/stop.js
@@ -33,6 +33,23 @@ export const updateStop = (id, body) => ({
}
})
+export const mergeStop = (id, body) => ({
+ [RSAA]: {
+ endpoint: `${API_URL}/stop/${id}/merge/`,
+ body: JSON.stringify(body),
+ method: 'POST',
+ headers: RSAAHeaders,
+ types: [
+ types.STOP_REQUEST,
+ {
+ type: types.STOP_UPDATE,
+ meta: { id, opt: 'merge', mergeWith: body }
+ },
+ types.STOP_FAILURE,
+ ]
+ }
+})
+
export const createStop = (body) => ({
[RSAA]: {
endpoint: `${API_URL}/stop/`,
diff --git a/src/components/FrequencyForm.js b/src/components/FrequencyForm.js
new file mode 100644
index 0000000..aabe446
--- /dev/null
+++ b/src/components/FrequencyForm.js
@@ -0,0 +1,126 @@
+import React, { Component } from 'react'
+import styled from 'styled-components'
+
+import Input from './parts/Input'
+import Select from './parts/Select'
+import {
+ updateFrequency, createFrequency, deleteFrequency
+} from '../actions/frequency'
+import store from '../store'
+import {
+ ExactTimeChoices
+} from '../constants/choices'
+import { getItemFromList } from '../utils'
+
+const StyleBox = styled.div`
+padding: 5px;
+background: white;
+margin-bottom: 1rem;
+`
+
+class FrequencyForm extends Component {
+
+ cancel = null
+ state = {
+ editMode: false,
+ trip: null,
+ id: null,
+ }
+
+ constructor(props) {
+ super(props)
+ this.handleChange = this.handleChange.bind(this)
+ this.handleSubmit = this.handleSubmit.bind(this)
+ this.handleDelete = this.handleDelete.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
+ if (id !== null) {
+ store.dispatch(updateFrequency(id, body))
+ } else {
+ store.dispatch(createFrequency(body))
+ }
+ this.props.toggleEditMode()
+ }
+
+ handleDelete() {
+ const { id } = this.state
+ store.dispatch(deleteFrequency(id))
+ this.props.toggleEditMode()
+ }
+
+ static getDerivedStateFromProps(props, state) {
+ if (props.item && props.item.id !== null && state.id === null) {
+ return props.item
+ } else if (props.trip !== undefined) {
+ return { trip: props.trip }
+ }
+ return null
+ }
+
+ render() {
+ const item = this.state
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ {item.id !== null &&
+
+
}
+ {this.props.toggleEditMode &&
+
}
+
+
+ )
+ }
+
+}
+
+export default FrequencyForm
diff --git a/src/components/FrequencyOne.js b/src/components/FrequencyOne.js
new file mode 100644
index 0000000..628799c
--- /dev/null
+++ b/src/components/FrequencyOne.js
@@ -0,0 +1,73 @@
+import React, { Component } from 'react'
+import styled from 'styled-components'
+
+import FrequencyForm from './FrequencyForm'
+import { ExactTimeChoices } from '../constants/choices'
+import { getItemFromList } from '../utils'
+
+const StyledRow = styled.div`
+padding-top: 5px;
+padding-bottom: 5px;
+background: white;
+margin-bottom: 1rem;
+`
+
+class FrequencyOne extends Component {
+
+ state = {
+ editMode: false
+ }
+
+ constructor(props) {
+ super(props)
+ this.toggleEditMode = this.toggleEditMode.bind(this)
+ }
+
+ toggleEditMode() {
+ this.setState({editMode: !this.state.editMode})
+ }
+
+ renderReadOnly = (item) => (
+
+
+
+
+
End Time
+
{item.end_time}
+
+
+
+
+
Headway secs
+
{item.headway_secs}
+
+
+
+
+
Exact Times
+
{getItemFromList(item.exact_times, ExactTimeChoices).value}
+
+
+
+ )
+
+ render() {
+ const { item, tripId } = this.props
+ if (this.state.editMode)
+ return
+ return this.renderReadOnly(item)
+ }
+
+}
+
+export default FrequencyOne
diff --git a/src/components/StopForm.js b/src/components/StopForm.js
index b8be9fe..ea4041c 100644
--- a/src/components/StopForm.js
+++ b/src/components/StopForm.js
@@ -2,24 +2,36 @@ import React, { Component } from 'react'
import styled from 'styled-components'
import { connect } from 'react-redux'
import { Redirect, Link } from 'react-router-dom'
+import AsyncSelect from 'react-select/lib/Async'
+import { components } from 'react-select'
import Input from './parts/Input'
import Select from './parts/Select'
import {
mapCenterUpdate, draggableMarkerEnable, draggableMarkerDisable
} from '../actions'
-import { updateStop, createStop, deleteStop, getStop } from '../actions/stop'
+import { updateStop, createStop, deleteStop, getStop, mergeStop } from '../actions/stop'
import store from '../store'
import {
StopLocationTypes, StopWheelChairInfo
} from '../constants/choices'
-import { getItemFromList } from '../utils'
+import { getItemFromList, getStopsAsyncSelect } from '../utils'
const StyledStopForm = styled.div`
padding: 1rem;
background: #fafafa;
`
+const Option = (props) => {
+ const { stop_id, name, stop_desc } = props.data
+ return (
+
+ {stop_id}
{name}
+ {stop_desc.length > 0 &&
{stop_desc}}
+
+ )
+}
+
// TODO: need to deal with shapes
class StopForm extends Component {
@@ -35,6 +47,7 @@ class StopForm extends Component {
stop_timezone: 'Asia/Bangkok',
wheelchair_boarding: '0',
latlon: [],
+ mergeWith: null,
// parent_station: null,
}
@@ -42,6 +55,7 @@ class StopForm extends Component {
super()
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
+ this.handleMerge = this.handleMerge.bind(this)
this.handleDelete = this.handleDelete.bind(this)
this.renderForm = this.renderForm.bind(this)
this.setToCurrentLocation = this.setToCurrentLocation.bind(this)
@@ -62,6 +76,8 @@ class StopForm extends Component {
delete body.id
delete body.geojson
delete body.latlon
+ // this is for merger only
+ delete body.mergeWith
if (id !== null) {
store.dispatch(updateStop(id, body))
} else {
@@ -70,6 +86,15 @@ class StopForm extends Component {
this.setState({justSubmit: true})
}
+ handleMerge() {
+ const { id } = this.state
+ let body = {
+ stop: this.state.mergeWith,
+ }
+ store.dispatch(mergeStop(id, body))
+ this.setState({justSubmit: true})
+ }
+
handleDelete() {
const { id } = this.state
store.dispatch(deleteStop(id))
@@ -244,6 +269,38 @@ class StopForm extends Component {
className="button is-text">Cancel}
+
+
+
+
+
{
+ if (evt.action === 'select-option') {
+ let evt = {
+ target: {
+ name: 'mergeWith',
+ value: item,
+ }}
+ this.handleChange(evt)
+ }
+ }}
+ />
+
+
+
+
)
}
diff --git a/src/components/StopTimeForm.js b/src/components/StopTimeForm.js
index 1753d3e..bd33a5d 100644
--- a/src/components/StopTimeForm.js
+++ b/src/components/StopTimeForm.js
@@ -2,7 +2,6 @@ import React, { Component } from 'react'
import styled from 'styled-components'
import AsyncSelect from 'react-select/lib/Async'
import { components } from 'react-select'
-import { CancelToken } from 'axios'
import Input from './parts/Input'
import OurSelect from './parts/Select'
@@ -13,8 +12,7 @@ import store from '../store'
import {
PickUpTypes, DropOffTypes, TimePointChoices
} from '../constants/choices'
-import { getItemFromList } from '../utils'
-import { apiClient } from '../utils/ApiClient'
+import { getItemFromList, getStopsAsyncSelect } from '../utils'
const StyleBox = styled.div`
padding: 5px;
@@ -23,7 +21,6 @@ margin-bottom: 1rem;
`
const Option = (props) => {
- console.log('option: ', props)
const { stop_id, name, stop_desc } = props.data
return (
@@ -83,23 +80,6 @@ class StopTimeForm extends Component {
return null
}
- getStops = (inputValue, callback) => {
- const that = this
- const cancelToken = new CancelToken(function executor(c) {
- // An executor function receives a cancel function as a parameter
- if (that.cancel)
- that.cancel()
- that.cancel = c
- })
- apiClient(`/stop/?search=${inputValue}`, { cancelToken })
- .then((resp) => {
- callback(resp.data.results.map(i => ({
- ...i,
- label: i.name
- })))
- })
- }
-
render() {
const item = this.state
return (
@@ -117,7 +97,7 @@ class StopTimeForm extends Component {
cacheOptions={true}
defaultOptions
defaultValue={item.stop && {...item.stop, label: item.stop.name}}
- loadOptions={this.getStops}
+ loadOptions={getStopsAsyncSelect}
components={{ Option }}
onChange={(item, evt) => {
if (evt.action === 'select-option') {
diff --git a/src/components/TripForm.js b/src/components/TripForm.js
index 370d836..a30c7fe 100644
--- a/src/components/TripForm.js
+++ b/src/components/TripForm.js
@@ -4,13 +4,17 @@ import { connect } from 'react-redux'
import { Redirect, Link } from 'react-router-dom'
import StopTimeOne from './StopTimeOne'
+import FrequencyOne from './FrequencyOne'
import StopTimeForm from './StopTimeForm'
+import FrequencyForm from './FrequencyForm'
import HorizontalInput from './parts/HorizontalInput'
import HorizontalSelect from './parts/HorizontalSelect'
import { updateTrip, createTrip, deleteTrip } from '../actions/trip'
import { getCalendar } from '../actions/calendar'
import { getRoute } from '../actions'
+import { getTrip } from '../actions/trip'
import { getStopTime } from '../actions/stoptime'
+import { getFrequency } from '../actions/frequency'
import store from '../store'
import {
DirectionChoices, WheelChairAccessibles,
@@ -37,6 +41,7 @@ class TripForm extends Component {
service: null,
frequency_set: [],
newStopTime: false,
+ newFrequency: false,
}
constructor() {
@@ -51,6 +56,11 @@ class TripForm extends Component {
this.setState({ newStopTime: !this.state.newStopTime })
}
+
+ toggleNewFrequency = () => {
+ this.setState({ newFrequency: !this.state.newFrequency })
+ }
+
handleChange(evt) {
let updated = {}
updated[evt.target.name] = evt.target.value
@@ -90,16 +100,14 @@ class TripForm extends Component {
}
}
if (tripId !== undefined && state.id === null) {
- const { route } = props
- const tRoute = route.results.filter(ele => ele.route_id === routeId)
- if (tRoute.length > 0) {
- const trips = tRoute[0].trip_set.filter(ele => ele.trip_id === tripId)
- if (trips.length > 0) {
- store.dispatch(getStopTime(`trip=${trips[0].id}&limit=100`))
- return trips[0]
- }
+ const { trip } = props
+ const matches = trip.results.filter(ele => ele.trip_id === tripId)
+ if (matches.length > 0) {
+ store.dispatch(getStopTime(`trip=${matches[0].id}&limit=100`))
+ store.dispatch(getFrequency(`trip=${matches[0].id}`))
+ return matches[0]
} else {
- store.dispatch(getRoute(`agency=${agencyId}`))
+ store.dispatch(getTrip(`route=${routeId}`))
}
}
return null
@@ -215,24 +223,20 @@ class TripForm extends Component {
render () {
const one = this.state
+ const { frequency } = this.props
const { fetching } = this.props.trip
// redirect to view page if no data
const { agencyId, routeId } = this.props.match.params
- // this is a create form
- // if (tripId === undefined) {
- // if (one.justSubmit === true && !fetching) {
- // return
- // }
- // return this.renderForm()
- // }
-
- // if (one.id === null && tripId.length > 0)
- // return
+ let freq = (one.frequency_set.length > 0) ? one.frequency_set[0] : null
+ if (freq === null && frequency.count > 0) {
+ freq = frequency.results[0]
+ }
// redirect to trip list if submitted
if (one.justSubmit === true && !fetching) {
return
}
+
let StopTimePane = null
if (this.state.id !== null) {
StopTimePane = (
@@ -253,6 +257,13 @@ class TripForm extends Component {
{this.renderForm()}
+
+ {!this.state.newFrequency && }
+ {this.state.newFrequency && }
+ {frequency.results.map(
+ ele => )}
{StopTimePane}
@@ -266,6 +277,7 @@ const mapStateToProps = state => ({
trip: state.trip,
calendar: state.calendar,
stoptime: state.stoptime,
+ frequency: state.frequency,
})
const connectTripForm = connect(
diff --git a/src/constants/ActionTypes.js b/src/constants/ActionTypes.js
index 4c01957..daa9403 100644
--- a/src/constants/ActionTypes.js
+++ b/src/constants/ActionTypes.js
@@ -70,6 +70,13 @@ export const CALENDAR_CREATE = 'CALENDAR_CREATE'
export const CALENDAR_DELETE = 'CALENDAR_DELETE'
export const CALENDAR_UPDATE = 'CALENDAR_UPDATE'
+export const FREQUENCY_REQUEST = 'FREQUENCY_REQUEST'
+export const FREQUENCY_FAILURE = 'FREQUENCY_FAILURE'
+export const FREQUENCY_SUCCESS = 'FREQUENCY_SUCCESS'
+// below items are SUCCESS for other tasks
+export const FREQUENCY_CREATE = 'FREQUENCY_CREATE'
+export const FREQUENCY_DELETE = 'FREQUENCY_DELETE'
+export const FREQUENCY_UPDATE = 'FREQUENCY_UPDATE'
export const FAREATTR_REQUEST = 'FAREATTR_REQUEST'
export const FAREATTR_FAILURE = 'FAREATTR_FAILURE'
diff --git a/src/constants/choices.js b/src/constants/choices.js
index 5805655..583d483 100644
--- a/src/constants/choices.js
+++ b/src/constants/choices.js
@@ -58,3 +58,7 @@ export const BikeAllowanceChoices = [
{ value: '2', label: 'Not possible' },
]
+export const ExactTimeChoices = [
+ { value: '0', label: 'Not exactly scheduled.' },
+ { value: '1', label: 'Exactly scheduled' },
+]
diff --git a/src/container/Geo.js b/src/container/Geo.js
index 86cd9c3..0a25409 100644
--- a/src/container/Geo.js
+++ b/src/container/Geo.js
@@ -276,14 +276,15 @@ class Geo extends Component {