Skip to content

Commit 8e0c5d2

Browse files
feat(StopScheduleTable): Add stop schedule table component.
1 parent d32b6b5 commit 8e0c5d2

2 files changed

Lines changed: 192 additions & 67 deletions

File tree

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import coreUtils from '@opentripplanner/core-utils'
2+
import React, { Component } from 'react'
3+
import { connect } from 'react-redux'
4+
import styled from 'styled-components'
5+
6+
import { getFormattedStopTime, getStopTimesByPattern } from '../../util/viewer'
7+
8+
const { getTimeFormat } = coreUtils.time
9+
10+
// Styles for the schedule table and its contents.
11+
const StyledTable = styled.table`
12+
border-spacing: collapse;
13+
margin-top: 20px;
14+
width: 100%;
15+
th {
16+
font-size: 75%;
17+
}
18+
tr > * {
19+
border-bottom: 1px solid #ccc;
20+
padding: 2px 0 2px 10px;
21+
vertical-align: top;
22+
}
23+
td:first-child, th:first-child {
24+
padding-left: 0;
25+
}
26+
`
27+
28+
const RouteTd = styled.td`
29+
font-weight: bold;
30+
`
31+
const DestTd = styled.td`
32+
width: 100%;
33+
`
34+
const TimeTd = styled.td`
35+
font-weight: bold;
36+
text-align: right;
37+
white-space: nowrap;
38+
`
39+
40+
class StopScheduleTable extends Component {
41+
render () {
42+
const { homeTimezone, stopData, stopViewerArriving, timeFormat } = this.props
43+
const hasStopTimesAndRoutes = !!(stopData && stopData.stopTimes && stopData.stopTimes.length > 0 && stopData.routes)
44+
45+
if (!hasStopTimesAndRoutes) {
46+
return <div>No stop times found for date.</div>
47+
}
48+
49+
const stopTimesByPattern = getStopTimesByPattern(stopData)
50+
51+
// Merge stop times, so that we can sort them across all route patterns.
52+
let mergedStopTimes = []
53+
Object.values(stopTimesByPattern).forEach(pattern => {
54+
const filteredTimes = pattern.times
55+
//TODO refactor - Copied from util/viewers
56+
.filter(stopTime => {
57+
return stopTime.stopIndex < stopTime.stopCount - 1 // ensure that this isn't the last stop
58+
})
59+
.map(stopTime => {
60+
// Add a route attribute to each stop time for rendering route info.
61+
return {
62+
...stopTime,
63+
route: pattern.route
64+
}
65+
})
66+
mergedStopTimes = mergedStopTimes.concat(filteredTimes) // reduce?
67+
})
68+
69+
//TODO Refactor - Copied from pattern-row
70+
mergedStopTimes = mergedStopTimes.sort((a, b) => {
71+
const aTime = a.serviceDay + a.scheduledDeparture
72+
const bTime = b.serviceDay + b.scheduledDeparture
73+
return aTime - bTime
74+
})
75+
76+
return (
77+
<StyledTable>
78+
<thead>
79+
<tr>
80+
<th>Block</th>
81+
<th>Route</th>
82+
<th>To</th>
83+
<th>Departure</th>
84+
</tr>
85+
</thead>
86+
<tbody>
87+
{mergedStopTimes.map(stopTime => {
88+
const { blockId, headsign, route } = stopTime
89+
const routeName = route.shortName ? route.shortName : route.longName
90+
const time = getFormattedStopTime(stopTime, homeTimezone, stopViewerArriving, timeFormat, true)
91+
return (
92+
<tr>
93+
<td>{blockId}</td>
94+
<RouteTd>{routeName}</RouteTd>
95+
<DestTd>{headsign}</DestTd>
96+
<TimeTd>{time}</TimeTd>
97+
</tr>
98+
)
99+
})}
100+
</tbody>
101+
</StyledTable>
102+
)
103+
}
104+
}
105+
106+
// connect to redux store
107+
108+
const mapStateToProps = (state, ownProps) => {
109+
const { config, transitIndex, ui } = state.otp
110+
return {
111+
homeTimezone: config.homeTimezone,
112+
stopData: transitIndex.stops[ui.viewedStop.stopId],
113+
stopViewerArriving: config.language.stopViewerArriving,
114+
timeFormat: getTimeFormat(config)
115+
}
116+
}
117+
118+
const mapDispatchToProps = {
119+
}
120+
121+
export default connect(mapStateToProps, mapDispatchToProps)(StopScheduleTable)

