diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..eb260ed --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "todo-tree.flat": true, + "todo-tree.grouped": false, + "todo-tree.expanded": true +} \ No newline at end of file diff --git a/src/actions/index.js b/src/actions/index.js index 1317976..97423d2 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -91,6 +91,13 @@ export const polygonReset = () => { } } +export const mapCenterUpdate = (lat, lon) => { + return { + type: types.GEO_MAPCENTER_UPDATE, + payload: [lat, lon] + } +} + // AGENCY export const getAgency = () => ({ diff --git a/src/actions/route.js b/src/actions/route.js new file mode 100644 index 0000000..0996619 --- /dev/null +++ b/src/actions/route.js @@ -0,0 +1,64 @@ +import { RSAA } from 'redux-api-middleware' + +import * as types from '../constants/ActionTypes' +import { RSAAHeaders } from '../utils/ApiClient' +import { API_URL } from '../constants/Api' + + +export const getRoute = (query) => ({ + [RSAA]: { + endpoint: `${API_URL}/route/?${query || ''}`, + method: 'GET', + headers: RSAAHeaders, + bailout: (state) => state.route.fetching, + types: [ + types.ROUTE_REQUEST, + types.ROUTE_SUCCESS, + types.ROUTE_FAILURE, + ] + } +}) + +export const updateRoute = (id, body) => ({ + [RSAA]: { + endpoint: `${API_URL}/route/${id}/`, + body: JSON.stringify(body), + method: 'PATCH', + headers: RSAAHeaders, + types: [ + types.ROUTE_REQUEST, + types.ROUTE_UPDATE, + types.ROUTE_FAILURE, + ] + } +}) + +export const createRoute = (body) => ({ + [RSAA]: { + endpoint: `${API_URL}/route/`, + body: JSON.stringify(body), + method: 'POST', + headers: RSAAHeaders, + types: [ + types.ROUTE_REQUEST, + types.ROUTE_CREATE, + types.ROUTE_FAILURE, + ] + } +}) + +export const deleteRoute = (id) => ({ + [RSAA]: { + endpoint: `${API_URL}/route/${id}/`, + method: 'DELETE', + headers: RSAAHeaders, + types: [ + types.ROUTE_REQUEST, + { + type: types.ROUTE_DELETE, + meta: { id } + }, + types.ROUTE_FAILURE, + ] + } +}) diff --git a/src/actions/stop.js b/src/actions/stop.js new file mode 100644 index 0000000..ad77118 --- /dev/null +++ b/src/actions/stop.js @@ -0,0 +1,64 @@ +import { RSAA } from 'redux-api-middleware' + +import * as types from '../constants/ActionTypes' +import { RSAAHeaders } from '../utils/ApiClient' +import { API_URL } from '../constants/Api' + + +export const getStop = (query) => ({ + [RSAA]: { + endpoint: `${API_URL}/stop/?${query || ''}`, + method: 'GET', + headers: RSAAHeaders, + bailout: (state) => state.stop.fetching, + types: [ + types.STOP_REQUEST, + types.STOP_SUCCESS, + types.STOP_FAILURE, + ] + } +}) + +export const updateStop = (id, body) => ({ + [RSAA]: { + endpoint: `${API_URL}/stop/${id}/`, + body: JSON.stringify(body), + method: 'PATCH', + headers: RSAAHeaders, + types: [ + types.STOP_REQUEST, + types.STOP_UPDATE, + types.STOP_FAILURE, + ] + } +}) + +export const createStop = (body) => ({ + [RSAA]: { + endpoint: `${API_URL}/stop/`, + body: JSON.stringify(body), + method: 'POST', + headers: RSAAHeaders, + types: [ + types.STOP_REQUEST, + types.STOP_CREATE, + types.STOP_FAILURE, + ] + } +}) + +export const deleteStop = (id) => ({ + [RSAA]: { + endpoint: `${API_URL}/stop/${id}/`, + method: 'DELETE', + headers: RSAAHeaders, + types: [ + types.STOP_REQUEST, + { + type: types.STOP_DELETE, + meta: { id } + }, + types.STOP_FAILURE, + ] + } +}) diff --git a/src/actions/stoptime.js b/src/actions/stoptime.js index f574ca0..b4822a6 100644 --- a/src/actions/stoptime.js +++ b/src/actions/stoptime.js @@ -10,7 +10,7 @@ export const getStopTime = (query) => ({ endpoint: `${API_URL}/stoptime/?${query || ''}`, method: 'GET', headers: RSAAHeaders, - bailout: (state) => state.stoptime.fetching, + bailout: (state) => state.stoptime.fetching || state.stoptime.query === query, types: [ { type: types.STOPTIME_REQUEST, diff --git a/src/components/AgencyForm.js b/src/components/AgencyForm.js index 57ebae2..2e4c665 100644 --- a/src/components/AgencyForm.js +++ b/src/components/AgencyForm.js @@ -184,7 +184,7 @@ const mapStateToProps = state => ({ agency: state.agency }) -const connectAgencyList = connect( +const connectAgencyForm = connect( mapStateToProps, )(AgencyForm) -export default connectAgencyList +export default connectAgencyForm diff --git a/src/components/AgencyItem.js b/src/components/AgencyItem.js index f1d8ac0..c6e93cf 100644 --- a/src/components/AgencyItem.js +++ b/src/components/AgencyItem.js @@ -1,10 +1,11 @@ import React, { Component } from 'react' import styled from 'styled-components' import { connect } from 'react-redux' -import { Link, Redirect, Route } from 'react-router-dom' +import { Link, Redirect, Route, Switch} from 'react-router-dom' import Spinner from './Spinner' import RouteList from './RouteList' +import RouteForm from './RouteForm' import FareAttrList from './FareAttrList' const StyledAgencyItem = styled.div` @@ -62,10 +63,13 @@ class AgencyItem extends Component {
  • Edit
  • - - - - + + + + + + + {agencyChild === undefined &&

    Pick options on the tab

    } diff --git a/src/components/Breadcrumb.js b/src/components/Breadcrumb.js index 346c779..8dc0ea2 100644 --- a/src/components/Breadcrumb.js +++ b/src/components/Breadcrumb.js @@ -10,15 +10,19 @@ margin-bottom: 5px !important; const Breadcrumb = (props) => ( - +
      -
    • ALL
    • +
    • Stop
    • +
    • Agency
    • {props.agencyId &&
    • {props.agencyId}
    • } {props.routeId &&
    • {props.routeId}
    • } + {props.stopId &&
    • + + {props.stopId}
    • }
    ) diff --git a/src/components/FloatPane.js b/src/components/FloatPane.js index 9b0181f..dceed6e 100644 --- a/src/components/FloatPane.js +++ b/src/components/FloatPane.js @@ -1,11 +1,14 @@ import React, { Component } from 'react' -import { Link, Route } from 'react-router-dom' +import { Link, Route, Switch } from 'react-router-dom' import { connect } from 'react-redux' import styled from 'styled-components' import { logout } from '../utils/Auth' import RouteList from './RouteList' import RouteDetail from './RouteDetail' +import StopList from './StopList' +import StopForm from './StopForm' +import RouteForm from './RouteForm' import Breadcrumb from './Breadcrumb' import { polygonReset } from '../actions' import store from '../store' @@ -61,6 +64,11 @@ const SimpleAgencyList = (props) => (

    Agency

    +

    + + New agency + +

    {props.agencies.map(ele => ( {ele.name} @@ -133,15 +141,29 @@ class FloatPane extends Component { {this.renderTopLevel(loggedIn)} - ( - )} /> + + ( + )} /> + ( + )} /> + + ( + )} /> + ( + )} /> + ( + )} /> + ( + )} /> + ( )} /> - - ( - )} /> diff --git a/src/components/RouteDetail.js b/src/components/RouteDetail.js index ea8c102..ebacc45 100644 --- a/src/components/RouteDetail.js +++ b/src/components/RouteDetail.js @@ -1,6 +1,6 @@ import React, { Component } from 'react' import styled from 'styled-components' -import { Link, Route } from 'react-router-dom' +import { Link, Route, Redirect } from 'react-router-dom' import { connect } from 'react-redux' import Spinner from './Spinner' @@ -8,12 +8,6 @@ import { getRoute, polygonUpdate } from '../actions' import { getStopTime } from '../actions/stoptime' import store from '../store' -/* -TODO: add shape -TODO: add Route detail - -*/ - const StyledTripDesc = styled.div` font-size: 0.87rem; line-height: 0.85rem; @@ -58,7 +52,7 @@ const TripList = (props) => (
    {props.trips.map(ele => ( { store.dispatch(getStopTime(`trip=${ele.id}`))}}> + onClick={() => { store.dispatch(getStopTime(`trip=${ele.id}&limit=100`))}}> {ele.trip_id} (ID {ele.id})  @@ -134,7 +128,10 @@ class RouteDetail extends Component { const { routeId, agencyId, routeParams } = match.params const tRoute = route.results.filter(ele => ele.route_id === routeId) if (tRoute.length === 0) { - return + if (route.fetching) + return + + return } const item = tRoute[0] const baseUrl = `/map/${agencyId}/route/${routeId}` @@ -147,6 +144,7 @@ class RouteDetail extends Component { detail trip fare + edit

    ( )} /> diff --git a/src/components/RouteForm.js b/src/components/RouteForm.js new file mode 100644 index 0000000..12a85d4 --- /dev/null +++ b/src/components/RouteForm.js @@ -0,0 +1,208 @@ +import React, { Component } from 'react' +import styled from 'styled-components' +import { connect } from 'react-redux' +import { Redirect, Link } from 'react-router-dom' + +import HorizontalInput from './parts/HorizontalInput' +import { updateRoute, createRoute, deleteRoute } from '../actions/route'; +import store from '../store' + +const StyledRouteForm = styled.div` +padding: 1rem; +background: #fafafa; +` + +// TODO: need to deal with shapes + +class RouteForm extends Component { + + state = { + id: null, + agency: null, + route_id: "", + short_name: "", + long_name: "", + desc: "", + route_type: "2", + route_url: "", + route_color: "", + route_text_color: "", + route_sort_order: "0", + } + + constructor() { + super() + this.handleChange = this.handleChange.bind(this) + this.handleSubmit = this.handleSubmit.bind(this) + this.handleDelete = this.handleDelete.bind(this) + this.renderForm = this.renderForm.bind(this) + } + + handleChange(evt) { + let updated = {} + updated[evt.target.name] = evt.target.value + this.setState(updated) + } + + handleSubmit() { + const { id } = this.state + let body = { ...this.state } + delete body.id + delete body.farerule_set + delete body.trip_set + delete body.geojson + if (id !== null) { + store.dispatch(updateRoute(id, body)) + } else { + store.dispatch(createRoute(body)) + } + this.setState({justSubmit: true}) + } + + handleDelete() { + const { id } = this.state + store.dispatch(deleteRoute(id)) + this.setState({justSubmit: true}) + } + + componentWillMount() { + const { props } = this + const { agencyId, routeId } = props.match.params + const { results } = props.route + const ones = results.filter(ele => ele.route_id === routeId) + if (ones.length > 0) { + this.setState(ones[0]) + props.updateBreadcrumb({ agencyId, routeId }) + } else { + const agencies = props.agency.results.filter(ele => ele.agency_id === agencyId) + if (agencies.length > 0) { + let d = {} + d["agency_id"] = agencies[0].id + this.setState(d) + props.updateBreadcrumb({ agencyId, routeId: 'new' }) + } + } + } + + renderForm() { + const one = this.state + const { agencyId, routeId } = this.props.match.params + const { agency } = this.props + const agencies = agency.results.filter(ele => ele.agency_id === agencyId) + const { fetching } = this.props.route + return ( + +

    {one.route_id || 'New Route'}  

    +
    + + + + + + + + + + + + + + + + + +
    + +
    +
    + +
    + {one.id !== null &&
    + +
    } +
    + {agencies[0] && + Cancel} +
    +
    +
    + ) + } + + render () { + const one = this.state + const { fetching } = this.props.agency + // redirect to view page if no data + const { agencyId } = this.props.match.params + // redirect to agency list if submitted + if (one.justSubmit === true && !fetching) { + return + } + return this.renderForm() + } + +} + + +const mapStateToProps = state => ({ + agency: state.agency, + route: state.route, +}) + +const connectRouteForm = connect( + mapStateToProps, +)(RouteForm) +export default connectRouteForm diff --git a/src/components/RouteList.js b/src/components/RouteList.js index c5a93ee..3ed5d07 100644 --- a/src/components/RouteList.js +++ b/src/components/RouteList.js @@ -37,7 +37,7 @@ class RouteList extends Component { return (