sipp11
7 years ago
31 changed files with 1434 additions and 76 deletions
@ -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 getCalendar = (query) => ({ |
||||
[RSAA]: { |
||||
endpoint: `${API_URL}/calendar/?${query || ''}`, |
||||
method: 'GET', |
||||
headers: RSAAHeaders, |
||||
bailout: (state) => state.calendar.fetching || state.calendar.query === query, |
||||
types: [ |
||||
{ |
||||
type: types.CALENDAR_REQUEST, |
||||
meta: { query: query }, |
||||
}, |
||||
types.CALENDAR_SUCCESS, |
||||
types.CALENDAR_FAILURE, |
||||
] |
||||
} |
||||
}) |
||||
|
||||
export const updateCalendar = (id, body) => ({ |
||||
[RSAA]: { |
||||
endpoint: `${API_URL}/calendar/${id}/`, |
||||
body: JSON.stringify(body), |
||||
method: 'PATCH', |
||||
headers: RSAAHeaders, |
||||
types: [ |
||||
types.CALENDAR_REQUEST, |
||||
types.CALENDAR_UPDATE, |
||||
types.CALENDAR_FAILURE, |
||||
] |
||||
} |
||||
}) |
||||
|
||||
export const createCalendar = (body) => ({ |
||||
[RSAA]: { |
||||
endpoint: `${API_URL}/calendar/`, |
||||
body: JSON.stringify(body), |
||||
method: 'POST', |
||||
headers: RSAAHeaders, |
||||
types: [ |
||||
types.CALENDAR_REQUEST, |
||||
types.CALENDAR_CREATE, |
||||
types.CALENDAR_FAILURE, |
||||
] |
||||
} |
||||
}) |
||||
|
||||
export const deleteCalendar = (id) => ({ |
||||
[RSAA]: { |
||||
endpoint: `${API_URL}/calendar/${id}/`, |
||||
method: 'DELETE', |
||||
headers: RSAAHeaders, |
||||
types: [ |
||||
types.CALENDAR_REQUEST, |
||||
{ |
||||
type: types.CALENDAR_DELETE, |
||||
meta: { id } |
||||
}, |
||||
types.CALENDAR_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 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,207 @@
|
||||
import React, { Component } from 'react' |
||||
import styled from 'styled-components' |
||||
import { connect } from 'react-redux' |
||||
import { Redirect, Link } from 'react-router-dom' |
||||
|
||||
import { updateAgency, createAgency, deleteAgency } from '../actions'; |
||||
import store from '../store' |
||||
|
||||
const StyledAgencyForm = styled.div` |
||||
padding: 1rem; |
||||
background: #fafafa; |
||||
`;
|
||||
|
||||
const HorizontalInput = (props) => ( |
||||
<div className="field is-horizontal"> |
||||
<div className="field-label is-normal"> |
||||
<label className="label">{props.label}</label> |
||||
</div> |
||||
<div className="field-body"> |
||||
<div className="field"> |
||||
<p className="control"> |
||||
<input className="input" type={props.type || 'text'} |
||||
name={props.fieldName} |
||||
onChange={props.handleChange} |
||||
defaultValue={props.value} /> |
||||
</p> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
) |
||||
|
||||
|
||||
class AgencyForm extends Component { |
||||
|
||||
state = { |
||||
id: null, |
||||
agency_id: "", |
||||
email: "", |
||||
fare_url: "", |
||||
lang: "", |
||||
name: "", |
||||
phone: "", |
||||
timezone: "", |
||||
url: "", |
||||
} |
||||
|
||||
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(updateAgency(id, body)) |
||||
} else { |
||||
store.dispatch(createAgency(body)) |
||||
} |
||||
this.setState({justSubmit: true}) |
||||
} |
||||
|
||||
handleDelete() { |
||||
const { id } = this.state |
||||
store.dispatch(deleteAgency(id)) |
||||
this.setState({justSubmit: true}) |
||||
} |
||||
|
||||
componentWillMount() { |
||||
const { props } = this |
||||
const { agencyId } = props.match.params |
||||
const { results } = props.agency |
||||
const ones = results.filter(ele => ele.agency_id === agencyId) |
||||
if (ones.length > 0) { |
||||
this.setState(ones[0]) |
||||
} |
||||
} |
||||
|
||||
renderForm() { |
||||
const one = this.state |
||||
const { fetching } = this.props.agency |
||||
return ( |
||||
<StyledAgencyForm> |
||||
<h1 className="title">{one.name} </h1> |
||||
<div className="content"> |
||||
<HorizontalInput |
||||
label="Agency ID" |
||||
type="text" |
||||
fieldName="agency_id" |
||||
value={one.agency_id || ''} |
||||
handleChange={this.handleChange} /> |
||||
|
||||
<HorizontalInput |
||||
label="Name" |
||||
type="text" |
||||
fieldName="name" |
||||
value={one.name || ''} |
||||
handleChange={this.handleChange} /> |
||||
|
||||
<HorizontalInput |
||||
label="URL" |
||||
type="text" |
||||
fieldName="url" |
||||
value={one.url || ''} |
||||
handleChange={this.handleChange} /> |
||||
|
||||
<HorizontalInput |
||||
label="Timezone" |
||||
type="text" |
||||
fieldName="timezone" |
||||
value={one.timezone || ''} |
||||
handleChange={this.handleChange} /> |
||||
|
||||
<HorizontalInput |
||||
label="Phone" |
||||
type="text" |
||||
fieldName="phone" |
||||
value={one.phone || ''} |
||||
handleChange={this.handleChange} /> |
||||
|
||||
<HorizontalInput |
||||
label="Language" |
||||
type="text" |
||||
fieldName="lang" |
||||
value={one.lang || ''} |
||||
handleChange={this.handleChange} /> |
||||
|
||||
<HorizontalInput |
||||
label="Fare URL" |
||||
type="text" |
||||
fieldName="fare_url" |
||||
value={one.fare_url || ''} |
||||
handleChange={this.handleChange} /> |
||||
|
||||
<HorizontalInput |
||||
label="Email" |
||||
type="email" |
||||
fieldName="email" |
||||
value={one.email || ''} |
||||
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={`/agency/${one.agency_id}`} className="button is-text">Cancel</Link> |
||||
</div> |
||||
</div> |
||||
</StyledAgencyForm> |
||||
) |
||||
} |
||||
|
||||
render () { |
||||
const one = this.state |
||||
const { fetching } = this.props.agency |
||||
// redirect to view page if no data
|
||||
const { agencyId } = this.props.match.params |
||||
// this is a create form
|
||||
if (agencyId === undefined) { |
||||
if (one.justSubmit === true && !fetching) { |
||||
return <Redirect to={`/agency/`} /> |
||||
} |
||||
return this.renderForm() |
||||
} |
||||
|
||||
if (one.id === null && agencyId.length > 0) |
||||
return <Redirect to={`/agency/`} /> |
||||
|
||||
// redirect to agency list if submitted
|
||||
if (one.justSubmit === true && !fetching) { |
||||
return <Redirect to={`/agency/${one.agency_id || ''}`} /> |
||||
} |
||||
return this.renderForm() |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
const mapStateToProps = state => ({ |
||||
agency: state.agency |
||||
}) |
||||
|
||||
const connectAgencyList = connect( |
||||
mapStateToProps, |
||||
)(AgencyForm) |
||||
export default connectAgencyList |
@ -0,0 +1,87 @@
|
||||
import React, { Component } from 'react' |
||||
import styled from 'styled-components' |
||||
import { connect } from 'react-redux' |
||||
import { Link, Redirect, Route } from 'react-router-dom' |
||||
|
||||
// import { getRoute } from '../actions'
|
||||
// import store from '../store'
|
||||
|
||||
import Spinner from './Spinner' |
||||
import RouteList from './RouteList' |
||||
import FareAttrList from './FareAttrList' |
||||
|
||||
const StyledAgencyItem = styled.div` |
||||
padding: 1rem; |
||||
background: #fafafa; |
||||
` |
||||
|
||||
class AgencyItem extends Component { |
||||
|
||||
render() { |
||||
const { props } = this |
||||
const { agencyId, agencyChild } = props.match.params |
||||
const { results, fetching } = props.agency |
||||
const one = results.filter(ele => ele.agency_id === agencyId)[0] |
||||
|
||||
if (!fetching && one === undefined) |
||||
return <Redirect to='/agency' /> |
||||
|
||||
if (fetching) |
||||
return <Spinner show={true} /> |
||||
|
||||
return ( |
||||
<StyledAgencyItem> |
||||
<h1 className="title"> |
||||
{one.name} |
||||
<Link to={`/agency/${one.agency_id}/edit`} className="button">Edit</Link></h1> |
||||
<div className="columns"> |
||||
<div className="column is-3 is-hidden-mobile"> |
||||
<div className="content"> |
||||
<dl> |
||||
<dt>agency_id</dt> |
||||
<dd>{one.agency_id}</dd> |
||||
<dt>URL</dt> |
||||
<dd>{one.url}</dd> |
||||
<dt>Timezone</dt> |
||||
<dd>{one.timezone || '-'}</dd> |
||||
<dt>Phone</dt> |
||||
<dd>{one.phone || '-'}</dd> |
||||
<dt>Language</dt> |
||||
<dd>{one.lang || '-'}</dd> |
||||
<dt>Fare URL</dt> |
||||
<dd>{one.fare_url || '-'}</dd> |
||||
<dt>Email</dt> |
||||
<dd>{one.email || '-'}</dd> |
||||
</dl> |
||||
</div> |
||||
</div> |
||||
<div className="column is-9"> |
||||
<div className="tabs"> |
||||
<ul> |
||||
<li className={`${agencyChild === undefined && "is-active"}`}><Link to={`/agency/${one.agency_id}`}>Overview</Link></li> |
||||
<li className={`${agencyChild === 'route' && "is-active"}`}><Link to={`/agency/${one.agency_id}/route`}>Route</Link></li> |
||||
<li className={`${agencyChild === 'fare-attr' && "is-active"}`}><Link to={`/agency/${one.agency_id}/fare-attr`}>Fare Attributes</Link></li> |
||||
</ul> |
||||
</div> |
||||
<Route path={`/agency/:agencyId/route/:routeId`} component={RouteList} /> |
||||
<Route path={`/agency/:agencyId/route`} component={RouteList} /> |
||||
<Route path={`/agency/:agencyId/fare-attr/:fareAttrId`} component={FareAttrList} /> |
||||
<Route path={`/agency/:agencyId/fare-attr`} component={FareAttrList} /> |
||||
{agencyChild === undefined && <p>Pick options on the tab</p>} |
||||
</div> |
||||
</div> |
||||
{props.children} |
||||
</StyledAgencyItem> |
||||
) |
||||
} |
||||
} |
||||
|
||||
const mapStateToProps = state => ({ |
||||
agency: state.agency, |
||||
route: state.route, |
||||
}) |
||||
|
||||
const connectAgencyList = connect( |
||||
mapStateToProps, |
||||
)(AgencyItem) |
||||
export default connectAgencyList |
@ -0,0 +1,47 @@
|
||||
import React, { Component } from 'react' |
||||
import styled from 'styled-components' |
||||
import { connect } from 'react-redux' |
||||
import { Link } from 'react-router-dom' |
||||
|
||||
import { getAgency } from '../actions' |
||||
import store from '../store' |
||||
|
||||
// const StyledAgencyList = styled.section`
|
||||
// padding: 1em;
|
||||
// background: papayawhip;
|
||||
// `;
|
||||
|
||||
class AgencyList extends Component { |
||||
|
||||
componentWillMount() { |
||||
const { count } = this.props.agency |
||||
if (count === 0) |
||||
store.dispatch(getAgency()) |
||||
} |
||||
|
||||
render() { |
||||
const { results } = this.props.agency |
||||
return ( |
||||
<ul> |
||||
{results && Object.keys(results).map(i => ( |
||||
<li key={i}> |
||||
<Link to={`/agency/${results[i].agency_id}`}>{results[i].agency_id}</Link> |
||||
</li> |
||||
))} |
||||
</ul> |
||||
) |
||||
} |
||||
} |
||||
|
||||
const mapStateToProps = state => ({ |
||||
agency: state.agency |
||||
}) |
||||
const connectAgencyList = connect( |
||||
mapStateToProps, |
||||
{}, |
||||
)(AgencyList) |
||||
|
||||
export default styled(connectAgencyList)` |
||||
color: palevioletred; |
||||
font-weight: bold; |
||||
` |
@ -0,0 +1,50 @@
|
||||
import React, { Component } from 'react' |
||||
import { connect } from 'react-redux' |
||||
|
||||
import Spinner from './Spinner' |
||||
import { getFareAttr } from '../actions/fareattr' |
||||
import store from '../store' |
||||
|
||||
|
||||
class FareAttrList extends Component { |
||||
|
||||
|
||||
componentDidMount() { |
||||
const { agencyId } = this.props.match.params |
||||
store.dispatch(getFareAttr(`agency=${agencyId}`)) |
||||
} |
||||
|
||||
componentWillReceiveProps(newProps) { |
||||
const { agencyId } = newProps.match.params |
||||
if (agencyId !== this.props.match.params.agencyId) |
||||
store.dispatch(getFareAttr(`agency=${agencyId}`)) |
||||
} |
||||
|
||||
render() { |
||||
const { fareattr } = this.props |
||||
|
||||
return ( |
||||
<div> |
||||
<h3 className="title">Fare Attributes</h3> |
||||
<Spinner show={fareattr.fetching} /> |
||||
<ul> |
||||
{fareattr.count === 0 && <li>No fare attribute</li>} |
||||
{fareattr.count > 0 |
||||
&& fareattr.results.map(ele => ( |
||||
<li key={ele.fare_id}>{ ele.fare_id }</li>) |
||||
)} |
||||
</ul> |
||||
</div> |
||||
) |
||||
} |
||||
} |
||||
|
||||
const mapStateToProps = state => ({ |
||||
fareattr: state.fareattr, |
||||
}) |
||||
|
||||
const connectFareAttrList = connect( |
||||
mapStateToProps, |
||||
)(FareAttrList) |
||||
|
||||
export default connectFareAttrList |
@ -0,0 +1,13 @@
|
||||
import React from 'react' |
||||
|
||||
const Footer = () => |
||||
<footer className="footer"> |
||||
<div className="content has-text-centered"> |
||||
<p> |
||||
<strong>Go</strong><sup>TH</sup> |
||||
</p> |
||||
</div> |
||||
</footer> |
||||
|
||||
|
||||
export default Footer |
@ -0,0 +1,62 @@
|
||||
import React from 'react' |
||||
|
||||
let invalidEmail = false |
||||
let goodPassword = null |
||||
|
||||
const Login = (props) => ( |
||||
<div> |
||||
<h1 className="title">Login</h1> |
||||
<div className="field"> |
||||
<label className="label">Email</label> |
||||
<div className="control has-icons-left has-icons-right"> |
||||
<input className={`input ${invalidEmail && 'is-danger'}`} |
||||
type="email" |
||||
name="email" |
||||
onChange={props.updateField} |
||||
placeholder="yourname@example.com" /> |
||||
<span className="icon is-small is-left"> |
||||
<i className="fas fa-envelope"></i> |
||||
</span> |
||||
{ invalidEmail && |
||||
<span className="icon is-small is-right"> |
||||
<i className="fas fa-exclamation-triangle"></i> |
||||
</span>} |
||||
</div> |
||||
{ invalidEmail && |
||||
<p className="help is-danger">This email is invalid</p> } |
||||
</div> |
||||
<div className="field"> |
||||
<label className="label">Password</label> |
||||
<div className="control has-icons-left has-icons-right"> |
||||
<input type="password" |
||||
name="password" |
||||
className={`input ${goodPassword === true && 'is-success'}`} |
||||
onChange={props.updateField} |
||||
onKeyPress={(e) => {(e.key === 'Enter') && props.fetchAuth()}} |
||||
placeholder="Password" /> |
||||
<span className="icon is-small is-left"> |
||||
<i className="fas fa-key"></i> |
||||
</span> |
||||
{ goodPassword === true && |
||||
<span className="icon is-small is-right"> |
||||
<i className="fas fa-check"></i> |
||||
</span> } |
||||
</div> |
||||
{ goodPassword === true && |
||||
<p className="help is-success">This password is great</p>} |
||||
</div> |
||||
|
||||
<div className="field is-grouped"> |
||||
<div className="control"> |
||||
<button className="button is-link" |
||||
onClick={props.fetchAuth}> |
||||
Submit</button> |
||||
</div> |
||||
{/* <div className="control"> |
||||
<button className="button is-text">Cancel</button> |
||||
</div> */} |
||||
</div> |
||||
</div> |
||||
) |
||||
|
||||
export default Login |
@ -0,0 +1,96 @@
|
||||
import React, { Component } from 'react' |
||||
import { connect } from 'react-redux' |
||||
import styled from 'styled-components' |
||||
import { Link } from 'react-router-dom' |
||||
|
||||
import { logout } from '../utils/Auth' |
||||
|
||||
const StyledLink = styled(Link)` |
||||
&[data-active] { |
||||
color: red; |
||||
} |
||||
`;
|
||||
|
||||
class Nav extends Component { |
||||
|
||||
state = { expandNavbar: false } |
||||
|
||||
render() { |
||||
const { props } = this |
||||
return ( |
||||
<nav className="navbar"> |
||||
<div className="navbar-brand"> |
||||
<StyledLink to="/" className="navbar-item"> |
||||
<img src="https://static.10ninox.com/goth-rect-640x160.svg" alt="GoTH" width="112" height="28" /> |
||||
</StyledLink> |
||||
<div className="navbar-burger burger" onClick={() => { this.setState({expandNavbar: !this.state.expandNavbar}) }}> |
||||
<span></span> |
||||
<span></span> |
||||
<span></span> |
||||
</div> |
||||
</div> |
||||
|
||||
<div className={`navbar-menu ${(this.state.expandNavbar ? 'is-active' : '')}`}> |
||||
<div className="navbar-start"> |
||||
<StyledLink to="/" className="navbar-item"> |
||||
Home |
||||
</StyledLink> |
||||
<div className="navbar-item has-dropdown is-hoverable"> |
||||
<StyledLink to="/calendar" className="navbar-link"> |
||||
Service |
||||
</StyledLink> |
||||
<div className="navbar-dropdown is-boxed"> |
||||
<StyledLink to={`/calendar/new`} className="navbar-item"> |
||||
<i className="fas fa-plus" /> |
||||
new service calendar |
||||
</StyledLink> |
||||
</div> |
||||
</div> |
||||
<div className="navbar-item has-dropdown is-hoverable"> |
||||
<StyledLink to="/agency" className="navbar-link"> |
||||
Agency |
||||
</StyledLink> |
||||
<div className="navbar-dropdown is-boxed"> |
||||
{props.agency.count > 0 |
||||
&& props.agency.results.map(ele => ( |
||||
<StyledLink key={`nav$a${ele.agency_id}`} to={`/agency/${ele.agency_id}`} className="navbar-item"> |
||||
{ele.name || ele.agency_id} |
||||
</StyledLink> |
||||
))} |
||||
<StyledLink to={`/agency/new`} className="navbar-item"> |
||||
<i className="fas fa-plus" /> |
||||
Add new agency |
||||
</StyledLink> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
<div className="navbar-end"> |
||||
<div className="navbar-item"> |
||||
<div className="field is-grouped"> |
||||
<p className="control"> |
||||
<a className="button is-primary" |
||||
onClick={() => logout()}> |
||||
<span className="icon"> |
||||
<i className="fas fa-sign-out-alt"></i> |
||||
</span> |
||||
<span>Logout</span> |
||||
</a> |
||||
</p> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</nav> |
||||
) |
||||
} |
||||
} |
||||
|
||||
|
||||
const mapStateToProps = state => ({ |
||||
agency: state.agency, |
||||
expandNavbar: false, |
||||
}) |
||||
export default connect( |
||||
mapStateToProps, |
||||
)(Nav) |
@ -0,0 +1,49 @@
|
||||
import React, { Component } from 'react' |
||||
import { connect } from 'react-redux' |
||||
|
||||
import Spinner from './Spinner' |
||||
import { getRoute } from '../actions' |
||||
import store from '../store' |
||||
|
||||
|
||||
class RouteList extends Component { |
||||
|
||||
componentDidMount() { |
||||
const { agencyId } = this.props.match.params |
||||
store.dispatch(getRoute(`agency=${agencyId}`)) |
||||
} |
||||
|
||||
componentWillReceiveProps(newProps) { |
||||
const { agencyId } = newProps.match.params |
||||
if (agencyId !== this.props.match.params.agencyId) |
||||
store.dispatch(getRoute(`agency=${agencyId}`)) |
||||
} |
||||
|
||||
render() { |
||||
const { route } = this.props |
||||
|
||||
return ( |
||||
<div> |
||||
<h3 className="title">Routes</h3> |
||||
<Spinner show={route.fetching} /> |
||||
<ul> |
||||
{route.count === 0 && <li>No route</li>} |
||||
{route.count > 0 |
||||
&& route.results.map(ele => ( |
||||
<li key={ele.route_id}>{ ele.route_id }</li>) |
||||
)} |
||||
</ul> |
||||
</div> |
||||
) |
||||
} |
||||
} |
||||
|
||||
const mapStateToProps = state => ({ |
||||
route: state.route, |
||||
}) |
||||
|
||||
const connectRouteList = connect( |
||||
mapStateToProps, |
||||
)(RouteList) |
||||
|
||||
export default connectRouteList |
@ -0,0 +1,9 @@
|
||||
import React from 'react' |
||||
|
||||
const Spinner = (props) => ( |
||||
<span> |
||||
{props.show && <span><i className="fas fa-spinner" /></span>} |
||||
</span> |
||||
) |
||||
|
||||
export default Spinner |
@ -1,9 +1,45 @@
|
||||
|
||||
export const FIRST_INCREMENT_COUNTER = 'FIRST_INCREMENT_COUNTER' |
||||
export const FIRST_ASSIGN_ID = 'FIRST_ASSIGN_ID' |
||||
export const SET_TOKEN = 'SET_TOKEN' |
||||
|
||||
// export const SET_TOKEN = 'SET_TOKEN'
|
||||
|
||||
export const REQUEST_LOGIN = 'REQUEST_LOGIN' |
||||
export const SUCCESS_LOGIN = 'SUCCESS_LOGIN' |
||||
export const FAILED_LOGIN = 'FAILED_LOGIN' |
||||
export const SUCCESS_LOGOUT = 'SUCCESS_LOGOUT' |
||||
|
||||
|
||||
export const AGENCY_REQUEST = 'AGENCY_REQUEST' |
||||
export const AGENCY_FAILURE = 'AGENCY_FAILURE' |
||||
export const AGENCY_SUCCESS = 'AGENCY_SUCCESS' |
||||
// below items are SUCCESS for other tasks
|
||||
export const AGENCY_CREATE = 'AGENCY_CREATE' |
||||
export const AGENCY_DELETE = 'AGENCY_DELETE' |
||||
export const AGENCY_UPDATE = 'AGENCY_UPDATE' |
||||
|
||||
|
||||
export const ROUTE_REQUEST = 'ROUTE_REQUEST' |
||||
export const ROUTE_FAILURE = 'ROUTE_FAILURE' |
||||
export const ROUTE_SUCCESS = 'ROUTE_SUCCESS' |
||||
// below items are SUCCESS for other tasks
|
||||
export const ROUTE_CREATE = 'ROUTE_CREATE' |
||||
export const ROUTE_DELETE = 'ROUTE_DELETE' |
||||
export const ROUTE_UPDATE = 'ROUTE_UPDATE' |
||||
|
||||
|
||||
export const CALENDAR_REQUEST = 'CALENDAR_REQUEST' |
||||
export const CALENDAR_FAILURE = 'CALENDAR_FAILURE' |
||||
export const CALENDAR_SUCCESS = 'CALENDAR_SUCCESS' |
||||
// below items are SUCCESS for other tasks
|
||||
export const CALENDAR_CREATE = 'CALENDAR_CREATE' |
||||
export const CALENDAR_DELETE = 'CALENDAR_DELETE' |
||||
export const CALENDAR_UPDATE = 'CALENDAR_UPDATE' |
||||
|
||||
|
||||
export const FAREATTR_REQUEST = 'FAREATTR_REQUEST' |
||||
export const FAREATTR_FAILURE = 'FAREATTR_FAILURE' |
||||
export const FAREATTR_SUCCESS = 'FAREATTR_SUCCESS' |
||||
// below items are SUCCESS for other tasks
|
||||
export const FAREATTR_CREATE = 'FAREATTR_CREATE' |
||||
export const FAREATTR_DELETE = 'FAREATTR_DELETE' |
||||
export const FAREATTR_UPDATE = 'FAREATTR_UPDATE' |
||||
|
@ -1,3 +1,5 @@
|
||||
|
||||
export const URL = process.env.API_URL || 'https://showtimes.everyday.in.th' |
||||
export const URL = process.env.API_URL || '//localhost:8000' |
||||
export const API_PREFIX = process.env.API_PREFIX || '/v1' |
||||
export const API_URL = `${URL}${API_PREFIX}` |
||||
export const LOGIN = '/api-token-auth/' |
||||
|
@ -1,30 +0,0 @@
|
||||
import React from 'react' |
||||
import { Link } from 'react-router-dom' |
||||
import { connect } from 'react-redux' |
||||
import { getToken, loggedIn } from '../reducers/auth' |
||||
import { fetchAuth } from '../actions' |
||||
|
||||
const Gone = (props) => ( |
||||
<div> |
||||
Gone { props.match.params.id } |
||||
|
||||
<hr /> |
||||
<Link to={`/`}>Main</Link> |
||||
|
||||
<hr /> |
||||
{props.loggedIn ? 'Logged in': 'Nah anonymous'} |
||||
<hr /> |
||||
|
||||
<button onClick={_ => props.fetchAuth("sipp11", "gi23ft")}>Login</button> |
||||
</div> |
||||
) |
||||
|
||||
|
||||
const mapStateToProps = state => ({ |
||||
token: getToken(state.auth), |
||||
auth: state.auth, |
||||
}) |
||||
export default connect( |
||||
mapStateToProps, |
||||
{ loggedIn, fetchAuth } |
||||
)(Gone) |
@ -1,25 +1,64 @@
|
||||
import React from 'react' |
||||
import React, { Component } from 'react' |
||||
import { connect } from 'react-redux' |
||||
import { Link } from 'react-router-dom' |
||||
import { Redirect, Route, Switch } from 'react-router-dom' |
||||
|
||||
import { getCounter } from '../reducers/first' |
||||
import { incFirstCounter } from '../actions' |
||||
import { loggedIn } from '../reducers/auth' |
||||
import { getAgency } from '../actions' |
||||
|
||||
const Main = (props) => ( |
||||
<div> |
||||
Main |
||||
import Nav from '../components/Nav' |
||||
import AgencyList from '../components/AgencyList' |
||||
import AgencyItem from '../components/AgencyItem' |
||||
import AgencyForm from '../components/AgencyForm' |
||||
import { LOGIN_PATH } from '../constants/path' |
||||
import store from '../store' |
||||
|
||||
<Link to={`/roster/12`}>12</Link> |
||||
<p>Counter: { props.counter }</p> |
||||
<p><button onClick={() => props.incFirstCounter() }>Increment</button></p> |
||||
</div> |
||||
) |
||||
class Main extends Component { |
||||
|
||||
componentWillMount() { |
||||
const { count } = this.props.agency |
||||
if (count === 0) |
||||
store.dispatch(getAgency()) |
||||
} |
||||
|
||||
// componentWillReceiveProps(nextProps) {
|
||||
// const oldPathname = this.props.location.pathname
|
||||
// const { pathname } = nextProps.location
|
||||
// console.log(oldPathname, pathname)
|
||||
// }
|
||||
|
||||
render() { |
||||
const { match, loggedIn } = this.props |
||||
if (loggedIn !== true) { |
||||
return <Redirect to={LOGIN_PATH} /> |
||||
} |
||||
return ( |
||||
<div> |
||||
<Nav /> |
||||
<div className="container is-widescreen"> |
||||
<Switch> |
||||
<Route exact path={`${match.url}agency/new`} component={AgencyForm} /> |
||||
<Route exact path={`${match.url}agency/:agencyId`} component={AgencyItem} /> |
||||
<Route path={`${match.url}agency/:agencyId/edit`} component={AgencyForm} /> |
||||
<Route path={`${match.url}agency/:agencyId/:agencyChild`} component={AgencyItem} /> |
||||
<Route exact path={`${match.url}agency`} component={AgencyList} /> |
||||
|
||||
<Route exact path={`${match.url}calendar/new`} component={AgencyForm} /> |
||||
<Route exact path={`${match.url}calendar/:serviceId`} component={AgencyForm} /> |
||||
<Route path={`${match.url}calendar/:serviceId/edit`} component={AgencyForm} /> |
||||
<Route exact path={`${match.url}calendar`} component={AgencyList} /> |
||||
</Switch> |
||||
</div> |
||||
</div> |
||||
) |
||||
} |
||||
} |
||||
|
||||
|
||||
const mapStateToProps = state => ({ |
||||
counter: getCounter(state.first) |
||||
loggedIn: loggedIn(state.auth), |
||||
agency: state.agency, |
||||
}) |
||||
export default connect( |
||||
mapStateToProps, |
||||
{ incFirstCounter } |
||||
)(Main) |
||||
{ } |
||||
)(Main) |
||||
|
@ -0,0 +1,81 @@
|
||||
import React, { Component } from 'react' |
||||
import { Redirect } from 'react-router-dom' |
||||
import { connect } from 'react-redux' |
||||
|
||||
import { getToken, loggedIn } from '../reducers/auth' |
||||
import { fetchAuth } from '../actions' |
||||
|
||||
import Login from '../components/Login' |
||||
|
||||
class Public extends Component { |
||||
state = { |
||||
email: null, password: null |
||||
} |
||||
constructor() { |
||||
super() |
||||
this.updateField = this.updateField.bind(this) |
||||
this.handleFetchAuth = this.handleFetchAuth.bind(this) |
||||
} |
||||
|
||||
updateField(evt) { |
||||
const { name, value } = evt.target |
||||
let newState = {} |
||||
newState[name] = value |
||||
this.setState(newState) |
||||
} |
||||
|
||||
handleFetchAuth() { |
||||
const { dispatch } = this.props |
||||
const { email, password } = this.state |
||||
dispatch(fetchAuth(email, password)) |
||||
} |
||||
|
||||
render() { |
||||
const { loggedIn } = this.props |
||||
return ( |
||||
<div style={{minHeight: '100vh'}} |
||||
className="columns is-mobile is-centered is-desktop is-vcentered"> |
||||
<div className="column is-centered is-12-mobile is-two-thirds-tablet |
||||
is-half-desktop is-one-third-widescreen is-one-quarter-fullhd"> |
||||
<div className="card"> |
||||
<div className="card-content"> |
||||
{/* {<h1 className="title">Public</h1> { props.match.params.id } |
||||
<Link to={`/`}>Main</Link> |
||||
<hr />} */} |
||||
{loggedIn === true |
||||
? <Redirect to={'/'} /> |
||||
: <Login |
||||
fetchAuth={this.handleFetchAuth} |
||||
updateField={this.updateField} /> } |
||||
{/* <hr /> */} |
||||
</div> |
||||
{/* <footer className="card-footer"> |
||||
<p className="card-footer-item"> |
||||
<span> |
||||
<button className="button is-info" |
||||
onClick={_ => props.fetchAuth("sipp11", "gi23ft")}> |
||||
Login</button> |
||||
</span> |
||||
</p> |
||||
</footer> */} |
||||
</div> |
||||
</div> |
||||
</div> |
||||
) |
||||
} |
||||
} |
||||
|
||||
const mapStateToProps = state => ({ |
||||
token: getToken(state.auth), |
||||
loggedIn: loggedIn(state.auth), |
||||
}) |
||||
const mapDispatchToProps = (dispatch) => { |
||||
return { |
||||
dispatch, |
||||
fetchAuth, |
||||
} |
||||
} |
||||
export default connect( |
||||
mapStateToProps, |
||||
mapDispatchToProps, |
||||
)(Public) |
@ -0,0 +1,76 @@
|
||||
import { |
||||
AGENCY_CREATE, AGENCY_DELETE, AGENCY_UPDATE, |
||||
AGENCY_REQUEST, AGENCY_SUCCESS, AGENCY_FAILURE, |
||||
} from '../constants/ActionTypes' |
||||
|
||||
|
||||
const agencyInitState = { |
||||
results: [], |
||||
next: null, |
||||
count: 0, |
||||
fetching: false, |
||||
} |
||||
const agency = (state = agencyInitState, action) => { |
||||
switch(action.type) { |
||||
case AGENCY_REQUEST: |
||||
return { |
||||
...state, |
||||
fetching: true, |
||||
} |
||||
case AGENCY_SUCCESS: |
||||
const { count, next, prev, results } = action.payload |
||||
return { |
||||
...state, |
||||
fetching: false, |
||||
count, |
||||
next, |
||||
results: [ |
||||
...(prev ? state.results : []), |
||||
...results, |
||||
] |
||||
} |
||||
case AGENCY_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 AGENCY_CREATE: |
||||
return { |
||||
...state, |
||||
fetching: false, |
||||
count: state.count + 1, |
||||
results: [ |
||||
...state.results, |
||||
action.payload, |
||||
] |
||||
} |
||||
case AGENCY_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 AGENCY_FAILURE: |
||||
return { |
||||
...state, |
||||
fetching: false, |
||||
} |
||||
default: |
||||
return state; |
||||
} |
||||
} |
||||
|
||||
export default agency |
@ -0,0 +1,79 @@
|
||||
import { |
||||
CALENDAR_CREATE, CALENDAR_DELETE, CALENDAR_UPDATE, |
||||
CALENDAR_REQUEST, CALENDAR_SUCCESS, CALENDAR_FAILURE, |
||||
} from '../constants/ActionTypes' |
||||
|
||||
|
||||
const calendarInitState = { |
||||
results: [], |
||||
next: null, |
||||
count: 0, |
||||
fetching: false, |
||||
query: '', |
||||
} |
||||
const calendar = (state = calendarInitState, action) => { |
||||
switch(action.type) { |
||||
case CALENDAR_REQUEST: |
||||
const { query } = action.meta |
||||
return { |
||||
...state, |
||||
fetching: true, |
||||
query, |
||||
} |
||||
case CALENDAR_SUCCESS: |
||||
const { count, next, prev, results } = action.payload |
||||
return { |
||||
...state, |
||||
fetching: false, |
||||
count, |
||||
next, |
||||
results: [ |
||||
...( (prev) ? state.results : [] ), |
||||
...results, |
||||
] |
||||
} |
||||
case CALENDAR_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 CALENDAR_CREATE: |
||||
return { |
||||
...state, |
||||
fetching: false, |
||||
count: state.count + 1, |
||||
results: [ |
||||
...state.results, |
||||
action.payload, |
||||
] |
||||
} |
||||
case CALENDAR_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 CALENDAR_FAILURE: |
||||
return { |
||||
...state, |
||||
fetching: false, |
||||
} |
||||
default: |
||||
return state; |
||||
} |
||||
} |
||||
|
||||
export default calendar |
@ -0,0 +1,79 @@
|
||||
import { |
||||
CALENDAR_CREATE, CALENDAR_DELETE, CALENDAR_UPDATE, |
||||
CALENDAR_REQUEST, CALENDAR_SUCCESS, CALENDAR_FAILURE, |
||||
} from '../constants/ActionTypes' |
||||
|
||||
|
||||
const calendarInitState = { |
||||
results: [], |
||||
next: null, |
||||
count: 0, |
||||
fetching: false, |
||||
query: '', |
||||
} |
||||
const calendar = (state = calendarInitState, action) => { |
||||
switch(action.type) { |
||||
case CALENDAR_REQUEST: |
||||
const { query } = action.meta |
||||
return { |
||||
...state, |
||||
fetching: true, |
||||
query, |
||||
} |
||||
case CALENDAR_SUCCESS: |
||||
const { count, next, prev, results } = action.payload |
||||
return { |
||||
...state, |
||||
fetching: false, |
||||
count, |
||||
next, |
||||
results: [ |
||||
...( (prev) ? state.results : [] ), |
||||
...results, |
||||
] |
||||
} |
||||
case CALENDAR_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 CALENDAR_CREATE: |
||||
return { |
||||
...state, |
||||
fetching: false, |
||||
count: state.count + 1, |
||||
results: [ |
||||
...state.results, |
||||
action.payload, |
||||
] |
||||
} |
||||
case CALENDAR_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 CALENDAR_FAILURE: |
||||
return { |
||||
...state, |
||||
fetching: false, |
||||
} |
||||
default: |
||||
return state; |
||||
} |
||||
} |
||||
|
||||
export default calendar |
@ -1,8 +1,14 @@
|
||||
import { combineReducers } from 'redux' |
||||
import first from './first' |
||||
import auth from './auth' |
||||
import agency from './agency' |
||||
import route from './route' |
||||
import fareattr from './fareattr' |
||||
|
||||
export default combineReducers({ |
||||
auth, |
||||
first, |
||||
agency, |
||||
route, |
||||
fareattr, |
||||
}) |
||||
|
@ -0,0 +1,79 @@
|
||||
import { |
||||
ROUTE_CREATE, ROUTE_DELETE, ROUTE_UPDATE, |
||||
ROUTE_REQUEST, ROUTE_SUCCESS, ROUTE_FAILURE, |
||||
} from '../constants/ActionTypes' |
||||
|
||||
|
||||
const routeInitState = { |
||||
results: [], |
||||
next: null, |
||||
count: 0, |
||||
fetching: false, |
||||
query: '', |
||||
} |
||||
const route = (state = routeInitState, action) => { |
||||
switch(action.type) { |
||||
case ROUTE_REQUEST: |
||||
const { query } = action.meta |
||||
return { |
||||
...state, |
||||
fetching: true, |
||||
query, |
||||
} |
||||
case ROUTE_SUCCESS: |
||||
const { count, next, prev, results } = action.payload |
||||
return { |
||||
...state, |
||||
fetching: false, |
||||
count, |
||||
next, |
||||
results: [ |
||||
...( (prev) ? state.results : [] ), |
||||
...results, |
||||
] |
||||
} |
||||
case ROUTE_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 ROUTE_CREATE: |
||||
return { |
||||
...state, |
||||
fetching: false, |
||||
count: state.count + 1, |
||||
results: [ |
||||
...state.results, |
||||
action.payload, |
||||
] |
||||
} |
||||
case ROUTE_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 ROUTE_FAILURE: |
||||
return { |
||||
...state, |
||||
fetching: false, |
||||
} |
||||
default: |
||||
return state; |
||||
} |
||||
} |
||||
|
||||
export default route |
@ -1,12 +1,20 @@
|
||||
import axios from 'axios' |
||||
import store from '../store' |
||||
import { URL } from '../config/Api' |
||||
import { URL, API_PREFIX } from '../constants/Api' |
||||
|
||||
export const apiClient = function() { |
||||
const token = store.getState().token |
||||
export const apiClient = function(url) { |
||||
const token = store.getState().auth.token |
||||
const params = { |
||||
baseURL: URL, |
||||
headers: {'Authorization': 'Token ' + token} |
||||
baseURL: `${URL}${API_PREFIX}${url}`, |
||||
headers: {'Authorization': 'Bearer ' + token} |
||||
} |
||||
return axios.create(params) |
||||
} |
||||
return axios.get(params) |
||||
} |
||||
|
||||
export const RSAAHeaders = () => { |
||||
const token = store.getState().auth.token |
||||
return { |
||||
'Content-Type': 'application/json', |
||||
'Authorization': 'Bearer ' + token, |
||||
} |
||||
} |
||||
|
Loading…
Reference in new issue