Skip to content

Commit 428ba0c

Browse files
committed
feat(form): add plan first/previous/next/last buttons
1 parent 6058031 commit 428ba0c

3 files changed

Lines changed: 151 additions & 5 deletions

File tree

lib/components/narrative/narrative-itineraries-header.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import styled from 'styled-components'
22

33
import Icon from '../narrative/icon'
4+
5+
import PlanFirstLastButtons from './plan-first-last-buttons'
46
import SaveTripButton from './save-trip-button'
57

68
const IssueButton = styled.button`
@@ -46,7 +48,8 @@ export default function NarrativeItinerariesHeader ({
4648
className='options header'
4749
style={{
4850
alignItems: 'end',
49-
display: 'flex'
51+
display: 'flex',
52+
flexWrap: 'wrap'
5053
}}
5154
>
5255
{(itineraryIsExpanded || showingErrors)
@@ -57,7 +60,6 @@ export default function NarrativeItinerariesHeader ({
5760
>
5861
<Icon type='arrow-left' /> View all options
5962
</button>
60-
6163
{itineraryIsExpanded && (
6264
// marginLeft: auto is a way of making something "float right"
6365
// within a flex container
@@ -72,9 +74,7 @@ export default function NarrativeItinerariesHeader ({
7274
style={{flexGrow: 1}}
7375
title={titleText}
7476
>
75-
<span style={{
76-
marginRight: '10px'
77-
}}>
77+
<span style={{marginRight: '10px'}}>
7878
{resultText}
7979
</span>
8080
{errors.length > 0 && (
@@ -105,6 +105,7 @@ export default function NarrativeItinerariesHeader ({
105105
<option value='COST'>Cost</option>
106106
</select>
107107
</div>
108+
<PlanFirstLastButtons />
108109
</>
109110
}
110111
</div>
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import {OTP_API_DATE_FORMAT, OTP_API_TIME_FORMAT} from '@opentripplanner/core-utils/lib/time'
2+
import moment from 'moment'
3+
import React, {Component} from 'react'
4+
import {Button} from 'react-bootstrap'
5+
import {connect} from 'react-redux'
6+
7+
import * as apiActions from '../../actions/api'
8+
import * as formActions from '../../actions/form'
9+
import {getFirstStopId} from '../../util/itinerary'
10+
import {
11+
getActiveItineraries,
12+
getActiveSearch,
13+
getResponsesWithErrors,
14+
getVisibleItineraryIndex
15+
} from '../../util/state'
16+
17+
const serviceBreakTime = '03:00am'
18+
const NINETY_SECONDS = 90000
19+
20+
class PlanFirstLastButtons extends Component {
21+
_planFirst = () => {
22+
const {activeItinerary, currentQuery, itineraries, routingQuery, setQueryParam} = this.props
23+
const itinerary = itineraries[activeItinerary]
24+
const params = {
25+
startTransitStopId: getFirstStopId(itinerary),
26+
date: moment(currentQuery.date).format(OTP_API_DATE_FORMAT),
27+
time: serviceBreakTime,
28+
departArrive: 'DEPART',
29+
originalQueryTime: currentQuery.time
30+
}
31+
setQueryParam(params)
32+
routingQuery()
33+
}
34+
35+
_planPrevious = () => {
36+
const {activeItinerary, currentQuery, itineraries, routingQuery, setQueryParam} = this.props
37+
const itinerary = itineraries[activeItinerary]
38+
const newEndTime = moment(itinerary.endTime - NINETY_SECONDS)
39+
const time = newEndTime.format(OTP_API_TIME_FORMAT)
40+
console.log(activeItinerary, itinerary, itinerary.endTime, time)
41+
const params = {
42+
startTransitStopId: getFirstStopId(itinerary),
43+
time,
44+
date: newEndTime.format(OTP_API_DATE_FORMAT),
45+
departArrive: 'ARRIVE',
46+
originalQueryTime: currentQuery.time
47+
}
48+
setQueryParam(params)
49+
routingQuery()
50+
}
51+
52+
_planNext = () => {
53+
const {activeItinerary, currentQuery, itineraries, routingQuery, setQueryParam} = this.props
54+
const itinerary = itineraries[activeItinerary]
55+
const newStartTime = moment(itinerary.startTime + NINETY_SECONDS)
56+
const time = newStartTime.format(OTP_API_TIME_FORMAT)
57+
console.log(time)
58+
const params = {
59+
startTransitStopId: getFirstStopId(itinerary),
60+
time,
61+
date: newStartTime.format(OTP_API_DATE_FORMAT),
62+
departArrive: 'DEPART',
63+
originalQueryTime: currentQuery.time
64+
}
65+
setQueryParam(params)
66+
routingQuery()
67+
}
68+
69+
_planLast = () => {
70+
const {activeItinerary, currentQuery, itineraries, routingQuery, setQueryParam} = this.props
71+
const itinerary = itineraries[activeItinerary]
72+
const params = {
73+
startTransitStopId: getFirstStopId(itinerary),
74+
date: moment(currentQuery.date).format(OTP_API_DATE_FORMAT),
75+
time: serviceBreakTime,
76+
departArrive: 'ARRIVE',
77+
originalQueryTime: currentQuery.time
78+
}
79+
setQueryParam(params)
80+
routingQuery()
81+
}
82+
83+
render () {
84+
console.log(moment((this.props.itineraries[this.props.activeItinerary] || {}).endTime).format(OTP_API_TIME_FORMAT))
85+
return (
86+
<div style={{alignItems: 'stretch', display: 'flex', flexDirection: 'row', width: '100%'}}>
87+
<Button onClick={this._planFirst} style={{flex: 1}}>
88+
First
89+
</Button>
90+
<Button onClick={this._planPrevious} style={{flex: 1}}>
91+
Previous
92+
</Button>
93+
<Button onClick={this._planNext} style={{flex: 1}}>
94+
Next
95+
</Button>
96+
<Button onClick={this._planLast} style={{flex: 1}}>
97+
Last
98+
</Button>
99+
</div>
100+
)
101+
}
102+
}
103+
104+
const mapStateToProps = (state, ownProps) => {
105+
const activeSearch = getActiveSearch(state.otp)
106+
const activeItinerary = activeSearch && activeSearch.activeItinerary
107+
const { errorMessages, modes } = state.otp.config
108+
const { sort } = state.otp.filter
109+
const pending = activeSearch ? Boolean(activeSearch.pending) : false
110+
const itineraries = getActiveItineraries(state.otp)
111+
112+
return {
113+
// swap out realtime itineraries with non-realtime depending on boolean
114+
activeItinerary,
115+
activeLeg: activeSearch && activeSearch.activeLeg,
116+
activeSearch,
117+
activeStep: activeSearch && activeSearch.activeStep,
118+
currentQuery: state.otp.currentQuery,
119+
errors: getResponsesWithErrors(state.otp),
120+
errorMessages,
121+
itineraries,
122+
// use a key so that the NarrativeItineraries component and its state is
123+
// reset each time a new search is shown
124+
key: state.otp.activeSearchId,
125+
modes,
126+
pending,
127+
sort,
128+
visibleItinerary: getVisibleItineraryIndex(state)
129+
}
130+
}
131+
132+
const mapDispatchToProps = {
133+
// beginCallIfNeeded: callTakerActions.beginCallIfNeeded,
134+
// findRoutes: apiActions.findRoutes,
135+
routingQuery: apiActions.routingQuery,
136+
// setGroupSize: fieldTripActions.setGroupSize,
137+
setQueryParam: formActions.setQueryParam
138+
}
139+
140+
export default connect(mapStateToProps, mapDispatchToProps)(PlanFirstLastButtons)

lib/util/itinerary.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { latLngBounds } from 'leaflet'
22
import moment from 'moment'
33
import coreUtils from '@opentripplanner/core-utils'
4+
45
import { WEEKDAYS, WEEKEND_DAYS } from './monitored-trip'
56

67
export function getLeafletItineraryBounds (itinerary) {
@@ -45,6 +46,10 @@ export function itineraryCanBeMonitored (itinerary) {
4546
return hasTransit && !hasRentalOrRideHail
4647
}
4748

49+
export function getFirstStopId (itinerary) {
50+
return getFirstTransitLeg(itinerary)?.from.stopId
51+
}
52+
4853
export function getMinutesUntilItineraryStart (itinerary) {
4954
return moment(itinerary.startTime).diff(moment(), 'minutes')
5055
}

0 commit comments

Comments
 (0)