Compare commits
30 Commits
Author | SHA1 | Date |
---|---|---|
sipp11 | 0cfcac439d | 6 years ago |
sipp11 | f4f1fd10fc | 6 years ago |
sipp11 | 4a87a75674 | 6 years ago |
sipp11 | cd9cc45901 | 6 years ago |
sipp11 | d7d34127a1 | 6 years ago |
sipp11 | 97ff4c0320 | 6 years ago |
sipp11 | 30288f60e3 | 6 years ago |
sipp11 | 8d311d5070 | 6 years ago |
sipp11 | 37410ee90d | 6 years ago |
sipp11 | 0de7cc7309 | 6 years ago |
sipp11 | b2645ae296 | 6 years ago |
sipp11 | 27eff5d615 | 6 years ago |
sipp11 | 4d38652eb4 | 6 years ago |
sipp11 | d8286004a4 | 6 years ago |
sipp11 | 2124d7d121 | 6 years ago |
sipp11 | b6145d4841 | 6 years ago |
sipp11 | 277fdde5b1 | 6 years ago |
sipp11 | 2bfec7f796 | 6 years ago |
sipp11 | 7d4bcbd30c | 6 years ago |
sipp11 | bcb837c19c | 6 years ago |
sipp11 | 229627749a | 6 years ago |
sipp11 | a7ee6a9e6d | 6 years ago |
sipp11 | 0d57bcbd5f | 6 years ago |
sipp11 | 65b5c9398e | 6 years ago |
sipp11 | e80c0a1b1a | 6 years ago |
sipp11 | a8183f31a4 | 6 years ago |
sipp11 | 59e257c2be | 6 years ago |
sipp11 | d1ae3c5349 | 6 years ago |
sipp11 | 72dafd63fc | 6 years ago |
sipp11 | 23d1df270a | 6 years ago |
59 changed files with 2345 additions and 315 deletions
@ -1,5 +1,8 @@
|
||||
{ |
||||
"todo-tree.flat": true, |
||||
"todo-tree.flat": false, |
||||
"todo-tree.grouped": false, |
||||
"todo-tree.expanded": true |
||||
"todo-tree.expanded": true, |
||||
"workbench.colorCustomizations": { |
||||
"titleBar.activeBackground": "#e2c34a" |
||||
} |
||||
} |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 483 B |
@ -0,0 +1,139 @@
|
||||
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 getFareAttr = (query) => ({ |
||||
[RSAA]: { |
||||
endpoint: (state) => ( |
||||
query === 'next' ? |
||||
state.next : |
||||
`${API_URL}/fare-attribute/?${query || ''}` |
||||
), |
||||
method: 'GET', |
||||
headers: RSAAHeaders, |
||||
bailout: (state) => state.fareattr.fetching || ( |
||||
state.fareattr.query !== undefined && state.fareattr.query === query), |
||||
types: [ |
||||
{ |
||||
type: types.FAREATTR_REQUEST, |
||||
meta: { query: query || '' }, |
||||
}, |
||||
types.FAREATTR_SUCCESS, |
||||
types.FAREATTR_FAILURE, |
||||
] |
||||
} |
||||
}) |
||||
|
||||
export const updateFareAttr = (id, body) => ({ |
||||
[RSAA]: { |
||||
endpoint: `${API_URL}/fare-attribute/${id}/`, |
||||
body: JSON.stringify(body), |
||||
method: 'PATCH', |
||||
headers: RSAAHeaders, |
||||
types: [ |
||||
types.FAREATTR_REQUEST, |
||||
types.FAREATTR_UPDATE, |
||||
types.FAREATTR_FAILURE, |
||||
] |
||||
} |
||||
}) |
||||
|
||||
export const createFareAttr = (body) => ({ |
||||
[RSAA]: { |
||||
endpoint: `${API_URL}/fare-attribute/`, |
||||
body: JSON.stringify(body), |
||||
method: 'POST', |
||||
headers: RSAAHeaders, |
||||
types: [ |
||||
types.FAREATTR_REQUEST, |
||||
types.FAREATTR_CREATE, |
||||
types.FAREATTR_FAILURE, |
||||
] |
||||
} |
||||
}) |
||||
|
||||
export const deleteFareAttr = (id) => ({ |
||||
[RSAA]: { |
||||
endpoint: `${API_URL}/fare-attribute/${id}/`, |
||||
method: 'DELETE', |
||||
headers: RSAAHeaders, |
||||
types: [ |
||||
types.FAREATTR_REQUEST, |
||||
{ |
||||
type: types.FAREATTR_DELETE, |
||||
meta: { id } |
||||
}, |
||||
types.FAREATTR_FAILURE, |
||||
] |
||||
} |
||||
}) |
||||
|
||||
|
||||
export const getFareRule = (query) => ({ |
||||
[RSAA]: { |
||||
endpoint: (state) => ( |
||||
query === 'next' ? |
||||
state.farerule.next : |
||||
`${API_URL}/fare-rule/?${query || ''}` |
||||
), |
||||
method: 'GET', |
||||
headers: RSAAHeaders, |
||||
bailout: (state) => state.farerule.fetching || ( |
||||
state.farerule.query !== undefined && state.farerule.query === query), |
||||
types: [ |
||||
{ |
||||
type: types.FARERULE_REQUEST, |
||||
meta: { query: query || '' }, |
||||
}, |
||||
types.FARERULE_SUCCESS, |
||||
types.FARERULE_FAILURE, |
||||
] |
||||
} |
||||
}) |
||||
|
||||
export const updateFareRule = (id, body) => ({ |
||||
[RSAA]: { |
||||
endpoint: `${API_URL}/fare-rule/${id}/`, |
||||
body: JSON.stringify(body), |
||||
method: 'PATCH', |
||||
headers: RSAAHeaders, |
||||
types: [ |
||||
types.FARERULE_REQUEST, |
||||
types.FARERULE_UPDATE, |
||||
types.FARERULE_FAILURE, |
||||
] |
||||
} |
||||
}) |
||||
|
||||
export const createFareRule = (body) => ({ |
||||
[RSAA]: { |
||||
endpoint: `${API_URL}/fare-rule/`, |
||||
body: JSON.stringify(body), |
||||
method: 'POST', |
||||
headers: RSAAHeaders, |
||||
types: [ |
||||
types.FARERULE_REQUEST, |
||||
types.FARERULE_CREATE, |
||||
types.FARERULE_FAILURE, |
||||
] |
||||
} |
||||
}) |
||||
|
||||
export const deleteFareRule = (id) => ({ |
||||
[RSAA]: { |
||||
endpoint: `${API_URL}/fare-rule/${id}/`, |
||||
method: 'DELETE', |
||||
headers: RSAAHeaders, |
||||
types: [ |
||||
types.FARERULE_REQUEST, |
||||
{ |
||||
type: types.FARERULE_DELETE, |
||||
meta: { id } |
||||
}, |
||||
types.FARERULE_FAILURE, |
||||
] |
||||
} |
||||
}) |
@ -1,67 +0,0 @@
|
||||
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 getFareAttr = (query) => ({ |
||||
[RSAA]: { |
||||
endpoint: `${API_URL}/fare-attribute/?${query || ''}`, |
||||
method: 'GET', |
||||
headers: RSAAHeaders, |
||||
bailout: (state) => state.fareattr.fetching || state.fareattr.query === query, |
||||
types: [ |
||||
{ |
||||
type: types.FAREATTR_REQUEST, |
||||
meta: { query: query }, |
||||
}, |
||||
types.FAREATTR_SUCCESS, |
||||
types.FAREATTR_FAILURE, |
||||
] |
||||
} |
||||
}) |
||||
|
||||
export const updateFareAttr = (id, body) => ({ |
||||
[RSAA]: { |
||||
endpoint: `${API_URL}/fare-attribute/${id}/`, |
||||
body: JSON.stringify(body), |
||||
method: 'PATCH', |
||||
headers: RSAAHeaders, |
||||
types: [ |
||||
types.FAREATTR_REQUEST, |
||||
types.FAREATTR_UPDATE, |
||||
types.FAREATTR_FAILURE, |
||||
] |
||||
} |
||||
}) |
||||
|
||||
export const createFareAttr = (body) => ({ |
||||
[RSAA]: { |
||||
endpoint: `${API_URL}/fare-attribute/`, |
||||
body: JSON.stringify(body), |
||||
method: 'POST', |
||||
headers: RSAAHeaders, |
||||
types: [ |
||||
types.FAREATTR_REQUEST, |
||||
types.FAREATTR_CREATE, |
||||
types.FAREATTR_FAILURE, |
||||
] |
||||
} |
||||
}) |
||||
|
||||
export const deleteFareAttr = (id) => ({ |
||||
[RSAA]: { |
||||
endpoint: `${API_URL}/fare-attribute/${id}/`, |
||||
method: 'DELETE', |
||||
headers: RSAAHeaders, |
||||
types: [ |
||||
types.FAREATTR_REQUEST, |
||||
{ |
||||
type: types.FAREATTR_DELETE, |
||||
meta: { id } |
||||
}, |
||||
types.FAREATTR_FAILURE, |
||||
] |
||||
} |
||||
}) |
@ -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, |
||||
] |
||||
} |
||||
}) |
@ -0,0 +1,148 @@
|
||||
import React, { Component } from 'react' |
||||
import styled from 'styled-components' |
||||
import { connect } from 'react-redux' |
||||
import { Redirect, Link } from 'react-router-dom' |
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' |
||||
import VisibilitySensor from 'react-visibility-sensor' |
||||
|
||||
import { |
||||
PaymentMethodChoices, TransferChoices |
||||
} from '../constants/choices' |
||||
import { getItemFromList } from '../utils' |
||||
import { getFareAttr, getFareRule } from '../actions/fare' |
||||
import store from '../store' |
||||
import { FareRulesOne } from './FareRulesDetail' |
||||
import Spinner from './Spinner' |
||||
|
||||
const StyledFareAttributesDetail = styled.div` |
||||
padding: 1rem; |
||||
background: #fafafa; |
||||
` |
||||
|
||||
const FakeRow = styled.nav` |
||||
padding-top: 5px; |
||||
padding-bottom: 5px; |
||||
background: white; |
||||
margin-bottom: 1rem; |
||||
` |
||||
|
||||
export const FareAttributesOne = (props) => { |
||||
const { item, edit } = props |
||||
const payTxt = getItemFromList(item.payment_method, PaymentMethodChoices, '') |
||||
const transferTxt = getItemFromList(item.transfer, TransferChoices, '') |
||||
|
||||
return ( |
||||
<FakeRow className="level panel" key={item.fare_id}> |
||||
<div className="level-item has-text-centered"> |
||||
<div> |
||||
<p className="heading">Fare ID</p> |
||||
<Link to={`/fare/attributes/${item.id}${edit ? '/edit' : ''}`}>{item.fare_id}</Link> |
||||
</div> |
||||
</div> |
||||
<div className="level-item has-text-centered"> |
||||
<div> |
||||
<p className="heading">Price</p> |
||||
<p key={item.id}>{item.price} <small>{item.currency_type}</small> |
||||
<br /> |
||||
{payTxt && payTxt.label} |
||||
</p> |
||||
</div> |
||||
</div> |
||||
<div className="level-item has-text-centered"> |
||||
<div> |
||||
<p className="heading">Transfer / sec</p> |
||||
<p>{transferTxt && transferTxt.label} / {item.transfer_duration}</p> |
||||
</div> |
||||
</div> |
||||
<div className="level-item has-text-centered"> |
||||
<div> |
||||
<p className="heading">Agency</p> |
||||
<Link to={`/agency/${item.agency.agency_id}`} >{item.agency.name}</Link> |
||||
</div> |
||||
</div> |
||||
</FakeRow> |
||||
) |
||||
} |
||||
|
||||
|
||||
class FareAttributesDetail extends Component { |
||||
state = { |
||||
fetching: true |
||||
} |
||||
|
||||
constructor() { |
||||
super() |
||||
this.renderItem = this.renderItem.bind(this) |
||||
} |
||||
|
||||
componentWillMount() { |
||||
const { props } = this |
||||
const { attribID } = props.match.params |
||||
const { results } = props.fareattr |
||||
const ones = results.filter(ele => ele.id === +attribID) |
||||
store.dispatch(getFareRule(`fareattr=${attribID}`)) |
||||
if (ones.length > 0) { |
||||
this.setState({fetching: false}) |
||||
} else { |
||||
store.dispatch(getFareAttr()) |
||||
this.setState({fetching: true}) |
||||
} |
||||
} |
||||
|
||||
componentWillReceiveProps(nextProps) { |
||||
if (this.props.fareattr.fetching && !nextProps.fareattr.fetching) { |
||||
this.setState({fetching: false}) |
||||
} |
||||
} |
||||
|
||||
renderItem(one) { |
||||
return ( |
||||
<StyledFareAttributesDetail> |
||||
<FareAttributesOne item={one} edit={true} /> |
||||
</StyledFareAttributesDetail> |
||||
) |
||||
} |
||||
|
||||
handleFetchMoreRules() { |
||||
store.dispatch(getFareRule('next')) |
||||
} |
||||
|
||||
render () { |
||||
const { fetching, results } = this.props.fareattr |
||||
const { farerule } = this.props |
||||
// redirect to view page if no data
|
||||
const { attribID } = this.props.match.params |
||||
const ones = results.filter(ele => ele.id === +attribID) |
||||
if (this.state.fetching || fetching) |
||||
return <Spinner show /> |
||||
|
||||
if (ones.length < 1) |
||||
return <Redirect to='/fare' /> |
||||
|
||||
return ( |
||||
<div> |
||||
{this.renderItem(ones[0])} |
||||
<Link className="link is-info" to={`/fare/rules/new?fareattr=${ones[0].id}`}> |
||||
<FontAwesomeIcon icon="plus" /> New fare rule |
||||
</Link> |
||||
{farerule.results && farerule.results.map(ele => <FareRulesOne key={`ff-${ele.id}`} item={ele} />)} |
||||
<Spinner show={farerule.fetching} /> |
||||
{(!farerule.fetching && farerule.results.length > 10 && farerule.next) && <VisibilitySensor onChange={this.handleFetchMoreRules}> |
||||
<div>More</div> |
||||
</VisibilitySensor>} |
||||
</div> |
||||
) |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
const mapStateToProps = state => ({ |
||||
fareattr: state.fareattr, |
||||
farerule: state.farerule, |
||||
}) |
||||
|
||||
const connectFareAttributesDetail = connect( |
||||
mapStateToProps, |
||||
)(FareAttributesDetail) |
||||
export default connectFareAttributesDetail |
@ -0,0 +1,222 @@
|
||||
import React, { Component } from 'react' |
||||
import styled from 'styled-components' |
||||
import { connect } from 'react-redux' |
||||
import { Redirect, Link } from 'react-router-dom' |
||||
|
||||
import { |
||||
updateFareAttr, |
||||
createFareAttr, |
||||
deleteFareAttr |
||||
} from '../actions/fare' |
||||
import store from '../store' |
||||
import Input from './parts/Input' |
||||
import OurSelect from './parts/Select' |
||||
import AsyncSelect from 'react-select/lib/Async' |
||||
import { |
||||
PaymentMethodChoices, TransferChoices |
||||
} from '../constants/choices' |
||||
import { getItemFromList, getAgencyAsyncSelect } from '../utils' |
||||
import { AgencyOption } from './parts/SelectOptions' |
||||
|
||||
const StyledFareAttributesForm = styled.div` |
||||
padding: 1rem; |
||||
background: #fafafa; |
||||
` |
||||
|
||||
class FareAttributesForm extends Component { |
||||
|
||||
state = { |
||||
id: null, |
||||
fare_id: "", |
||||
price: 0, |
||||
currency_type: "THB", // ISO 4217
|
||||
payment_method: "0", // 0 - paid on board or 1 - paid before boarding
|
||||
transfer: "0", // 0 no transfer, 1 - transfer once, 2 tranfer twice,
|
||||
// '' - unlimited transfer
|
||||
agency: null, // optional
|
||||
transfer_duration: "0", // optional 0 - no transfer allowed,
|
||||
// otherwise it's length of time in seconds before
|
||||
// transfer expires
|
||||
} |
||||
|
||||
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 |
||||
if (id !== null) { |
||||
store.dispatch(updateFareAttr(id, body)) |
||||
} else { |
||||
store.dispatch(createFareAttr(body)) |
||||
} |
||||
this.setState({justSubmit: true}) |
||||
} |
||||
|
||||
handleDelete() { |
||||
const { id } = this.state |
||||
store.dispatch(deleteFareAttr(id)) |
||||
this.setState({justSubmit: true}) |
||||
} |
||||
|
||||
componentWillMount() { |
||||
const { props } = this |
||||
const { attribID } = props.match.params |
||||
const { results } = props.fareattr |
||||
const ones = results.filter(ele => ele.id === +attribID) |
||||
if (ones.length > 0) { |
||||
this.setState(ones[0]) |
||||
} |
||||
} |
||||
|
||||
renderForm() { |
||||
const one = this.state |
||||
const { fetching } = this.props.fareattr |
||||
return ( |
||||
<StyledFareAttributesForm> |
||||
<h1 className="title">{one.name} </h1> |
||||
<div className="content"> |
||||
<Input |
||||
label="Fare ID" |
||||
type="text" |
||||
fieldName="fare_id" |
||||
value={one.fare_id || ''} |
||||
handleChange={this.handleChange} /> |
||||
|
||||
<Input |
||||
label="Price" |
||||
type="text" |
||||
fieldName="price" |
||||
value={one.price || ''} |
||||
handleChange={this.handleChange} /> |
||||
|
||||
<Input |
||||
key="currency_type" |
||||
label="Currency" |
||||
type="text" |
||||
fieldName="currency_type" |
||||
value={one.currency_type || 'THB'} |
||||
handleChange={this.handleChange} /> |
||||
|
||||
<OurSelect |
||||
label="Payment Method" |
||||
type="text" |
||||
fieldName="payment_method" |
||||
value={getItemFromList(one.payment_method, PaymentMethodChoices, '0')} |
||||
handleChange={this.handleChange} |
||||
choices={PaymentMethodChoices} /> |
||||
|
||||
<OurSelect |
||||
label="Transfer" |
||||
type="text" |
||||
fieldName="transfer" |
||||
value={getItemFromList(one.transfer, TransferChoices, '0')} |
||||
handleChange={this.handleChange} |
||||
choices={TransferChoices} /> |
||||
|
||||
<Input |
||||
key="transfer" |
||||
label="Transfer" |
||||
type="text" |
||||
fieldName="transfer" |
||||
value={one.transfer || '0'} |
||||
handleChange={this.handleChange} /> |
||||
|
||||
<div className="field"> |
||||
<label className="label">Agency</label> |
||||
<AsyncSelect |
||||
cacheOptions={true} |
||||
defaultOptions |
||||
defaultValue={one.agency && {...one.agency, label: one.agency.name}} |
||||
loadOptions={getAgencyAsyncSelect} |
||||
components={{ Option: AgencyOption }} |
||||
onChange={(resp, evt) => { |
||||
if (evt.action === 'select-option') { |
||||
let evt = { |
||||
target: { |
||||
name: 'agency', |
||||
value: resp.value, |
||||
}} |
||||
this.handleChange(evt) |
||||
} |
||||
}} |
||||
/> |
||||
</div> |
||||
|
||||
<Input |
||||
key="transfer_duration" |
||||
label="Transfer Duration (sec)" |
||||
type="text" |
||||
fieldName="transfer_duration" |
||||
value={one.transfer_duration || '0'} |
||||
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"> |
||||
<Link to={`/fare`} className="button is-text">Cancel</Link> |
||||
</div> |
||||
</div> |
||||
</StyledFareAttributesForm> |
||||
) |
||||
} |
||||
|
||||
render () { |
||||
const one = this.state |
||||
const { fetching } = this.props.fareattr |
||||
// redirect to view page if no data
|
||||
const { attribID } = this.props.match.params |
||||
// this is a create form
|
||||
if (attribID === undefined) { |
||||
if (one.justSubmit === true && !fetching) { |
||||
return <Redirect to={`/fare`} /> |
||||
} |
||||
return this.renderForm() |
||||
} |
||||
|
||||
if (one.id === null && attribID.length > 0) |
||||
return <Redirect to={`/fare`} /> |
||||
|
||||
// redirect to fare list if submitted
|
||||
if (one.justSubmit === true && !fetching) { |
||||
return <Redirect to={`/fare`} /> |
||||
} |
||||
return this.renderForm() |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
const mapStateToProps = state => ({ |
||||
fareattr: state.fareattr |
||||
}) |
||||
|
||||
const connectFareAttributesForm = connect( |
||||
mapStateToProps, |
||||
)(FareAttributesForm) |
||||
export default connectFareAttributesForm |
@ -0,0 +1,81 @@
|
||||
import React, { Component } from 'react' |
||||
import styled from 'styled-components' |
||||
import { connect } from 'react-redux' |
||||
import { Link } from 'react-router-dom' |
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' |
||||
|
||||
import { getFareAttr, getFareRule } from '../actions/fare' |
||||
import store from '../store' |
||||
import { FareAttributesOne } from './FareAttributesDetail' |
||||
import { FareRulesOne } from './FareRulesDetail' |
||||
|
||||
|
||||
const StyledBox = styled.div` |
||||
padding: 1rem; |
||||
background: #fafafa; |
||||
` |
||||
|
||||
class FareList extends Component { |
||||
|
||||
componentWillMount() { |
||||
const { fareattr, farerule } = this.props |
||||
if (fareattr.count === 0) |
||||
store.dispatch(getFareAttr()) |
||||
|
||||
if (farerule.count === 0) |
||||
store.dispatch(getFareRule()) |
||||
} |
||||
|
||||
render() { |
||||
const { fareattr, farerule } = this.props |
||||
const { match } = this.props |
||||
return ( |
||||
<StyledBox> |
||||
<h1 className="title">Fare</h1> |
||||
<div className="columns"> |
||||
|
||||
<div className="column is-6"> |
||||
<nav className="level is-mobile"> |
||||
<p className="level-item has-text-centered"> |
||||
<Link className="link is-info" to={`${match.url}/attributes/new`}> |
||||
<FontAwesomeIcon icon="plus" /> New fare attributes |
||||
</Link> |
||||
</p> |
||||
</nav> |
||||
{fareattr.results && Object.keys(fareattr.results).map(i => ( |
||||
<FareAttributesOne item={fareattr.results[i]} /> |
||||
))} |
||||
</div> |
||||
|
||||
<div className="column is-6"> |
||||
<nav className="level is-mobile"> |
||||
<p className="level-item has-text-centered"> |
||||
<Link className="link is-info" to={`${match.url}/rules/new`}> |
||||
<FontAwesomeIcon icon="plus" /> New fare rule |
||||
</Link> |
||||
</p> |
||||
</nav> |
||||
{farerule.results && Object.keys(farerule.results).map(i => ( |
||||
<FareRulesOne key={`ff-${farerule.results[i].id}`} item={farerule.results[i]} /> |
||||
))} |
||||
</div> |
||||
|
||||
</div> |
||||
</StyledBox> |
||||
) |
||||
} |
||||
} |
||||
|
||||
const mapStateToProps = state => ({ |
||||
farerule: state.farerule, |
||||
fareattr: state.fareattr, |
||||
}) |
||||
const connectFareList = connect( |
||||
mapStateToProps, |
||||
{}, |
||||
)(FareList) |
||||
|
||||
export default styled(connectFareList)` |
||||
color: palevioletred; |
||||
font-weight: bold; |
||||
` |
@ -0,0 +1,48 @@
|
||||
import React from 'react' |
||||
import styled from 'styled-components' |
||||
import { Link } from 'react-router-dom' |
||||
|
||||
const FakeRow = styled.nav` |
||||
padding-top: 3px; |
||||
padding-bottom: 3px; |
||||
background: white; |
||||
margin-bottom: 5px !important; |
||||
` |
||||
|
||||
export const FareRulesOne = (props) => { |
||||
const { item } = props |
||||
|
||||
return ( |
||||
<FakeRow className="level panel" key={`fro-${item.fare_id}`}> |
||||
<div className="level-item has-text-centered"> |
||||
<div> |
||||
<p className="heading">Fare ID</p> |
||||
<Link to={`/fare/rules/${item.id}`}>{item.fare.fare_id}</Link> |
||||
</div> |
||||
</div> |
||||
<div className="level-item has-text-centered"> |
||||
<div> |
||||
<p className="heading">Origin ID</p> |
||||
<p className="title">{item.origin_id}</p> |
||||
</div> |
||||
</div> |
||||
<div className="level-item has-text-centered"> |
||||
<div> |
||||
<p className="heading">Destination ID (or contains)</p> |
||||
<p className="title"> |
||||
{item.destination_id} |
||||
{item.contains_id} |
||||
</p> |
||||
</div> |
||||
</div> |
||||
<div className="level-item has-text-centered"> |
||||
<div> |
||||
<p className="heading">Route ID</p> |
||||
<p> |
||||
{item.route && (item.route.route_id || '-')} |
||||
</p> |
||||
</div> |
||||
</div> |
||||
</FakeRow> |
||||
) |
||||
} |
@ -0,0 +1,289 @@
|
||||
import React, { Component } from 'react' |
||||
import styled from 'styled-components' |
||||
import { connect } from 'react-redux' |
||||
import { Redirect, Link } from 'react-router-dom' |
||||
|
||||
import { |
||||
updateFareRule, |
||||
createFareRule, |
||||
deleteFareRule |
||||
} from '../actions/fare' |
||||
import store from '../store' |
||||
import AsyncSelect from 'react-select/lib/Async' |
||||
import { |
||||
getStopsAsyncSelect, |
||||
getFareAttrAsyncSelect, |
||||
getRouteAsyncSelect |
||||
} from '../utils' |
||||
import { |
||||
StopOption, FareAttrOption, RouteOption |
||||
} from './parts/SelectOptions' |
||||
|
||||
const StyledFareRulesForm = styled.div` |
||||
padding: 1rem; |
||||
background: #fafafa; |
||||
` |
||||
|
||||
|
||||
class FareRulesForm extends Component { |
||||
|
||||
state = { |
||||
id: null, |
||||
fare: null, |
||||
route: null, // optinal
|
||||
origin_id: "", |
||||
destination_id: "", |
||||
contains_id: "", |
||||
} |
||||
|
||||
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 |
||||
if (id !== null) { |
||||
store.dispatch(updateFareRule(id, body)) |
||||
} else { |
||||
store.dispatch(createFareRule(body)) |
||||
} |
||||
this.setState({ justSubmit: true }) |
||||
} |
||||
|
||||
handleDelete() { |
||||
const { id } = this.state |
||||
store.dispatch(deleteFareRule(id)) |
||||
this.setState({ justSubmit: true }) |
||||
} |
||||
|
||||
componentWillMount() { |
||||
const { props } = this |
||||
const { ruleID } = props.match.params |
||||
const { results } = props.farerule |
||||
const { search } = props.location |
||||
if (ruleID === undefined) { |
||||
// this is for create form
|
||||
if (search.indexOf("?fareattr=") > -1) { |
||||
const fa = search.split("=") |
||||
const fat = props.fareattrItems.filter(ele => ele.id === +fa[1]) |
||||
if (fat.length === 1) |
||||
this.setState({ fare: fat[0] }) |
||||
} |
||||
} else { |
||||
// get state ready for edit form
|
||||
const ones = results.filter(ele => ele.id === +ruleID) |
||||
if (ones.length > 0) { |
||||
this.setState(ones[0]) |
||||
} |
||||
} |
||||
} |
||||
|
||||
renderForm() { |
||||
const one = this.state |
||||
const { fetching } = this.props.farerule |
||||
return ( |
||||
<StyledFareRulesForm> |
||||
<h1 className="title">{one.name} </h1> |
||||
<div className="content"> |
||||
|
||||
<div className="field"> |
||||
<label className="label">Fare ID</label> |
||||
<AsyncSelect |
||||
cacheOptions={true} |
||||
defaultOptions |
||||
defaultValue={one.fare && { ...one.fare, label: one.fare.fare_id }} |
||||
loadOptions={getFareAttrAsyncSelect} |
||||
components={{ Option: FareAttrOption }} |
||||
onChange={(resp, evt) => { |
||||
if (evt.action === 'select-option') { |
||||
let evt = { |
||||
target: { |
||||
name: 'fare', |
||||
value: resp.value, |
||||
} |
||||
} |
||||
this.handleChange(evt) |
||||
} |
||||
}} |
||||
/> |
||||
</div> |
||||
|
||||
<div className="field"> |
||||
<label className="label">Route <i>optional</i></label> |
||||
<AsyncSelect |
||||
cacheOptions={true} |
||||
defaultOptions |
||||
defaultValue={one.route && { value: one.route, label: one.route.route_id }} |
||||
loadOptions={getRouteAsyncSelect} |
||||
components={{ Option: RouteOption }} |
||||
onChange={(resp, evt) => { |
||||
if (evt.action === 'select-option') { |
||||
let evt = { |
||||
target: { |
||||
name: 'route', |
||||
value: resp.value, |
||||
} |
||||
} |
||||
this.handleChange(evt) |
||||
} |
||||
}} |
||||
/> |
||||
</div> |
||||
|
||||
<div className="field"> |
||||
<label className="label">Origin ID</label> |
||||
<AsyncSelect |
||||
cacheOptions={true} |
||||
defaultOptions |
||||
defaultValue={one.origin_id && { value: one.origin_id, label: one.origin_id }} |
||||
loadOptions={getStopsAsyncSelect} |
||||
components={{ Option: StopOption }} |
||||
onChange={(resp, evt) => { |
||||
let result |
||||
const fieldName = 'origin_id' |
||||
if (evt.action === 'select-option') { |
||||
result = { |
||||
target: { |
||||
name: fieldName, |
||||
value: resp.value.stop_id, |
||||
} |
||||
} |
||||
} else if (evt.action === 'clear') { |
||||
result = { target: { name: fieldName, value: '' } } |
||||
} |
||||
if (result !== undefined) |
||||
this.handleChange(result) |
||||
}} |
||||
/> |
||||
</div> |
||||
|
||||
<div className="field"> |
||||
<label className="label">Destination ID (or IDs)</label> |
||||
<AsyncSelect |
||||
isMulti |
||||
isClearable |
||||
cacheOptions |
||||
defaultOptions |
||||
defaultValue={one.destination_id && { value: one.destination_id, label: one.destination_id }} |
||||
loadOptions={getStopsAsyncSelect} |
||||
components={{ Option: StopOption }} |
||||
onChange={(resp, evt) => { |
||||
let result |
||||
const fieldName = 'destination_id' |
||||
if (evt.action === 'select-option') { |
||||
result = { |
||||
target: { |
||||
name: fieldName, |
||||
value: resp.map(el => el.value.stop_id).join(','), |
||||
} |
||||
} |
||||
} else if (evt.action === 'clear') { |
||||
result = { target: { name: fieldName, value: '' } } |
||||
} |
||||
if (result !== undefined) |
||||
this.handleChange(result) |
||||
}} |
||||
/> |
||||
</div> |
||||
|
||||
<div className="field"> |
||||
<label className="label">Contains ID (or IDs)</label> |
||||
<AsyncSelect |
||||
isMulti |
||||
isClearable |
||||
cacheOptions={true} |
||||
defaultOptions |
||||
defaultValue={one.contains_id && { value: one.contains_id, label: one.contains_id }} |
||||
loadOptions={getStopsAsyncSelect} |
||||
components={{ Option: StopOption }} |
||||
onChange={(resp, evt) => { |
||||
let result |
||||
const fieldName = 'contains_id' |
||||
if (evt.action === 'select-option') { |
||||
result = { |
||||
target: { |
||||
name: fieldName, |
||||
value: resp.map(el => el.value.stop_id).join(','), |
||||
} |
||||
} |
||||
} else if (evt.action === 'clear') { |
||||
result = { target: { name: fieldName, value: '' } } |
||||
} |
||||
if (result !== undefined) |
||||
this.handleChange(result) |
||||
}} |
||||
/> |
||||
</div> |
||||
|
||||
<p>When having multiple destinations or contains, multiple fare rule records will be created automatically. Nothing differs from adding multiple records manually, it only saves time. !! only works with CREATE, UPDATE will throw 500</p> |
||||
</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"> |
||||
<Link to={`/fare${one.fare ? `/attributes/${one.fare.id}` : ''}`} |
||||
className="button is-text">Cancel</Link> |
||||
</div> |
||||
</div> |
||||
</StyledFareRulesForm> |
||||
) |
||||
} |
||||
|
||||
render() { |
||||
const one = this.state |
||||
const { fetching } = this.props.farerule |
||||
// redirect to view page if no data
|
||||
const { ruleID } = this.props.match.params |
||||
// this is a create form
|
||||
if (ruleID === undefined) { |
||||
if (one.justSubmit === true && !fetching) { |
||||
return <Redirect to={`/fare/attributes/${one.fare.id}`} /> |
||||
} |
||||
return this.renderForm() |
||||
} |
||||
|
||||
if (one.id === null && ruleID.length > 0) |
||||
return <Redirect to={`/fare`} /> |
||||
|
||||
// redirect to fare list if submitted
|
||||
if (one.justSubmit === true && !fetching) { |
||||
return <Redirect to={`/fare/attributes/${one.fare.id}`} /> |
||||
} |
||||
return this.renderForm() |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
const mapStateToProps = state => ({ |
||||
farerule: state.farerule, |
||||
fareattrItems: state.fareattr.results, |
||||
}) |
||||
|
||||
const connectFareRulesForm = connect( |
||||
mapStateToProps, |
||||
)(FareRulesForm) |
||||
export default connectFareRulesForm |
@ -0,0 +1,27 @@
|
||||
import React, { Component } from 'react' |
||||
|
||||
class First extends Component { |
||||
|
||||
state = { inputValue: '' } |
||||
|
||||
handleInputChange = (newValue) => { |
||||
console.log("inputChange", newValue) |
||||
const inputValue = newValue.replace(/\W/g, '') |
||||
this.setState({ inputValue }) |
||||
return inputValue |
||||
} |
||||
|
||||
render() { |
||||
return ( |
||||
<div> |
||||
<br /> |
||||
<br /> |
||||
<h1>GoTH first page</h1> |
||||
<br /> |
||||
<br /> |
||||
</div> |
||||
) |
||||
} |
||||
} |
||||
|
||||
export default First |
@ -1,13 +1,15 @@
|
||||
import React from 'react' |
||||
import { version } from '../../package.json' |
||||
|
||||
const Footer = () => |
||||
<footer className="footer"> |
||||
<div className="content has-text-centered"> |
||||
<img src="https://static.10ninox.com/goth-rect-640x160.svg" alt="GoTH" width="168" height="42" /> |
||||
<br /> |
||||
<small>{ version }</small> |
||||
<br /> |
||||
<a href="javascript:location.reload(true)"><i className="fas fa-sync"></i> reload</a> |
||||
</div> |
||||
</footer> |
||||
|
||||
|
||||
export default Footer |
||||
|
@ -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 ( |
||||
<StyleBox> |
||||
<Input |
||||
label="Start" |
||||
type="text" |
||||
fieldName="start_time" |
||||
value={item.start_time || ''} |
||||
handleChange={this.handleChange} /> |
||||
|
||||
<Input |
||||
label="End" |
||||
type="text" |
||||
fieldName="end_time" |
||||
value={item.end_time || ''} |
||||
handleChange={this.handleChange} /> |
||||
<Input |
||||
label="Headway secs" |
||||
type="text" |
||||
fieldName="headway_secs" |
||||
value={item.headway_secs || ''} |
||||
handleChange={this.handleChange} /> |
||||
|
||||
<Select |
||||
label="Exact Times" |
||||
type="text" |
||||
fieldName="exact_times" |
||||
value={getItemFromList(item.exact_times, ExactTimeChoices, '0')} |
||||
handleChange={this.handleChange} |
||||
choices={ExactTimeChoices} /> |
||||
|
||||
<div className="field is-grouped"> |
||||
<div className="control"> |
||||
<button className="button is-link" |
||||
onClick={this.handleSubmit} |
||||
disabled={false}> |
||||
Save</button> |
||||
</div> |
||||
{item.id !== null && <div className="control"> |
||||
<button className="button is-danger" |
||||
onClick={this.handleDelete} |
||||
disabled={false}> |
||||
DELETE</button> |
||||
</div>} |
||||
{this.props.toggleEditMode && |
||||
<div className="control"> |
||||
<a onClick={() => this.props.toggleEditMode()} className="button is-text">Cancel</a> |
||||
</div>} |
||||
</div> |
||||
</StyleBox> |
||||
) |
||||
} |
||||
|
||||
} |
||||
|
||||
export default FrequencyForm |
@ -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) => ( |
||||
<StyledRow className="level panel" key={`st-item-${item.id}`}> |
||||
<div className="level-item has-text-centered"> |
||||
<div> |
||||
<p className="heading"> |
||||
<a onClick={() => this.toggleEditMode()}> EDIT</a> |
||||
Start Time</p> |
||||
<p className="title">{item.start_time}</p> |
||||
</div> |
||||
</div> |
||||
<div className="level-item has-text-centered"> |
||||
<div> |
||||
<p className="heading">End Time</p> |
||||
<p className="title">{item.end_time}</p> |
||||
</div> |
||||
</div> |
||||
<div className="level-item has-text-centered"> |
||||
<div> |
||||
<p className="heading">Headway secs</p> |
||||
<p className="title">{item.headway_secs}</p> |
||||
</div> |
||||
</div> |
||||
<div className="level-item has-text-centered"> |
||||
<div> |
||||
<p className="heading">Exact Times</p> |
||||
<p className="title">{getItemFromList(item.exact_times, ExactTimeChoices).value}</p> |
||||
</div> |
||||
</div> |
||||
</StyledRow> |
||||
) |
||||
|
||||
render() { |
||||
const { item, tripId } = this.props |
||||
if (this.state.editMode) |
||||
return <FrequencyForm |
||||
item={item} |
||||
trip={tripId} |
||||
toggleEditMode={this.toggleEditMode} /> |
||||
return this.renderReadOnly(item) |
||||
} |
||||
|
||||
} |
||||
|
||||
export default FrequencyOne |
@ -0,0 +1,41 @@
|
||||
import React from 'react' |
||||
import { components } from 'react-select' |
||||
|
||||
|
||||
export const StopOption = (props) => { |
||||
const { stop_id, name, stop_desc } = props.data.value |
||||
return ( |
||||
<components.Option {...props}> |
||||
<code>{stop_id}</code> {name} |
||||
{stop_desc.length > 0 && <small><br />{stop_desc}</small>} |
||||
</components.Option> |
||||
) |
||||
} |
||||
|
||||
export const FareAttrOption = (props) => { |
||||
const { fare_id, price, currency_type } = props.data.value |
||||
return ( |
||||
<components.Option {...props}> |
||||
<code>{fare_id}</code> {price} {currency_type} |
||||
</components.Option> |
||||
) |
||||
} |
||||
|
||||
export const RouteOption = (props) => { |
||||
const { route_id, short_name, long_name} = props.data.value |
||||
return ( |
||||
<components.Option {...props}> |
||||
<code>{route_id}</code> {short_name} |
||||
<br/>{long_name} |
||||
</components.Option> |
||||
) |
||||
} |
||||
|
||||
export const AgencyOption = (props) => { |
||||
const { agency_id, name } = props.data.value |
||||
return ( |
||||
<components.Option {...props}> |
||||
<code>{agency_id}</code> {name} |
||||
</components.Option> |
||||
) |
||||
} |
@ -1,5 +1,7 @@
|
||||
|
||||
// export const URL = process.env.API_URL || '//localhost:8000'
|
||||
export const URL = process.env.API_URL || 'https://api.goth.app' |
||||
export const API_PREFIX = process.env.API_PREFIX || '/v1' |
||||
export const API_URL = `${URL}${API_PREFIX}` |
||||
export const LOGIN = '/api-token-auth/' |
||||
export const RAVEN_DSN = 'https://7d87b4ca392d48589794769a223e9bfb@sentry.io/1248366' |
||||
|
@ -0,0 +1,76 @@
|
||||
|
||||
export const RouteTypeChoices = [ |
||||
{ value: '0', label: 'Tram, Light rail' }, |
||||
{ value: '1', label: 'Subway (within metro area)' }, |
||||
{ value: '2', label: 'Rail' }, |
||||
{ value: '3', label: 'Bus' }, |
||||
{ value: '4', label: 'Ferry' }, |
||||
{ value: '5', label: 'Cable car' }, |
||||
{ value: '6', label: 'Gondola' }, |
||||
{ value: '7', label: 'Funicular (steep inclines)' }, |
||||
] |
||||
|
||||
export const StopLocationTypes = [ |
||||
{ value: '0', label: 'Stop' }, |
||||
{ value: '1', label: 'Station' }, |
||||
{ value: '2', label: 'Station Entrance/Exit' }, |
||||
] |
||||
|
||||
export const StopWheelChairInfo = [ |
||||
{ value: '0', label: 'No information' }, |
||||
{ value: '1', label: 'Possible (partially or fully)' }, |
||||
{ value: '2', label: 'Not possible' }, |
||||
] |
||||
|
||||
export const DropOffTypes = [ |
||||
{ value: '0', label: 'Regularly scheduled dropoff' }, |
||||
{ value: '1', label: 'No drop off' }, |
||||
{ value: '2', label: 'Arrange drop off' }, |
||||
{ value: '3', label: 'Contact driver' }, |
||||
] |
||||
|
||||
export const TimePointChoices = [ |
||||
{ value: '0', label: 'times are considered approximate.' }, |
||||
{ value: '1', label: 'times are considered exact.' }, |
||||
] |
||||
|
||||
export const PickUpTypes = [ |
||||
{ value: '0', label: 'Regularly scheduled pickup' }, |
||||
{ value: '1', label: 'No pickup' }, |
||||
{ value: '2', label: 'Arrange pickup' }, |
||||
{ value: '3', label: 'Contact driver' }, |
||||
] |
||||
|
||||
export const DirectionChoices = [ |
||||
{ value: '0', label: 'outbound' }, |
||||
{ value: '1', label: 'inbound' }, |
||||
] |
||||
|
||||
export const WheelChairAccessibles = [ |
||||
{ value: '0', label: 'No information' }, |
||||
{ value: '1', label: 'Yes' }, |
||||
{ value: '2', label: 'Not possible' }, |
||||
] |
||||
|
||||
export const BikeAllowanceChoices = [ |
||||
{ value: '0', label: 'No information' }, |
||||
{ value: '1', label: 'Yes' }, |
||||
{ value: '2', label: 'Not possible' }, |
||||
] |
||||
|
||||
export const ExactTimeChoices = [ |
||||
{ value: '0', label: 'Not exactly scheduled.' }, |
||||
{ value: '1', label: 'Exactly scheduled' }, |
||||
] |
||||
|
||||
export const PaymentMethodChoices = [ |
||||
{ value: '0', label: 'Paid on board' }, |
||||
{ value: '1', label: 'Paid before boarding' }, |
||||
] |
||||
|
||||
export const TransferChoices = [ |
||||
{ value: '0', label: 'No transfer' }, |
||||
{ value: '1', label: 'Transfer once' }, |
||||
{ value: '2', label: 'Transfer twice' }, |
||||
{ value: '', label: 'Unlimited transfer' }, |
||||
] |
@ -0,0 +1,100 @@
|
||||
import { |
||||
FARERULE_CREATE, FARERULE_DELETE, FARERULE_UPDATE, |
||||
FARERULE_REQUEST, FARERULE_SUCCESS, FARERULE_FAILURE, |
||||
} from '../constants/ActionTypes' |
||||
|
||||
|
||||
const fareRuleInitState = { |
||||
results: [], |
||||
previous: null, |
||||
next: null, |
||||
count: 0, |
||||
fetching: false, |
||||
query: '', |
||||
isNext: false, |
||||
} |
||||
const fareRule = (state = fareRuleInitState, action) => { |
||||
switch(action.type) { |
||||
case FARERULE_REQUEST: |
||||
let resetResult, resetCount, isNextReq = false |
||||
if (action.meta !== undefined) { |
||||
if (action.meta.query === 'next') { |
||||
isNextReq = true |
||||
} else { |
||||
resetResult = state.query !== action.meta.query ? [] : state.results |
||||
resetCount = state.query !== action.meta.query ? 0 : state.count |
||||
} |
||||
} |
||||
const updateQRC = (action.meta !== undefined) && !isNextReq |
||||
return { |
||||
...state, |
||||
fetching: true, |
||||
query: updateQRC ? action.meta.query : state.query, |
||||
results: updateQRC ? resetResult : state.results, |
||||
count: updateQRC ? resetCount : state.count, |
||||
} |
||||
case FARERULE_SUCCESS: |
||||
const { count, next, previous, results } = action.payload |
||||
return { |
||||
...state, |
||||
fetching: false, |
||||
count, |
||||
previous, |
||||
next, |
||||
results: [ |
||||
...( (previous) ? state.results : [] ), |
||||
...results, |
||||
], |
||||
} |
||||
case FARERULE_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 FARERULE_CREATE: |
||||
let r |
||||
/* check if it's array by |
||||
- action.payload instanceof Array |
||||
- Array.isArray(action.payload) |
||||
*/ |
||||
if (Array.isArray(action.payload)) { |
||||
r = [...state.results, ...action.payload] |
||||
} else { |
||||
r = [...state.results, action.payload] |
||||
} |
||||
return { |
||||
...state, |
||||
fetching: false, |
||||
count: state.count + 1, |
||||
results: r, |
||||
} |
||||
case FARERULE_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 FARERULE_FAILURE: |
||||
return { |
||||
...state, |
||||
fetching: false, |
||||
} |
||||
default: |
||||
return state; |
||||
} |
||||
} |
||||
|
||||
export default fareRule |
@ -0,0 +1,88 @@
|
||||
import { |
||||
FREQUENCY_CREATE, FREQUENCY_DELETE, FREQUENCY_UPDATE, |
||||
FREQUENCY_REQUEST, FREQUENCY_SUCCESS, FREQUENCY_FAILURE, |
||||
GEO_POLYGON_RESET, |
||||
} from '../constants/ActionTypes' |
||||
|
||||
|
||||
const frequencyInitState = { |
||||
results: [], |
||||
next: null, |
||||
count: 0, |
||||
fetching: false, |
||||
query: '', |
||||
} |
||||
const frequency = (state = frequencyInitState, action) => { |
||||
switch (action.type) { |
||||
case GEO_POLYGON_RESET: |
||||
return { |
||||
...state, |
||||
fetching: false, |
||||
count: 0, |
||||
next: null, |
||||
query: '', |
||||
results: [], |
||||
} |
||||
case FREQUENCY_REQUEST: |
||||
return { |
||||
...state, |
||||
fetching: true, |
||||
query: action.meta !== undefined ? action.meta.query : state.query, |
||||
} |
||||
case FREQUENCY_SUCCESS: |
||||
const { count, next, previous, results } = action.payload |
||||
return { |
||||
...state, |
||||
fetching: false, |
||||
count, |
||||
next, |
||||
results: [ |
||||
...((previous) ? state.results : []), |
||||
...results, |
||||
] |
||||
} |
||||
case FREQUENCY_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 FREQUENCY_CREATE: |
||||
return { |
||||
...state, |
||||
fetching: false, |
||||
count: state.count + 1, |
||||
results: [ |
||||
...state.results, |
||||
action.payload, |
||||
] |
||||
} |
||||
case FREQUENCY_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 FREQUENCY_FAILURE: |
||||
return { |
||||
...state, |
||||
fetching: false, |
||||
} |
||||
default: |
||||
return state; |
||||
} |
||||
} |
||||
|
||||
export default frequency |
@ -0,0 +1,19 @@
|
||||
import { call, put, takeLatest } from 'redux-saga/effects' // takeEvery
|
||||
import { apiClient } from './utils/ApiClient' |
||||
import * as types from './constants/ActionTypes' |
||||
|
||||
function* fetchStop(action) { |
||||
try { |
||||
const url = `/stop/?${action.payload.query || ''}` |
||||
const stops = yield call(apiClient, url) |
||||
yield put({type: types.STOP_SUCCESS, payload: stops.data}) |
||||
} catch (e) { |
||||
yield put({type: types.STOP_FAILURE, message: e.message}) |
||||
} |
||||
} |
||||
|
||||
function* mySaga() { |
||||
yield takeLatest(types.STOP_REQUEST, fetchStop) |
||||
} |
||||
|
||||
export default mySaga |
@ -0,0 +1,96 @@
|
||||
import { CancelToken } from 'axios' |
||||
import { apiClient } from './ApiClient' |
||||
import polyUtil from 'polyline-encoded' |
||||
|
||||
|
||||
export const decodeGeoJson = (encodedOne) => { |
||||
if (typeof encodedOne.coordinates === "string") |
||||
encodedOne.coordinates = polyUtil.decode(encodedOne.coordinates) |
||||
return encodedOne |
||||
} |
||||
|
||||
|
||||
export const getItemFromList = (targetValue, list, defaultValue) => { |
||||
if (!targetValue && defaultValue === undefined) |
||||
return '' |
||||
let f = list.filter(ele => ele.value === targetValue) |
||||
if (f.length > 0) |
||||
return f[0] |
||||
// default value
|
||||
f = list.filter(ele => ele.value === defaultValue) |
||||
if (f.length > 0) |
||||
return f[0] |
||||
return '' |
||||
} |
||||
|
||||
|
||||
export const getStopsAsyncSelect = (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 => ({ |
||||
value: {...i}, |
||||
label: `${i.stop_id}-${i.name}` |
||||
}))) |
||||
}) |
||||
} |
||||
|
||||
|
||||
export const getFareAttrAsyncSelect = (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(`/fare-attribute/?search=${inputValue}`, { cancelToken }) |
||||
.then((resp) => { |
||||
callback(resp.data.results.map(i => ({ |
||||
value: {...i}, |
||||
label: i.fare_id |
||||
}))) |
||||
}) |
||||
} |
||||
|
||||
|
||||
export const getRouteAsyncSelect = (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(`/route/?search=${inputValue}`, { cancelToken }) |
||||
.then((resp) => { |
||||
callback(resp.data.results.map(i => ({ |
||||
value: {...i}, |
||||
label: i.route_id |
||||
}))) |
||||
}) |
||||
} |
||||
|
||||
|
||||
export const getAgencyAsyncSelect = (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(`/agency/?search=${inputValue}`, { cancelToken }) |
||||
.then((resp) => { |
||||
callback(resp.data.results.map(i => ({ |
||||
value: {...i}, |
||||
label: i.name |
||||
}))) |
||||
}) |
||||
} |
Loading…
Reference in new issue