diff --git a/lib/actions/call-taker.js b/lib/actions/call-taker.js index b0929234d..4f6b61ccb 100644 --- a/lib/actions/call-taker.js +++ b/lib/actions/call-taker.js @@ -6,7 +6,7 @@ import { createAction } from 'redux-actions' import {routingQuery} from './api' import {setQueryParam} from './form' -import {getGroupSize, searchToQuery} from '../util/call-taker' +import {getGroupSize, getTrip, searchToQuery} from '../util/call-taker' import {URL_ROOT} from '../util/constants' import {getTimestamp} from '../util/state' @@ -31,6 +31,7 @@ const requestingFieldTripDetails = createAction('REQUESTING_FIELD_TRIP_DETAILS') export const beginCall = createAction('BEGIN_CALL') export const setFieldTripFilter = createAction('SET_FIELD_TRIP_FILTER') export const setActiveFieldTrip = createAction('SET_ACTIVE_FIELD_TRIP') +export const setGroupSize = createAction('SET_GROUP_SIZE') export const toggleCallHistory = createAction('TOGGLE_CALL_HISTORY') export const toggleFieldTrips = createAction('TOGGLE_FIELD_TRIPS') @@ -183,12 +184,13 @@ export function addFieldTripNote (request, note) { const {callTaker, otp} = getState() const {datastoreUrl} = otp.config if (sessionIsInvalid(callTaker.session)) return - const {sessionId, userName} = callTaker.session + console.log(callTaker.session) + const {sessionId, username} = callTaker.session const queryData = new FormData() queryData.append('sessionId', sessionId) queryData.append('note.note', note.note) queryData.append('note.type', note.type) - queryData.append('note.userName', userName) + queryData.append('note.userName', username) queryData.append('requestId', request.id) return fetch(`${datastoreUrl}/fieldtrip/addNote`, {method: 'POST', body: queryData} @@ -222,6 +224,56 @@ export function deleteFieldTripNote (request, noteId) { } } +function processPlan (tripPlan, restoring) { + if (updateActiveOnly) { + var itinIndex = itinWidget.activeIndex + tripPlan.itineraries[0].groupSize = groupPlan.itineraries[itinIndex].groupSize + itinWidget.updateItineraries(tripPlan.itineraries) + updateActiveOnly = false + drawItinerary(tripPlan.itineraries[0]) + return + } + + if (groupPlan == null) { + groupPlan = new otp.modules.planner.TripPlan(null, _.extend(tripPlan.queryParams, { groupSize : groupSize })) + } + + if (itinWidget == null) createItinerariesWidget() + + var itin = tripPlan.itineraries[0] + var capacity = itin.getGroupTripCapacity() + + // if this itin shares a vehicle trip with another one already in use, only use the remainingCapacity (as set in checkTripValidity()) + if (itinCapacity) capacity = Math.min(capacity, itinCapacity) + + groupPlan.addItinerary(itin) + + var transitLegs = itin.getTransitLegs() + for (var i = 0; i < transitLegs.length; i++) { + var leg = transitLegs[i] + bannedSegments.push({ + tripId : leg.tripId, + fromStopIndex : leg.from.stopIndex, + toStopIndex : leg.to.stopIndex, + }) + } + + setBannedTrips() + + if (currentGroupSize > capacity) { + // group members remain. plan another trip + currentGroupSize -= capacity + itin.groupSize = capacity + //console.log("remaining: "+currentGroupSize) + itinCapacity = null + planTrip() + } else { + // we're done. show the results + itin.groupSize = currentGroupSize + showResults() + } +} + /** * Edit teacher (AKA submitter) notes for a field trip request. */ @@ -245,10 +297,132 @@ export function editSubmitterNotes (request, submitterNotes) { } } -export function planOutbound (request) { +export function saveRequestTrip (request, outbound, groupPlan) { + return function (dispatch, getState) { + // If plan is not valid, return before persisting trip. + const check = checkPlanValidity(request, groupPlan) + if (!check.isValid) return alert(check.message) + const requestOrder = outbound ? 0 : 1 + const type = outbound ? 'outbound' : 'inbound' + const preExistingTrip = getTrip(request, outbound) + if (preExistingTrip) { + const msg = `This action will overwrite a previously planned ${type} itinerary for this request. Do you wish to continue?` + if (!confirm(msg)) return + } + dispatch(saveTrip(request, requestOrder)) + } +} + +function checkPlanValidity (request, groupPlan) { + if (groupPlan == null) { + return { + isValid: false, + message: 'No active plan to save' + } + } + + // FIXME: add back in offset? + const planDeparture = moment(groupPlan.earliestStartTime) // .add('hours', otp.config.timeOffset) + const requestDate = moment(request.travelDate) + + if ( + planDeparture.date() !== requestDate.date() || + planDeparture.month() !== requestDate.month() || + planDeparture.year() !== requestDate.year() + ) { + return { + isValid: false, + message: `Planned trip date (${planDeparture.format('MM/DD/YYYY')}) is not the requested day of travel (${requestDate.format('MM/DD/YYYY')})` + } + } + + // FIXME More checks? E.g., origin/destination + + return { isValid: true } +} + +function saveTrip (request, requestOrder) { + return function (dispatch, getState) { + const {callTaker, otp} = getState() + const {datastoreUrl} = otp.config + if (sessionIsInvalid(callTaker.session)) return + const {sessionId, username} = callTaker.session + const data = { + sessionId: sessionId, + requestId: request.id, + 'trip.requestOrder': requestOrder, + 'trip.origin': getStartOTPString(), + 'trip.destination': getEndOTPString(), + 'trip.createdBy': username, + 'trip.passengers': groupSize, + 'trip.departure': moment(groupPlan.earliestStartTime).add('hours', otp.config.timeOffset).format('YYYY-MM-DDTHH:mm:ss'), + 'trip.queryParams': JSON.stringify(groupPlan.queryParams) + } + + for (let i = 0; i < groupPlan.itineraries.length; i++) { + const itin = groupPlan.itineraries[i] + data[`itins[${i}].passengers`] = itin.groupSize + data[`itins[${i}].itinData`] = otp.util.Text.lzwEncode(JSON.stringify(itin.itinData)) + data[`itins[${i}].timeOffset`] = otp.config.timeOffset || 0 + + const legs = itin.getTransitLegs() + + for (let l = 0; l < legs.length; l++) { + const leg = legs[l] + const routeName = (leg.routeShortName !== null ? ('(' + leg.routeShortName + ') ') : '') + (leg.routeLongName || '') + const tripHash = tripHashLookup[leg.tripId] + + data[`gtfsTrips[${i}][${l}].depart`] = moment(leg.startTime).format('HH:mm:ss') + data[`gtfsTrips[${i}][${l}].arrive`] = moment(leg.endTime).format('HH:mm:ss') + data[`gtfsTrips[${i}][${l}].agencyAndId`] = leg.tripId + data[`gtfsTrips[${i}][${l}].tripHash`] = tripHash + data[`gtfsTrips[${i}][${l}].routeName`] = routeName + data[`gtfsTrips[${i}][${l}].fromStopIndex`] = leg.from.stopIndex + data[`gtfsTrips[${i}][${l}].toStopIndex`] = leg.to.stopIndex + data[`gtfsTrips[${i}][${l}].fromStopName`] = leg.from.name + data[`gtfsTrips[${i}][${l}].toStopName`] = leg.to.name + data[`gtfsTrips[${i}][${l}].headsign`] = leg.headsign + data[`gtfsTrips[${i}][${l}].capacity`] = itin.getModeCapacity(leg.mode) + if (leg.tripBlockId) data[`gtfsTrips[${i}][${l}].blockId`] = leg.tripBlockId + } + } + return fetch(`${datastoreUrl}/fieldtrip/newTrip`, + {method: 'POST', body: data} + ) + .then((res) => { + console.log(res) + if (res === -1) { + alert('This plan could not be saved due to a lack of capacity on one or more vehicles. Please re-plan your trip.') + } else { + dispatch(fetchFieldTripDetails(request.id)) + } + }) + .catch(err => { + alert(`Error saving trip: ${JSON.stringify(err)}`) + }) + } +} + +export function planTrip (request, outbound) { + return async function (dispatch, getState) { + dispatch(setGroupSize(getGroupSize(request))) + const trip = getTrip(request, outbound) + if (!trip) { + // Construct params from request details + if (outbound) dispatch(planOutbound(request)) + else dispatch(planInbound(request)) + } else { + // Populate params from saved query params + const params = await planParamsToQueryAsync(JSON.parse(trip.queryParams)) + dispatch(setQueryParam(params, trip.id)) + } + } +} + +function planOutbound (request) { return async function (dispatch, getState) { const {config} = getState().otp - // this.clearTrip() + // clearTrip() const locations = await planParamsToQueryAsync({ fromPlace: request.startLocation, toPlace: request.endLocation @@ -265,14 +439,14 @@ export function planOutbound (request) { } } -export function planInbound (request) { +function planInbound (request) { return async function (dispatch, getState) { const {config} = getState().otp const locations = await planParamsToQueryAsync({ fromPlace: request.endLocation, toPlace: request.startLocation }, config) - // this.clearTrip() + // clearTrip() const queryParams = { date: moment(request.travelDate).format(OTP_API_DATE_FORMAT), departArrive: 'DEPART', @@ -285,6 +459,59 @@ export function planInbound (request) { } } +/** + * Set group size for a field trip request. + */ +export function setRequestGroupSize (request, groupSize) { + return function (dispatch, getState) { + const {callTaker, otp} = getState() + const {datastoreUrl} = otp.config + if (sessionIsInvalid(callTaker.session)) return + const {sessionId} = callTaker.session + const queryData = new FormData() + queryData.append('sessionId', sessionId) + queryData.append('numStudents', groupSize.numStudents) + queryData.append('numFreeStudents', groupSize.numFreeStudents) + queryData.append('numChaperones', groupSize.numChaperones) + queryData.append('requestId', request.id) + return fetch(`${datastoreUrl}/fieldtrip/setRequestGroupSize`, + {method: 'POST', body: queryData} + ) + .then(() => dispatch(fetchFieldTripDetails(request.id))) + .catch(err => { + alert(`Error setting group size: ${JSON.stringify(err)}`) + }) + } +} + +/** + * Set payment info for a field trip request. + */ +export function setRequestPaymentInfo (request, paymentInfo) { + return function (dispatch, getState) { + const {callTaker, otp} = getState() + const {datastoreUrl} = otp.config + if (sessionIsInvalid(callTaker.session)) return + const {sessionId} = callTaker.session + const queryData = new FormData() + queryData.append('sessionId', sessionId) + queryData.append('classpassId', paymentInfo.classpassId) + queryData.append('paymentPreference', paymentInfo.paymentPreference) + queryData.append('ccType', paymentInfo.ccType) + queryData.append('ccName', paymentInfo.ccName) + queryData.append('ccLastFour', paymentInfo.ccLastFour) + queryData.append('checkNumber', paymentInfo.checkNumber) + queryData.append('requestId', request.id) + return fetch(`${datastoreUrl}/fieldtrip/setRequestPaymentInfo`, + {method: 'POST', body: queryData} + ) + .then(() => dispatch(fetchFieldTripDetails(request.id))) + .catch(err => { + alert(`Error setting payment info: ${JSON.stringify(err)}`) + }) + } +} + /** * Set field trip request status (e.g., cancelled). */ diff --git a/lib/components/admin/editable-section.js b/lib/components/admin/editable-section.js new file mode 100644 index 000000000..1ad5d263b --- /dev/null +++ b/lib/components/admin/editable-section.js @@ -0,0 +1,138 @@ +import React, {Component} from 'react' + +import { + Button, + P, + Val +} from './styled' + +export default class EditableSection extends Component { + state = { + data: {}, + isEditing: false + } + + _exists = (val) => val !== null && typeof val !== 'undefined' + + _getVal = (fieldName) => this._exists(this.state.data[fieldName]) + ? this.state.data[fieldName] + : this.props.request[fieldName] + + _onChange = (fieldName, value) => { + const stateUpdate = this.state.data + stateUpdate[fieldName] = value + this.setState({data: stateUpdate}) + } + + _onClickSave = () => { + const {request, onChange} = this.props + const data = {} + this.props.fields.forEach(f => { + data[f.fieldName] = this._getVal(f.fieldName) + }) + onChange(request, data) + this.setState({data: {}, isEditing: false}) + } + + _toggleEditing = () => { + const stateUpdate = {isEditing: !this.state.isEditing} + if (this.state.isEditing) { + stateUpdate.data = {} + } + this.setState(stateUpdate) + } + + render () { + const {fields, header, inputStyle, request, valueFirst} = this.props + const {isEditing} = this.state + if (!request) return null + return ( + <> +

+ {header} + + {!isEditing + ? + : <> + + + + } + +

+ {fields.map(f => { + const input = ( + + ) + return ( +

+ {valueFirst + ? <>{input} {f.label} + : <>{f.label}: {input} + } +

+ ) + })} + + ) + } +} + +class InputToggle extends Component { + _onChange = (evt) => { + const {fieldName, inputProps = {}, onChange} = this.props + let value = evt.target.value + if (inputProps.type === 'number') { + value = +evt.target.value + } + onChange(fieldName, value) + } + render () { + const {inputProps, fieldName, isEditing, options, style, value} = this.props + if (isEditing) { + if (options) { + return ( + + ) + } else { + return + } + } + return {options ? options[value] : value} + } +} diff --git a/lib/components/admin/field-trip-details.js b/lib/components/admin/field-trip-details.js index 527cd8e03..29a53b5a7 100644 --- a/lib/components/admin/field-trip-details.js +++ b/lib/components/admin/field-trip-details.js @@ -1,23 +1,26 @@ +import { getDateFormat } from '@opentripplanner/core-utils/lib/time' +import moment from 'moment' import React, { Component } from 'react' import { DropdownButton, MenuItem } from 'react-bootstrap' import { connect } from 'react-redux' import * as callTakerActions from '../../actions/call-taker' import DraggableWindow from './draggable-window' +import EditableSection from './editable-section' import FieldTripNotes from './field-trip-notes' import Icon from '../narrative/icon' import { B, Button, Container, - Half, Full, + Half, Header, - P, - Val + P } from './styled' import TripStatus from './trip-status' import Updatable from './updatable' +import {getGroupSize} from '../../util/call-taker' const TICKET_TYPES = { own_tickets: 'Will use own tickets', @@ -30,14 +33,31 @@ const PAYMENT_PREFS = { fax_cc: 'Will fax credit card info to TriMet', mail_check: 'Will mail check to TriMet' } + +const inputProps = { + min: 0, + step: 1, + type: 'number' +} +const GROUP_FIELDS = [ + {inputProps, fieldName: 'numStudents', label: 'students 7 or older'}, + {inputProps, fieldName: 'numFreeStudents', label: 'students under 7'}, + {inputProps, fieldName: 'numChaperones', label: 'chaperones'} +] +const PAYMENT_FIELDS = [ + {label: 'Ticket type', fieldName: 'ticketType', options: TICKET_TYPES}, + {label: 'Payment preference', fieldName: 'paymentPreference', options: PAYMENT_PREFS}, + {label: 'Invoice required', fieldName: 'requireInvoice', options: ['Yes', 'No']}, + {label: 'Class Pass Hop Card #', fieldName: 'classpassId'}, + {label: 'Credit card type', fieldName: 'ccType'}, + {label: 'Name on credit card', fieldName: 'ccName'}, + {label: 'Credit card last 4 digits', fieldName: 'ccLastFour'}, + {label: 'Check/Money order number', fieldName: 'checkNumber'} +] /** * Shows the details for the active Field Trip Request. */ class FieldTripDetails extends Component { - state ={ - expandNotes: true - } - _editSubmitterNotes = (val) => this.props.editSubmitterNotes(this.props.request, val) _onCloseActiveFieldTrip = () => this.props.setActiveFieldTrip(null) @@ -49,36 +69,25 @@ class FieldTripDetails extends Component { } } - _toggleNotes = () => this.setState({expandNotes: !this.state.expandNotes}) - render () { const { addFieldTripNote, callTaker, + dateFormat, deleteFieldTripNote, - request + request, + setRequestGroupSize, + setRequestPaymentInfo } = this.props if (!request) return null const { - ccLastFour, - ccName, - ccType, - checkNumber, - classpassId, id, notes, - numChaperones, - numFreeStudents, - numStudents, - paymentPreference, - requireInvoice, schoolName, submitterNotes, teacherName, - ticketType, travelDate } = request - const total = numStudents + numChaperones + numFreeStudents const {fieldTrip} = callTaker const defaultPosition = {...fieldTrip.position} const internalNotes = [] @@ -89,6 +98,8 @@ class FieldTripDetails extends Component { }) defaultPosition.x = defaultPosition.x - 460 defaultPosition.y = defaultPosition.y - 100 + const travelDateAsMoment = moment(travelDate) + const total = getGroupSize(request) return ( } header={ -

+

{schoolName} Trip (#{id}) +
+ + Travel date: {travelDateAsMoment.format(dateFormat)}{' '} + ({travelDateAsMoment.fromNow()}) + +

} height='375px' @@ -128,28 +145,28 @@ class FieldTripDetails extends Component { style={{width: '450px'}} > -
Group Information ({travelDate})
+
Group Information

{schoolName}

Teacher: {teacherName}

} onUpdate={this._editSubmitterNotes} value={submitterNotes} />

-

- Total group size: {total} - -

-

{numStudents} students 7 or older

-

{numFreeStudents} students under 7

-

{numStudents} chaperones

+ {total} total group size} + inputStyle={{lineHeight: '0.8em', padding: '0px', width: '50px'}} + onChange={setRequestGroupSize} + request={request} + valueFirst + />
-
- Payment information -
-

Ticket type: {TICKET_TYPES[ticketType]}

-

Payment preference: {PAYMENT_PREFS[paymentPreference]}

-

Invoice required:

-

Class Pass Hop Card #: {classpassId}

-

Credit card type: {ccType}

-

Name on credit card: {ccName}

-

Credit card last 4 digits: {ccLastFour}

-

Check/Money order number: {checkNumber}

+ + Payment information + + } + inputStyle={{lineHeight: '0.8em', padding: '0px', width: '100px'}} + onChange={setRequestPaymentInfo} + request={request} + />
@@ -188,6 +203,7 @@ const mapStateToProps = (state, ownProps) => { return { callTaker: state.callTaker, currentQuery: state.otp.currentQuery, + dateFormat: getDateFormat(state.otp.config), request } } @@ -199,6 +215,8 @@ const mapDispatchToProps = { fetchQueries: callTakerActions.fetchQueries, setActiveFieldTrip: callTakerActions.setActiveFieldTrip, setFieldTripFilter: callTakerActions.setFieldTripFilter, + setRequestGroupSize: callTakerActions.setRequestGroupSize, + setRequestPaymentInfo: callTakerActions.setRequestPaymentInfo, setRequestStatus: callTakerActions.setRequestStatus, toggleFieldTrips: callTakerActions.toggleFieldTrips } diff --git a/lib/components/admin/field-trip-notes.js b/lib/components/admin/field-trip-notes.js index d87ce5aaf..9ff371bce 100644 --- a/lib/components/admin/field-trip-notes.js +++ b/lib/components/admin/field-trip-notes.js @@ -1,5 +1,5 @@ import React, { Component } from 'react' -import { Badge } from 'react-bootstrap' +import { Badge, Button as BsButton } from 'react-bootstrap' import styled from 'styled-components' import Icon from '../narrative/icon' @@ -21,10 +21,14 @@ const Footer = styled.footer` const Note = ({note, onClickDelete}) => { return (
+ onClickDelete(note)} + > + + {note.note} -
{note.userName} on {note.timeStamp}
) @@ -63,14 +67,14 @@ export default class FieldTripNotes extends Component { _deleteNote = (note) => { const {deleteFieldTripNote, request} = this.props - if (confirm(`Are you sure you want to delete note: ${note.note}?`)) { + if (confirm(`Are you sure you want to delete note "${note.note}"?`)) { console.log('OK deleting') deleteFieldTripNote(request, note.id) } } render () { - const {expanded, onClickToggle, request} = this.props + const {request} = this.props if (!request) return null const { feedback, @@ -87,9 +91,6 @@ export default class FieldTripNotes extends Component {
Notes/Feedback{' '} {this._getNotesCount()} - @@ -97,24 +98,22 @@ export default class FieldTripNotes extends Component { Ops. note
- {expanded && - <> -
User feedback
- {feedback && feedback.length > 0 - ? feedback.map((f, i) => ) - : 'No feedback submitted.' - } -
Internal agent notes
- {internalNotes && internalNotes.length > 0 - ? internalNotes.map(n => ) - : 'No internal notes submitted.' - } -
Operational notes
- {operationalNotes && operationalNotes.length > 0 - ? operationalNotes.map(n => ) - : 'No operational notes submitted.' - } - +
User feedback
+ {feedback && feedback.length > 0 + ? feedback.map((f, i) => ) + : 'No feedback submitted.' + } +
Internal agent notes
+ {internalNotes && internalNotes.length > 0 + ? internalNotes.map(n => + ) + : 'No internal notes submitted.' + } +
Operational notes
+ {operationalNotes && operationalNotes.length > 0 + ? operationalNotes.map(n => + ) + : 'No operational notes submitted.' } ) diff --git a/lib/components/admin/styled.js b/lib/components/admin/styled.js index 02d3d7603..d09e6c30d 100644 --- a/lib/components/admin/styled.js +++ b/lib/components/admin/styled.js @@ -26,6 +26,7 @@ export const Header = styled.h4` ` export const P = styled.p` + font-size: 0.9em; margin-bottom: 0px; ` diff --git a/lib/components/admin/trip-status.js b/lib/components/admin/trip-status.js index a0e99a318..f50ab1368 100644 --- a/lib/components/admin/trip-status.js +++ b/lib/components/admin/trip-status.js @@ -1,4 +1,3 @@ -import { planParamsToQueryAsync } from '@opentripplanner/core-utils/lib/query' import { getTimeFormat } from '@opentripplanner/core-utils/lib/time' import moment from 'moment' import React, {Component} from 'react' @@ -6,6 +5,7 @@ import { connect } from 'react-redux' import * as callTakerActions from '../../actions/call-taker' import * as formActions from '../../actions/form' +import Icon from '../narrative/icon' import { B, Button, @@ -13,88 +13,80 @@ import { Header, P } from './styled' +import { getTrip } from '../../util/call-taker' class TripStatus extends Component { + _getTrip = () => getTrip(this.props.request, this.props.outbound) + _formatTime = (time) => moment(time).format(this.props.timeFormat) - _formatTripStatus = (tripStatus) => { - if (!tripStatus) { + _formatTripStatus = () => { + if (!this._getStatus()) { return ( No itineraries planned! Click Plan to plan trip. ) } + const trip = this._getTrip() + if (!trip) return Error finding trip! return ( - {JSON.stringify(tripStatus)} + {trip.groupItineraries.length} group itineraries, planned by{' '} + {trip.createdBy} at {trip.timeStamp} ) } - _onPlanTrip = async () => { - const {outbound, planInbound, planOutbound, request} = this.props - const trip = this._getTrip() - if (!trip) { - // Construct params from request details - if (outbound) planOutbound(request) - else planInbound(request) - } else { - // Populate params from saved query params - const params = await planParamsToQueryAsync(JSON.parse(trip.queryParams)) - this.props.setQueryParam(params, trip.id) - } - } - - _getTrip = () => { + _getStatus = () => { const {outbound, request} = this.props - if (!request || !request.trips) return null - return outbound - ? request.trips[0] - : request.trips[1] + return outbound ? request.outboundTripStatus : request.inboundTripStatus } + _getStatusIcon = () => this._getStatus() + ? + : + + _onPlanTrip = () => this.props.planTrip(this.props.request, this.props.outbound) + + _onSaveTrip = () => this.props.saveRequestTrip(this.props.request, this.props.outbound) + render () { const {outbound, request} = this.props const { arriveDestinationTime, arriveSchoolTime, endLocation, - inboundTripStatus, leaveDestinationTime, - outboundTripStatus, startLocation } = request if (!request) { console.warn('Could not find field trip request') return null } - const status = outbound ? outboundTripStatus : inboundTripStatus const start = outbound ? startLocation : endLocation const end = outbound ? endLocation : startLocation - const trip = this._getTrip() return (
+ {this._getStatusIcon()} {outbound ? 'Outbound' : 'Inbound'} trip - {status && - - }

From {start} to {end}

{outbound ?

- Arriving at {this._formatTime(arriveDestinationTime)},{' '} - leave at {this._formatTime(leaveDestinationTime)} + Arriving at {this._formatTime(arriveDestinationTime)}

: <> -

From {start} to {end}

-

Due back at {this._formatTime(arriveSchoolTime)}

+

+ Leave at {this._formatTime(leaveDestinationTime)},{' '} + due back at {this._formatTime(arriveSchoolTime)} +

} -

{this._formatTripStatus(status)}

+

{this._formatTripStatus()}

) } @@ -109,8 +101,8 @@ const mapStateToProps = (state, ownProps) => { } const mapDispatchToProps = { - planInbound: callTakerActions.planInbound, - planOutbound: callTakerActions.planOutbound, + planTrip: callTakerActions.planTrip, + saveRequestTrip: callTakerActions.saveRequestTrip, setQueryParam: formActions.setQueryParam } diff --git a/lib/components/admin/updatable.js b/lib/components/admin/updatable.js index a36f073c6..c69800b55 100644 --- a/lib/components/admin/updatable.js +++ b/lib/components/admin/updatable.js @@ -18,10 +18,10 @@ export default class Updatable extends Component { } render () { - const {fieldName, value} = this.props + const {fieldName, label, value} = this.props return ( <> - {fieldName}:{' '} + {label || fieldName}:{' '} {value}
+ {groupSize !== null && maxGroupSize && + + Group size:{' '} + + + }