lib/components/viewers/stop-viewer.js

Lines changed: 71 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { setMainPanelContent, toggleAutoRefresh } from '../../actions/ui'
1212
import { findStop, findStopTimesForStop } from '../../actions/api'
1313
import { forgetStop, rememberStop, setLocation } from '../../actions/map'
1414
import PatternRow from './pattern-row'
15+
import StopScheduleTable from './stop-schedule-table'
1516
import { getShowUserSettings, getStopViewerConfig } from '../../util/state'
1617
import { getRouteIdForPattern, getStopTimesByPattern } from '../../util/viewer'
1718

@@ -107,8 +108,8 @@ class StopViewer extends Component {
107108
findStopTimesForStop({
108109
date,
109110
stopId: viewedStop.stopId//,
110-
//startTime: this._getStartTimeForDate(date),
111-
//timeRange: 86400
111+
// startTime: this._getStartTimeForDate(date),
112+
// timeRange: 86400
112113
})
113114
}
114115

@@ -119,13 +120,6 @@ class StopViewer extends Component {
119120
// date and turn off auto refresh.
120121
this._stopAutoRefresh()
121122
this._findStopTimesForDate(date)
122-
// realtime
123-
// https://commtrans-otp-server.ibi-transit.com/otp/routers/default/index/stops/kcm:82175/stoptimes?numberOfDepartures=10&timeRange=345600&startTime=1607641018
124-
// schedule
125-
// https://commtrans-otp-server.ibi-transit.com/otp/routers/default/index/stops/kcm:82175/stoptimes?numberOfDepartures=10&timeRange=86400&startTime=1607576400
126-
// trimet old call taker (shows all departures for the day)
127-
//This syntax also works on commtrans.
128-
// https://maps.trimet.org/otp_mod/index/stops/TriMet:4964/stoptimes/20201211
129123
} else {
130124
// Otherwise, turn on auto refresh.
131125
this._startAutoRefresh()
@@ -263,6 +257,7 @@ class StopViewer extends Component {
263257
} = this.props
264258
const { scheduleView, spin } = this.state
265259
const hasStopTimesAndRoutes = !!(stopData && stopData.stopTimes && stopData.stopTimes.length > 0 && stopData.routes)
260+
266261
// construct a lookup table mapping pattern (e.g. 'ROUTE_ID-HEADSIGN') to
267262
// an array of stoptimes
268263
const stopTimesByPattern = getStopTimesByPattern(stopData)
@@ -283,6 +278,72 @@ class StopViewer extends Component {
283278
// if same route, sort by headsign
284279
return patternHeadsignComparator(patternA, patternB)
285280
}
281+
282+
let contents
283+
if (!hasStopTimesAndRoutes) {
284+
contents = <div>No stop times found for date.</div>
285+
} else if (scheduleView) {
286+
contents = <StopScheduleTable />
287+
} else {
288+
contents = (
289+
<>
290+
<div style={{ marginTop: 20 }}>
291+
{Object.values(stopTimesByPattern)
292+
.sort(patternComparator)
293+
.map(patternTimes => {
294+
// Only add pattern row if route is found.
295+
// FIXME: there is currently a bug with the alernative transit index
296+
// where routes are not associated with the stop if the only stoptimes
297+
// for the stop are drop off only. See https://github.com/ibi-group/trimet-mod-otp/issues/217
298+
if (!patternTimes.route) {
299+
console.warn(`Cannot render stop times for missing route ID: ${getRouteIdForPattern(patternTimes.pattern)}`)
300+
return null
301+
}
302+
return (
303+
<PatternRow
304+
pattern={patternTimes.pattern}
305+
route={patternTimes.route}
306+
stopTimes={patternTimes.times}
307+
stopViewerConfig={stopViewerConfig}
308+
showScheduleView={scheduleView}
309+
key={patternTimes.id}
310+
stopViewerArriving={stopViewerArriving}
311+
homeTimezone={homeTimezone}
312+
timeFormat={timeFormat}
313+
/>
314+
)
315+
})
316+
}
317+
</div>
318+
{!scheduleView
319+
// If showing next arrivals, include auto update controls.
320+
? <div style={{ marginTop: '20px' }}>
321+
<label style={{ fontWeight: 300, fontSize: 'small' }}>
322+
<input
323+
name='autoUpdate'
324+
type='checkbox'
325+
checked={this.props.autoRefreshStopTimes}
326+
onChange={this._onToggleAutoRefresh} />{' '}
327+
Auto-refresh arrivals?
328+
</label>
329+
<button
330+
className='link-button pull-right'
331+
style={{ fontSize: 'small' }}
332+
onClick={this._refreshStopTimes}>
333+
<Icon
334+
className={spin ? 'fa-spin' : ''}
335+
type='refresh' />{' '}
336+
{moment(stopData.stopTimesLastUpdated)
337+
.tz(getUserTimezone())
338+
.format(timeFormat)}
339+
</button>
340+
</div>
341+
: null
342+
}
343+
</>
344+
)
345+
}
346+
286347
return (
287348
<div className='stop-viewer'>
288349
{/* Header Block */}
@@ -291,64 +352,7 @@ class StopViewer extends Component {
291352
{stopData && (
292353
<div className='stop-viewer-body'>
293354
{this._renderControls()}
294-
{hasStopTimesAndRoutes
295-
? <>
296-
<div style={{ marginTop: 20 }}>
297-
{Object.values(stopTimesByPattern)
298-
.sort(patternComparator)
299-
.map(patternTimes => {
300-
// Only add pattern row if route is found.
301-
// FIXME: there is currently a bug with the alernative transit index
302-
// where routes are not associated with the stop if the only stoptimes
303-
// for the stop are drop off only. See https://github.com/ibi-group/trimet-mod-otp/issues/217
304-
if (!patternTimes.route) {
305-
console.warn(`Cannot render stop times for missing route ID: ${getRouteIdForPattern(patternTimes.pattern)}`)
306-
return null
307-
}
308-
return (
309-
<PatternRow
310-
pattern={patternTimes.pattern}
311-
route={patternTimes.route}
312-
stopTimes={patternTimes.times}
313-
stopViewerConfig={stopViewerConfig}
314-
showScheduleView={scheduleView}
315-
key={patternTimes.id}
316-
stopViewerArriving={stopViewerArriving}
317-
homeTimezone={homeTimezone}
318-
timeFormat={timeFormat}
319-
/>
320-
)
321-
})
322-
}
323-
</div>
324-
{!scheduleView
325-
// If showing next arrivals, include auto update controls.
326-
? <div style={{ marginTop: '20px' }}>
327-
<label style={{ fontWeight: 300, fontSize: 'small' }}>
328-
<input
329-
name='autoUpdate'
330-
type='checkbox'
331-
checked={this.props.autoRefreshStopTimes}
332-
onChange={this._onToggleAutoRefresh} />{' '}
333-
Auto-refresh arrivals?
334-
</label>
335-
<button
336-
className='link-button pull-right'
337-
style={{ fontSize: 'small' }}
338-
onClick={this._refreshStopTimes}>
339-
<Icon
340-
className={spin ? 'fa-spin' : ''}
341-
type='refresh' />{' '}
342-
{moment(stopData.stopTimesLastUpdated)
343-
.tz(getUserTimezone())
344-
.format(timeFormat)}
345-
</button>
346-
</div>
347-
: null
348-
}
349-
</>
350-
: <div>No stop times found for date.</div>
351-
}
355+
{contents}
352356
{/* Future: add stop details */}
353357
</div>
354358
)}

0 commit comments

Comments
 (0)