Skip to content

Commit d24513f

Browse files
feat(field-trip): Add skeleton code for printing field trips.
1 parent babfeab commit d24513f

3 files changed

Lines changed: 195 additions & 9 deletions

File tree

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

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,17 @@ import { connect } from 'react-redux'
66
import styled from 'styled-components'
77

88
import * as fieldTripActions from '../../actions/field-trip'
9+
import Icon from '../narrative/icon'
10+
import {
11+
getGroupSize,
12+
GROUP_FIELDS,
13+
PAYMENT_FIELDS,
14+
TICKET_TYPES
15+
} from '../../util/call-taker'
16+
917
import DraggableWindow from './draggable-window'
1018
import EditableSection from './editable-section'
1119
import FieldTripNotes from './field-trip-notes'
12-
import Icon from '../narrative/icon'
1320
import {
1421
Bold,
1522
Button,
@@ -25,12 +32,6 @@ import {
2532
} from './styled'
2633
import TripStatus from './trip-status'
2734
import Updatable from './updatable'
28-
import {
29-
getGroupSize,
30-
GROUP_FIELDS,
31-
PAYMENT_FIELDS,
32-
TICKET_TYPES
33-
} from '../../util/call-taker'
3435

3536
const WindowHeader = styled(DefaultWindowHeader)`
3637
margin-bottom: 0px;
@@ -59,7 +60,9 @@ class FieldTripDetails extends Component {
5960
}
6061

6162
_renderFooter = () => {
62-
const cancelled = this.props.request.status === 'cancelled'
63+
const { request, session } = this.props
64+
const cancelled = request.status === 'cancelled'
65+
const printFieldTripLink = `/#/printFieldTrip/?requestId=${request.id}&sessionId=${session.sessionId}`
6366
return (
6467
<div style={{padding: '5px 10px 0px 10px'}}>
6568
<DropdownButton
@@ -81,6 +84,12 @@ class FieldTripDetails extends Component {
8184
>
8285
<Icon type='file-text-o' /> Receipt link
8386
</MenuItem>
87+
<MenuItem
88+
href={printFieldTripLink}
89+
target='_blank'
90+
>
91+
<Icon type='print' /> Printable trip plan
92+
</MenuItem>
8493
</DropdownButton>
8594
<Button
8695
bsSize='xsmall'
@@ -214,7 +223,8 @@ const mapStateToProps = (state, ownProps) => {
214223
currentQuery: state.otp.currentQuery,
215224
datastoreUrl: state.otp.config.datastoreUrl,
216225
dateFormat: getDateFormat(state.otp.config),
217-
request
226+
request,
227+
session: state.callTaker.session
218228
}
219229
}
220230

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
// import PrintableItinerary from '@opentripplanner/printable-itinerary'
2+
import React, { Component } from 'react'
3+
import { Button } from 'react-bootstrap'
4+
import { connect } from 'react-redux'
5+
6+
import * as callTakerActions from '../../actions/call-taker'
7+
import * as fieldTripActions from '../../actions/field-trip'
8+
import DefaultMap from '../map/default-map'
9+
// import TripDetails from '../narrative/connected-trip-details'
10+
// import { getTripFromRequest } from '../../util/call-taker'
11+
import { ComponentContext } from '../../util/contexts'
12+
13+
import {
14+
Val
15+
} from './styled'
16+
17+
class PrintFieldTripLayout extends Component {
18+
static contextType = ComponentContext
19+
20+
constructor (props) {
21+
super(props)
22+
this.state = {
23+
mapVisible: true
24+
}
25+
}
26+
27+
_toggleMap = () => {
28+
this.setState({ mapVisible: !this.state.mapVisible })
29+
}
30+
31+
_print = () => {
32+
window.print()
33+
}
34+
35+
componentDidMount () {
36+
const { initializeModules } = this.props
37+
// Add print-view class to html tag to ensure that iOS scroll fix only applies
38+
// to non-print views.
39+
const root = document.getElementsByTagName('html')[0]
40+
root.setAttribute('class', 'print-view')
41+
42+
// Load call-taker/field-trip functionality (performs a fetch).
43+
initializeModules()
44+
}
45+
46+
async componentDidUpdate (prevProps) {
47+
const { fetchFieldTrips, fetchFieldTripDetails, requestId, session } = this.props
48+
if (!prevProps.session && session) {
49+
// When session is set
50+
// Load all field trips
51+
await fetchFieldTrips()
52+
// Load details for field trip per request id.
53+
fetchFieldTripDetails(requestId)
54+
}
55+
}
56+
57+
/**
58+
* Remove class attribute from html tag on clean up.
59+
*/
60+
componentWillUnmount () {
61+
const root = document.getElementsByTagName('html')[0]
62+
root.removeAttribute('class')
63+
}
64+
65+
render () {
66+
const { request } = this.props
67+
// const { LegIcon } = this.context
68+
if (!request) return null
69+
70+
const {
71+
address,
72+
emailAddress,
73+
endLocation,
74+
faxNumber,
75+
grade,
76+
numChaperones,
77+
numFreeStudents,
78+
numStudents,
79+
phoneNumber,
80+
schoolName,
81+
teacherName,
82+
timeStamp
83+
} = request
84+
85+
// const outboundTrip = getTripFromRequest(request, true)
86+
// const inboundTrip = getTripFromRequest(request, false)
87+
88+
return (
89+
<div className='otp print-layout'>
90+
{/* The header bar, including the Toggle Map and Print buttons */}
91+
<div className='header'>
92+
<div style={{ float: 'right' }}>
93+
<Button bsSize='small' onClick={this._toggleMap}>
94+
<i className='fa fa-map' /> Toggle Map
95+
</Button>
96+
{' '}
97+
<Button bsSize='small' onClick={this._print}>
98+
<i className='fa fa-print' /> Print
99+
</Button>
100+
</div>
101+
<h1>Field Trip Plan: {schoolName} to {endLocation}</h1>
102+
</div>
103+
<div>
104+
<ul style={{ listStyle: 'none', padding: 0 }}>
105+
<li><b>Teacher</b>: <Val>{teacherName}</Val> ({schoolName}, Grade: <Val>{grade}</Val>)</li>
106+
<li><b>Teacher Address</b>: <Val>{address}</Val></li>
107+
<li><b>Phone</b>: <Val>{phoneNumber}</Val> / Fax: <Val>{faxNumber}</Val></li>
108+
<li><b>Email</b>: <Val>{emailAddress}</Val></li>
109+
<li><b>Students Age 7 and Over</b>: {numStudents || 0}</li>
110+
<li><b>Students Age 6 and Under</b>: {numFreeStudents || 0}</li>
111+
<li><b>Chaperones</b>: {numChaperones || 0}</li>
112+
<li><i>Request submitted: {timeStamp}</i></li>
113+
</ul>
114+
</div>
115+
116+
<div>
117+
<h2>Outbound Trip (to Destination)</h2>
118+
<p><i>No Outbound Trip Planned</i></p>
119+
{/* outboundTrip
120+
? outboundTrip.groupItineraries?.map((groupItin, i) => {
121+
//const itinerary = JSON.parse(groupItin.itinData)
122+
return (
123+
<div key={i}>
124+
<PrintableItinerary
125+
config={config}
126+
//itinerary={itinerary}
127+
LegIcon={LegIcon}
128+
/>
129+
<TripDetails itinerary={itinerary} />
130+
</div>
131+
)
132+
})
133+
: <p><i>No Outbound Trip Planned</i></p>
134+
*/}
135+
</div>
136+
<div>
137+
<h2>Inbound Trip (to Destination)</h2>
138+
<p><i>No Inbound Trip Planned</i></p>
139+
</div>
140+
141+
{/* The map, if visible */}
142+
{this.state.mapVisible &&
143+
<div className='map-container'>
144+
<DefaultMap />
145+
</div>
146+
}
147+
</div>
148+
)
149+
}
150+
}
151+
152+
// connect to the redux store
153+
154+
const mapStateToProps = (state, ownProps) => {
155+
const requestId = parseInt(state.router.location.query.requestId)
156+
const { requests } = state.callTaker.fieldTrip
157+
const request = requests.data.find(req => req.id === requestId)
158+
return {
159+
request,
160+
requestId,
161+
session: state.callTaker.session
162+
}
163+
}
164+
165+
const mapDispatchToProps = {
166+
fetchFieldTripDetails: fieldTripActions.fetchFieldTripDetails,
167+
fetchFieldTrips: fieldTripActions.fetchFieldTrips,
168+
initializeModules: callTakerActions.initializeModules
169+
}
170+
171+
export default connect(mapStateToProps, mapDispatchToProps)(PrintFieldTripLayout)

lib/components/app/responsive-webapp.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import * as formActions from '../../actions/form'
1717
import * as locationActions from '../../actions/location'
1818
import * as mapActions from '../../actions/map'
1919
import * as uiActions from '../../actions/ui'
20+
import PrintFieldTripLayout from '../admin/print-field-trip-layout'
2021
import { frame } from '../app/app-frame'
2122
import { RedirectWithQuery } from '../form/connected-links'
2223
import Map from '../map/map'
@@ -348,6 +349,10 @@ class RouterWrapperWithAuth0 extends Component {
348349
component={PrintLayout}
349350
path='/print'
350351
/>
352+
<Route
353+
component={PrintFieldTripLayout}
354+
path='/printFieldTrip'
355+
/>
351356
{/* For any other route, simply return the web app. */}
352357
<Route
353358
render={() => <WebappWithRouter {...this.props} />}

0 commit comments

Comments
 (0)