Skip to content

Commit f9492f4

Browse files
committed
feat(field-trip): add daily report feature; improve perf
1 parent 6058031 commit f9492f4

3 files changed

Lines changed: 91 additions & 31 deletions

File tree

lib/components/admin/field-trip-list.js

Lines changed: 86 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,28 @@
1+
import memoize from 'lodash.memoize'
2+
import moment from 'moment'
13
import React, { Component } from 'react'
2-
import { Badge } from 'react-bootstrap'
4+
import { Badge, Button } from 'react-bootstrap'
35
import { connect } from 'react-redux'
46

57
import * as fieldTripActions from '../../actions/field-trip'
6-
import DraggableWindow from './draggable-window'
78
import Icon from '../narrative/icon'
89
import Loading from '../narrative/loading'
9-
import {FieldTripRecordButton, WindowHeader} from './styled'
1010
import {getVisibleRequests, TABS} from '../../util/call-taker'
1111
import {FETCH_STATUS} from '../../util/constants'
1212

13+
import {FieldTripRecordButton, WindowHeader} from './styled'
14+
import DraggableWindow from './draggable-window'
15+
1316
/**
1417
* Displays a searchable list of field trip requests in a draggable window.
1518
*/
1619
class FieldTripList extends Component {
20+
constructor (props) {
21+
super(props)
22+
this.state = {
23+
date: moment().startOf('day').format('YYYY-MM-DD')
24+
}
25+
}
1726
_onClickFieldTrip = (request) => {
1827
const {callTaker, fetchFieldTripDetails, setActiveFieldTrip} = this.props
1928
if (request.id === callTaker.fieldTrip.activeId) {
@@ -30,6 +39,12 @@ class FieldTripList extends Component {
3039
this.props.setActiveFieldTrip(null)
3140
}
3241

42+
_getReportUrl = () => {
43+
const {date} = this.state
44+
const [year, month, day] = date.split('-')
45+
return `${this.props.datastoreUrl}/fieldtrip/opsReport?month=${month}&day=${day}&year=${year}`
46+
}
47+
3348
/**
3449
* Change search input selectively. This is to prevent excessive rendering
3550
* each time the search input changes (on TriMet's production instance there
@@ -51,13 +66,40 @@ class FieldTripList extends Component {
5166
this.props.setFieldTripFilter({tab: e.currentTarget.name})
5267
}
5368

69+
_updateReportDate = evt => this.setState({ date: evt.target.value })
70+
5471
render () {
5572
const {callTaker, style, toggleFieldTrips, visibleRequests} = this.props
5673
const {fieldTrip} = callTaker
5774
const {activeId, filter} = fieldTrip
5875
const {search} = filter
76+
const {date} = this.state
5977
return (
6078
<DraggableWindow
79+
footer={
80+
<>
81+
<input
82+
className='datetime-slim'
83+
onChange={this._updateReportDate}
84+
style={{
85+
border: 'none',
86+
fontSize: '14px',
87+
lineHeight: '1em',
88+
outline: 'none',
89+
width: '106px'
90+
}}
91+
type='date'
92+
value={date}
93+
/>
94+
<Button
95+
bsSize='xsmall'
96+
href={this._getReportUrl()}
97+
target='_blank'
98+
>
99+
View report
100+
</Button>
101+
</>
102+
}
61103
header={
62104
<>
63105
<WindowHeader>
@@ -83,27 +125,14 @@ class FieldTripList extends Component {
83125
/>
84126
</span>
85127
</WindowHeader>
86-
{TABS.map(tab => {
87-
const active = tab.id === filter.tab
88-
const style = {
89-
backgroundColor: active ? 'navy' : undefined,
90-
borderRadius: 5,
91-
color: active ? 'white' : undefined,
92-
padding: '2px 3px'
93-
}
94-
const requestCount = fieldTrip.requests.data.filter(tab.filter).length
95-
return (
96-
<button
97-
className='clear-button-formatting'
98-
key={tab.id}
99-
name={tab.id}
100-
onClick={this._onTabChange}
101-
style={style}
102-
>
103-
{tab.label} <Badge>{requestCount}</Badge>
104-
</button>
105-
)
106-
})}
128+
{TABS.map(tab =>
129+
<Tab
130+
data={fieldTrip.requests.data}
131+
filter={filter}
132+
key={tab.id}
133+
onClick={this._onTabChange}
134+
tab={tab} />
135+
)}
107136
</>
108137
}
109138
onClickClose={toggleFieldTrips}
@@ -127,6 +156,32 @@ class FieldTripList extends Component {
127156
}
128157
}
129158

159+
class Tab extends Component {
160+
getCount = memoize((data, filter) => data.filter(filter).length)
161+
162+
render () {
163+
const {data, filter, onClick, tab} = this.props
164+
const active = tab.id === filter.tab
165+
const style = {
166+
backgroundColor: active ? 'navy' : undefined,
167+
borderRadius: 5,
168+
color: active ? 'white' : undefined,
169+
padding: '2px 3px'
170+
}
171+
return (
172+
<button
173+
className='clear-button-formatting'
174+
key={tab.id}
175+
name={tab.id}
176+
onClick={onClick}
177+
style={style}
178+
>
179+
{tab.label} <Badge>{this.getCount(data, tab.filter)}</Badge>
180+
</button>
181+
)
182+
}
183+
}
184+
130185
class FieldTripRequestRecord extends Component {
131186
_onClick = () => {
132187
const {onClick, request} = this.props
@@ -151,8 +206,10 @@ class FieldTripRequestRecord extends Component {
151206
schoolName,
152207
startLocation,
153208
teacherName,
154-
timeStamp
209+
timeStamp,
210+
travelDate
155211
} = request
212+
const formattedDate = travelDate.split(' ').splice(0, 3).join(' ')
156213
return (
157214
<li
158215
className='list-unstyled'
@@ -174,11 +231,11 @@ class FieldTripRequestRecord extends Component {
174231
{this._getStatusIcon(outboundTripStatus)} Outbound
175232
</span>
176233
</span>
177-
<span style={{display: 'block'}}>
234+
<span style={{display: 'block', fontSize: '.9em'}}>
178235
Submitted by {teacherName} on {timeStamp}
179236
</span>
180-
<span style={{display: 'block'}}>
181-
{startLocation} to {endLocation}
237+
<span style={{display: 'block', fontSize: '.9em'}}>
238+
{startLocation} to {endLocation} on <strong>{formattedDate}</strong>
182239
</span>
183240
</FieldTripRecordButton>
184241
</li>
@@ -190,6 +247,7 @@ const mapStateToProps = (state, ownProps) => {
190247
return {
191248
callTaker: state.callTaker,
192249
currentQuery: state.otp.currentQuery,
250+
datastoreUrl: state.otp.config.datastoreUrl,
193251
searches: state.otp.searches,
194252
visibleRequests: getVisibleRequests(state)
195253
}

lib/components/form/call-taker/date-time-options.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ export default class DateTimeOptions extends Component {
184184
trigger={['focus', 'hover']}
185185
>
186186
<input
187+
className='datetime-slim'
187188
onChange={this.handleTimeChange}
188189
onFocus={this.handleTimeFocus}
189190
onKeyDown={onKeyDown}
@@ -199,6 +200,7 @@ export default class DateTimeOptions extends Component {
199200
</OverlayTrigger>
200201
</span>
201202
<input
203+
className='datetime-slim'
202204
onChange={this.handleDateChange}
203205
onKeyDown={onKeyDown}
204206
style={{

lib/components/form/form.css

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,17 +65,17 @@
6565
/* Date/Time selectors */
6666

6767
/* Remove up/down arrows on date/time input (to save on width) */
68-
.otp .search-options input[type="date"]::-webkit-inner-spin-button {
68+
.otp input[type="date"].datetime-slim::-webkit-inner-spin-button {
6969
-webkit-appearance: none;
7070
}
7171

7272
/* Prevent the calendar picker from having a white margin that covers the date */
73-
.otp .search-options input[type="date"]::-webkit-calendar-picker-indicator {
73+
.otp input[type="date"].datetime-slim::-webkit-calendar-picker-indicator {
7474
background-color: rgba(0, 0, 0, 0.0);
7575
margin-left: 0px;
7676
}
7777

78-
.otp .search-options input[type="time"]::-webkit-inner-spin-button {
78+
.otp input[type="time"].datetime-slim::-webkit-inner-spin-button {
7979
-webkit-appearance: none;
8080
}
8181

0 commit comments

Comments
 (0)