From 2cc982562de9b8646fbfd18cd6a559364855192b Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 25 Sep 2020 16:13:13 -0400 Subject: [PATCH 01/13] refactor(middleware): Test a query. --- lib/components/user/saved-trip-list.js | 2 +- lib/components/user/trip-basics-pane.js | 126 +++++++++++++++--------- lib/util/middleware.js | 10 ++ 3 files changed, 88 insertions(+), 50 deletions(-) diff --git a/lib/components/user/saved-trip-list.js b/lib/components/user/saved-trip-list.js index 9969a5d3c..39a7e2460 100644 --- a/lib/components/user/saved-trip-list.js +++ b/lib/components/user/saved-trip-list.js @@ -45,7 +45,7 @@ class SavedTripList extends Component {

My saved trips

Click on a saved trip below to modify it.

- {trips.map((trip, index) => ( + {trips && trips.map((trip, index) => ( diff --git a/lib/components/user/trip-basics-pane.js b/lib/components/user/trip-basics-pane.js index 5b1bbaa25..dbcb9663f 100644 --- a/lib/components/user/trip-basics-pane.js +++ b/lib/components/user/trip-basics-pane.js @@ -1,13 +1,15 @@ import { Field } from 'formik' -import React from 'react' +import React, { Component } from 'react' import { ControlLabel, FormControl, FormGroup, HelpBlock } from 'react-bootstrap' +import { connect } from 'react-redux' import styled from 'styled-components' +import { checkItinerary } from '../../util/middleware' import { ALL_DAYS } from '../../util/monitored-trip' import TripSummary from './trip-summary' @@ -44,60 +46,86 @@ const allDays = [ * This component shows summary information for a trip * and lets the user edit the trip name and day. */ -const TripBasicsPane = ({ errors, touched, values: monitoredTrip }) => { - const { itinerary } = monitoredTrip +class TripBasicsPane extends Component { + async componentDidMount () { + // Check itinerary existence for all days. - if (!itinerary) { - return
No itinerary to display.
- } else { - // Show an error indicaton when monitoredTrip.tripName is not blank (from the form's validation schema) - // and that tripName is not already used. - let tripNameValidationState = null - if (touched.tripName) { - tripNameValidationState = errors.tripName ? 'error' : null - } + const itineraryCheckResult = await checkItinerary( + this.props.config.persistence.otp_middleware, + this.props.accessToken, + this.props.monitoredTrip + ) - // Show a combined error indicaton when no day is selected. - let monitoredDaysValidationState = null - ALL_DAYS.forEach(day => { - if (touched[day]) { - if (!monitoredDaysValidationState) { - monitoredDaysValidationState = errors[day] ? 'error' : null - } + console.log(itineraryCheckResult) + } + + render () { + const { errors, touched, values: monitoredTrip } = this.props + const { itinerary } = monitoredTrip + + if (!itinerary) { + return
No itinerary to display.
+ } else { + // Show an error indicaton when monitoredTrip.tripName is not blank (from the form's validation schema) + // and that tripName is not already used. + let tripNameValidationState = null + if (touched.tripName) { + tripNameValidationState = errors.tripName ? 'error' : null } - }) - return ( -
- Selected itinerary: - + // Show a combined error indicaton when no day is selected. + let monitoredDaysValidationState = null + ALL_DAYS.forEach(day => { + if (touched[day]) { + if (!monitoredDaysValidationState) { + monitoredDaysValidationState = errors[day] ? 'error' : null + } + } + }) - - Please provide a name for this trip: - {/* onBlur, onChange, and value are passed automatically. */} - - - {errors.tripName && {errors.tripName}} - + return ( +
+ Selected itinerary: + - - What days to you take this trip? -
- {allDays.map(({ name, text }, index) => ( - - {text} - - - ))} -
- {monitoredDaysValidationState && Please select at least one day to monitor.} -
-
- ) + + Please provide a name for this trip: + {/* onBlur, onChange, and value are passed automatically. */} + + + {errors.tripName && {errors.tripName}} + + + + What days to you take this trip? +
+ {allDays.map(({ name, text }, index) => ( + + {text} + + + ))} +
+ {monitoredDaysValidationState && Please select at least one day to monitor.} +
+
+ ) + } } } -export default TripBasicsPane +// Connect to redux store +const mapStateToProps = (state, ownProps) => { + return { + config: state.otp.config, + accessToken: state.user.accessToken + } +} + +const mapDispatchToProps = { +} + +export default connect(mapStateToProps, mapDispatchToProps)(TripBasicsPane) diff --git a/lib/util/middleware.js b/lib/util/middleware.js index 10403b465..ea7641743 100644 --- a/lib/util/middleware.js +++ b/lib/util/middleware.js @@ -2,6 +2,7 @@ if (typeof (fetch) === 'undefined') require('isomorphic-fetch') const API_USER_PATH = '/api/secure/user' const API_MONITORTRIP_PATH = '/api/secure/monitoredtrip' +const API_ITINERARYCHECK_PATH = '/api/secure/itinerarycheck' /** * This method builds the options object for call to the fetch method. @@ -139,3 +140,12 @@ export async function deleteTrip (middlewareConfig, token, id) { } } } + +export async function checkItinerary (middlewareConfig, token, monitoredTrip) { + const { apiBaseUrl, apiKey } = middlewareConfig + const requestUrl = `${apiBaseUrl}${API_ITINERARYCHECK_PATH}` + + return secureFetch(requestUrl, token, apiKey, 'POST', { + body: JSON.stringify(monitoredTrip) + }) +} From cbf3feb63e19d6c79e2bc1cf2784f25210c83fee Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 1 Oct 2020 18:23:15 -0400 Subject: [PATCH 02/13] feat(TripBasicsPane): Grey out days on which a trip is not available. --- lib/components/user/trip-basics-pane.js | 55 +++++++++++++++++++------ 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/lib/components/user/trip-basics-pane.js b/lib/components/user/trip-basics-pane.js index dbcb9663f..1199bd0d3 100644 --- a/lib/components/user/trip-basics-pane.js +++ b/lib/components/user/trip-basics-pane.js @@ -4,7 +4,9 @@ import { ControlLabel, FormControl, FormGroup, - HelpBlock + HelpBlock, + Label, + ProgressBar } from 'react-bootstrap' import { connect } from 'react-redux' import styled from 'styled-components' @@ -20,10 +22,12 @@ const StyledLabel = styled.label` box-sizing: border-box; display: inline-block; font-weight: inherit; + float: left; + height: 3em; max-width: 150px; min-width: 14.28%; text-align: center; - & > span { + & > span:first-of-type { display: block; width: 100%; } @@ -47,21 +51,32 @@ const allDays = [ * and lets the user edit the trip name and day. */ class TripBasicsPane extends Component { + constructor () { + super() + + this.state = {} + } + async componentDidMount () { // Check itinerary existence for all days. const itineraryCheckResult = await checkItinerary( this.props.config.persistence.otp_middleware, this.props.accessToken, - this.props.monitoredTrip + this.props.values ) console.log(itineraryCheckResult) + + this.setState({ itineraryCheckResult }) + + // TODO: Update Formik state to uncheck days for which the itinerary is not available. } render () { const { errors, touched, values: monitoredTrip } = this.props const { itinerary } = monitoredTrip + const { itineraryCheckResult } = this.state if (!itinerary) { return
No itinerary to display.
@@ -99,16 +114,32 @@ class TripBasicsPane extends Component { What days to you take this trip?
- {allDays.map(({ name, text }, index) => ( - - {text} - - - ))} + {allDays.map(({ name, text }, index) => { + const isDayDisabled = + itineraryCheckResult && + itineraryCheckResult.status === 'success' && + !itineraryCheckResult.data[name] + + const boxClass = isDayDisabled ? 'bg-danger' : (monitoredTrip[name] ? 'bg-primary' : '') + + return ( + + {text} + {isDayDisabled + // eslint-disable-next-line jsx-a11y/label-has-for + ? + : + } + + ) + })} +
+ {!itineraryCheckResult && ( + + + + )} {monitoredDaysValidationState && Please select at least one day to monitor.}
From 8aad883dd815747b9d96a5d44c7c249695fadc5d Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 1 Oct 2020 19:31:52 -0400 Subject: [PATCH 03/13] fix(TripBasicsPane): Don't monitor days on which trips do not exist. --- lib/components/user/trip-basics-pane.js | 46 +++++++++++++++---------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/lib/components/user/trip-basics-pane.js b/lib/components/user/trip-basics-pane.js index 1199bd0d3..0964c5c44 100644 --- a/lib/components/user/trip-basics-pane.js +++ b/lib/components/user/trip-basics-pane.js @@ -4,8 +4,8 @@ import { ControlLabel, FormControl, FormGroup, + Glyphicon, HelpBlock, - Label, ProgressBar } from 'react-bootstrap' import { connect } from 'react-redux' @@ -37,13 +37,13 @@ const StyledLabel = styled.label` ` const allDays = [ - { name: 'monday', text: 'Mon.' }, - { name: 'tuesday', text: 'Tue.' }, - { name: 'wednesday', text: 'Wed.' }, - { name: 'thursday', text: 'Thu.' }, - { name: 'friday', text: 'Fri.' }, - { name: 'saturday', text: 'Sat.' }, - { name: 'sunday', text: 'Sun.' } + { name: 'monday', text: 'Mon.', fullText: 'Mondays' }, + { name: 'tuesday', text: 'Tue.', fullText: 'Tuesdays' }, + { name: 'wednesday', text: 'Wed.', fullText: 'Wednesdays' }, + { name: 'thursday', text: 'Thu.', fullText: 'Thursdays' }, + { name: 'friday', text: 'Fri.', fullText: 'Fridays' }, + { name: 'saturday', text: 'Sat.', fullText: 'Saturdays' }, + { name: 'sunday', text: 'Sun.', fullText: 'Sundays' } ] /** @@ -59,18 +59,26 @@ class TripBasicsPane extends Component { async componentDidMount () { // Check itinerary existence for all days. + const { accessToken, config, setFieldValue, values } = this.props const itineraryCheckResult = await checkItinerary( - this.props.config.persistence.otp_middleware, - this.props.accessToken, - this.props.values + config.persistence.otp_middleware, + accessToken, + values ) - console.log(itineraryCheckResult) - this.setState({ itineraryCheckResult }) - // TODO: Update Formik state to uncheck days for which the itinerary is not available. + // Update the Formik state to uncheck days for which the itinerary is not available. + // Note: If a trip becomes unavailable after the user initially saves a trip, + // upon saving, the days the itinerary does not exist will no longer be monitored. + if (itineraryCheckResult && itineraryCheckResult.status === 'success') { + ALL_DAYS.forEach(day => { + if (!itineraryCheckResult.data[day]) { + setFieldValue(day, false) + } + }) + } } render () { @@ -114,20 +122,20 @@ class TripBasicsPane extends Component { What days to you take this trip?
- {allDays.map(({ name, text }, index) => { + {allDays.map(({ name, fullText, text }, index) => { const isDayDisabled = itineraryCheckResult && itineraryCheckResult.status === 'success' && !itineraryCheckResult.data[name] - const boxClass = isDayDisabled ? 'bg-danger' : (monitoredTrip[name] ? 'bg-primary' : '') + const boxClass = isDayDisabled ? 'alert-danger' : (monitoredTrip[name] ? 'bg-primary' : '') + const notAvailableText = isDayDisabled ? `Trip not available on ${fullText}` : null return ( - + {text} {isDayDisabled - // eslint-disable-next-line jsx-a11y/label-has-for - ? + ? : } From aeb519f9070f26c8279bc675e769b57ea63cd205 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 26 Oct 2020 16:52:24 -0400 Subject: [PATCH 04/13] refactor(TripBasicsPane): Let user select/save days an existing trip is not available. --- lib/components/user/trip-basics-pane.js | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/lib/components/user/trip-basics-pane.js b/lib/components/user/trip-basics-pane.js index 0964c5c44..7e28f7cea 100644 --- a/lib/components/user/trip-basics-pane.js +++ b/lib/components/user/trip-basics-pane.js @@ -82,7 +82,7 @@ class TripBasicsPane extends Component { } render () { - const { errors, touched, values: monitoredTrip } = this.props + const { errors, isCreating, touched, values: monitoredTrip } = this.props const { itinerary } = monitoredTrip const { itineraryCheckResult } = this.state @@ -134,20 +134,26 @@ class TripBasicsPane extends Component { return ( {text} - {isDayDisabled - ? - : + { // Let users save an existing trip, even though it may not be available on some days. + // TODO: improve checking trip availability. + isDayDisabled && isCreating + ? + : } ) })}
- {!itineraryCheckResult && ( - - - - )} + + {itineraryCheckResult + ? ( + <>Your trip is available on the following days above. + ) : ( + + ) + } + {monitoredDaysValidationState && Please select at least one day to monitor.}
From b8faf63432a8e86f66e39000f487705b0ae22d72 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 26 Oct 2020 17:15:39 -0400 Subject: [PATCH 05/13] refactor(TripBasicsPane): Tweak text. --- lib/components/user/trip-basics-pane.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/components/user/trip-basics-pane.js b/lib/components/user/trip-basics-pane.js index 7e28f7cea..4497d8d1e 100644 --- a/lib/components/user/trip-basics-pane.js +++ b/lib/components/user/trip-basics-pane.js @@ -148,7 +148,7 @@ class TripBasicsPane extends Component { {itineraryCheckResult ? ( - <>Your trip is available on the following days above. + <>Your trip is available on the days of the week indicated above. ) : ( ) From cf3d214aece724cf5979a563ab51c0efdcc28d98 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 26 Oct 2020 17:25:39 -0400 Subject: [PATCH 06/13] refactor(TripBasicsPane): Tweak text. --- lib/components/user/trip-basics-pane.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/components/user/trip-basics-pane.js b/lib/components/user/trip-basics-pane.js index 4497d8d1e..d8f71b6ab 100644 --- a/lib/components/user/trip-basics-pane.js +++ b/lib/components/user/trip-basics-pane.js @@ -148,7 +148,7 @@ class TripBasicsPane extends Component { {itineraryCheckResult ? ( - <>Your trip is available on the days of the week indicated above. + <>Your trip is available on the days of the week as indicated above. ) : ( ) From 38b22499b883d6ce22d87c8822d2eacb21eb668b Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 27 Oct 2020 16:42:04 -0400 Subject: [PATCH 07/13] refactor(TripBasicsPane): Fix typo. --- lib/components/user/trip-basics-pane.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/components/user/trip-basics-pane.js b/lib/components/user/trip-basics-pane.js index d8f71b6ab..02cb8cb3f 100644 --- a/lib/components/user/trip-basics-pane.js +++ b/lib/components/user/trip-basics-pane.js @@ -120,7 +120,7 @@ class TripBasicsPane extends Component {
- What days to you take this trip? + What days do you take this trip?
{allDays.map(({ name, fullText, text }, index) => { const isDayDisabled = From b0831d1017638551b8ee0e07db9e67e55071f28a Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 29 Oct 2020 19:23:29 -0400 Subject: [PATCH 08/13] refactor(TripBasicsPane): Adjust to new backend return data structure for itin checks. --- lib/components/user/trip-basics-pane.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/components/user/trip-basics-pane.js b/lib/components/user/trip-basics-pane.js index 02cb8cb3f..c584dca19 100644 --- a/lib/components/user/trip-basics-pane.js +++ b/lib/components/user/trip-basics-pane.js @@ -16,7 +16,7 @@ import { ALL_DAYS } from '../../util/monitored-trip' import TripSummary from './trip-summary' // Styles. -const StyledLabel = styled.label` +const TripDayLabel = styled.label` border: 1px solid #ccc; border-left: none; box-sizing: border-box; @@ -59,7 +59,7 @@ class TripBasicsPane extends Component { async componentDidMount () { // Check itinerary existence for all days. - const { accessToken, config, setFieldValue, values } = this.props + const { accessToken, config, isCreating, setFieldValue, values } = this.props const itineraryCheckResult = await checkItinerary( config.persistence.otp_middleware, @@ -69,12 +69,12 @@ class TripBasicsPane extends Component { this.setState({ itineraryCheckResult }) - // Update the Formik state to uncheck days for which the itinerary is not available. - // Note: If a trip becomes unavailable after the user initially saves a trip, - // upon saving, the days the itinerary does not exist will no longer be monitored. - if (itineraryCheckResult && itineraryCheckResult.status === 'success') { + // For new trips only, + // update the Formik state to uncheck days for which the itinerary is not available. + if (isCreating && itineraryCheckResult && itineraryCheckResult.status === 'success') { ALL_DAYS.forEach(day => { - if (!itineraryCheckResult.data[day]) { + const dayAvailability = itineraryCheckResult.data[day] + if (!dayAvailability || !dayAvailability.isValid) { setFieldValue(day, false) } }) @@ -126,13 +126,13 @@ class TripBasicsPane extends Component { const isDayDisabled = itineraryCheckResult && itineraryCheckResult.status === 'success' && - !itineraryCheckResult.data[name] + (!itineraryCheckResult.data[name] || !itineraryCheckResult.data[name].isValid) const boxClass = isDayDisabled ? 'alert-danger' : (monitoredTrip[name] ? 'bg-primary' : '') const notAvailableText = isDayDisabled ? `Trip not available on ${fullText}` : null return ( - + {text} { // Let users save an existing trip, even though it may not be available on some days. // TODO: improve checking trip availability. @@ -140,7 +140,7 @@ class TripBasicsPane extends Component { ? : } - + ) })}
From ea97ce635402164e163c1f46be35c6ac7d971697 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 29 Oct 2020 21:21:27 -0400 Subject: [PATCH 09/13] refactor(actions/user): Move itinerary check to actions/reducer. --- lib/actions/user.js | 50 ++++++++++++------ lib/components/user/trip-basics-pane.js | 70 +++++++++++++------------ lib/reducers/create-user-reducer.js | 13 +++++ 3 files changed, 82 insertions(+), 51 deletions(-) diff --git a/lib/actions/user.js b/lib/actions/user.js index ba265128c..e575adb05 100644 --- a/lib/actions/user.js +++ b/lib/actions/user.js @@ -5,14 +5,17 @@ import { secureFetch } from '../util/middleware' import { isNewUser } from '../util/user' // Middleware API paths. -const API_MONITORTRIP_PATH = '/api/secure/monitoredtrip' +const API_ITINERARY_CHECK_PATH = '/api/secure/itinerarycheck' +const API_MONITORED_TRIP_PATH = '/api/secure/monitoredtrip' const API_OTPUSER_PATH = '/api/secure/user' -const API_OTPUSER_VERIFYSMS_PATH = '/verify_sms' +const API_OTPUSER_VERIFY_SMS_PATH = '/verify_sms' const setCurrentUser = createAction('SET_CURRENT_USER') const setCurrentUserMonitoredTrips = createAction('SET_CURRENT_USER_MONITORED_TRIPS') const setLastPhoneSmsRequest = createAction('SET_LAST_PHONE_SMS_REQUEST') export const setPathBeforeSignIn = createAction('SET_PATH_BEFORE_SIGNIN') +export const clearItineraryAvailability = createAction('CLEAR_ITINERARY_AVAILABILITY') +const setItineraryAvailability = createAction('SET_ITINERARY_AVAILABILITY') function createNewUser (auth0User) { return { @@ -160,7 +163,7 @@ export function createOrUpdateUser (userData, silentOnSuccess = false) { export function fetchUserMonitoredTrips () { return async function (dispatch, getState) { const { accessToken, apiBaseUrl, apiKey } = getMiddlewareVariables(getState()) - const requestUrl = `${apiBaseUrl}${API_MONITORTRIP_PATH}` + const requestUrl = `${apiBaseUrl}${API_MONITORED_TRIP_PATH}` const { data: trips, status } = await secureFetch(requestUrl, accessToken, apiKey, 'GET') if (status === 'success') { @@ -182,10 +185,10 @@ export function createOrUpdateUserMonitoredTrip (tripData, isNew, silentOnSucces // Determine URL and method to use. if (isNew) { - requestUrl = `${apiBaseUrl}${API_MONITORTRIP_PATH}` + requestUrl = `${apiBaseUrl}${API_MONITORED_TRIP_PATH}` method = 'POST' } else { - requestUrl = `${apiBaseUrl}${API_MONITORTRIP_PATH}/${id}` + requestUrl = `${apiBaseUrl}${API_MONITORED_TRIP_PATH}/${id}` method = 'PUT' } @@ -217,7 +220,7 @@ export function createOrUpdateUserMonitoredTrip (tripData, isNew, silentOnSucces export function deleteUserMonitoredTrip (tripId) { return async function (dispatch, getState) { const { accessToken, apiBaseUrl, apiKey } = getMiddlewareVariables(getState()) - const requestUrl = `${apiBaseUrl}${API_MONITORTRIP_PATH}/${tripId}` + const requestUrl = `${apiBaseUrl}${API_MONITORED_TRIP_PATH}/${tripId}` const { message, status } = await secureFetch(requestUrl, accessToken, apiKey, 'DELETE') if (status === 'success') { @@ -243,7 +246,7 @@ export function requestPhoneVerificationSms (newPhoneNumber) { // TODO: Should throttling be handled in the middleware? if (number !== newPhoneNumber || (now - timestamp) >= 60000) { const { accessToken, apiBaseUrl, apiKey, loggedInUser } = getMiddlewareVariables(state) - const requestUrl = `${apiBaseUrl}${API_OTPUSER_PATH}/${loggedInUser.id}${API_OTPUSER_VERIFYSMS_PATH}/${encodeURIComponent(newPhoneNumber)}` + const requestUrl = `${apiBaseUrl}${API_OTPUSER_PATH}/${loggedInUser.id}${API_OTPUSER_VERIFY_SMS_PATH}/${encodeURIComponent(newPhoneNumber)}` const { message, status } = await secureFetch(requestUrl, accessToken, apiKey, 'GET') @@ -273,7 +276,7 @@ export function requestPhoneVerificationSms (newPhoneNumber) { export function verifyPhoneNumber (code) { return async function (dispatch, getState) { const { accessToken, apiBaseUrl, apiKey, loggedInUser } = getMiddlewareVariables(getState()) - const requestUrl = `${apiBaseUrl}${API_OTPUSER_PATH}/${loggedInUser.id}${API_OTPUSER_VERIFYSMS_PATH}/${code}` + const requestUrl = `${apiBaseUrl}${API_OTPUSER_PATH}/${loggedInUser.id}${API_OTPUSER_VERIFY_SMS_PATH}/${code}` const { data, status } = await secureFetch(requestUrl, accessToken, apiKey, 'POST') @@ -293,13 +296,26 @@ export function verifyPhoneNumber (code) { } } } -/* -export async function checkItinerary (middlewareConfig, token, monitoredTrip) { - const { apiBaseUrl, apiKey } = middlewareConfig - const requestUrl = `${apiBaseUrl}${API_ITINERARYCHECK_PATH}` - - return secureFetch(requestUrl, token, apiKey, 'POST', { - body: JSON.stringify(monitoredTrip) - }) + +/** + * Check itinerary availability (existence) for the given monitored trip. + */ +export function checkItineraryAvailability (trip) { + return async function (dispatch, getState) { + const { accessToken, apiBaseUrl, apiKey } = getMiddlewareVariables(getState()) + const requestUrl = `${apiBaseUrl}${API_ITINERARY_CHECK_PATH}` + + // Empty state before performing the checks. + dispatch(clearItineraryAvailability()) + + const { data, status } = await secureFetch(requestUrl, accessToken, apiKey, 'POST', { + body: JSON.stringify(trip) + }) + + if (status === 'success' && data) { + dispatch(setItineraryAvailability(data)) + } else { + alert('Error checking the availability of your selected trip.') + } + } } -*/ diff --git a/lib/components/user/trip-basics-pane.js b/lib/components/user/trip-basics-pane.js index c584dca19..ca2d42519 100644 --- a/lib/components/user/trip-basics-pane.js +++ b/lib/components/user/trip-basics-pane.js @@ -11,7 +11,7 @@ import { import { connect } from 'react-redux' import styled from 'styled-components' -import { checkItinerary } from '../../util/middleware' +import * as userActions from '../../actions/user' import { ALL_DAYS } from '../../util/monitored-trip' import TripSummary from './trip-summary' @@ -46,45 +46,47 @@ const allDays = [ { name: 'sunday', text: 'Sun.', fullText: 'Sundays' } ] +/** + * @returns true if there is a trip matching for the specified availability/existence check. + */ +function isDayAvailable (dayAvailability) { + return dayAvailability && dayAvailability.isValid +} + /** * This component shows summary information for a trip * and lets the user edit the trip name and day. */ class TripBasicsPane extends Component { - constructor () { - super() - - this.state = {} + componentDidMount () { + // Check itinerary availability (existence) for all days. + const { checkItineraryAvailability, values: monitoredTrip } = this.props + checkItineraryAvailability(monitoredTrip) } - async componentDidMount () { - // Check itinerary existence for all days. - const { accessToken, config, isCreating, setFieldValue, values } = this.props - - const itineraryCheckResult = await checkItinerary( - config.persistence.otp_middleware, - accessToken, - values - ) + componentDidUpdate (prevProps) { + const { isCreating, itineraryAvailability, setFieldValue } = this.props - this.setState({ itineraryCheckResult }) - - // For new trips only, - // update the Formik state to uncheck days for which the itinerary is not available. - if (isCreating && itineraryCheckResult && itineraryCheckResult.status === 'success') { - ALL_DAYS.forEach(day => { - const dayAvailability = itineraryCheckResult.data[day] - if (!dayAvailability || !dayAvailability.isValid) { - setFieldValue(day, false) - } - }) + if (itineraryAvailability !== prevProps.itineraryAvailability) { + // For new trips only, + // update the Formik state to uncheck days for which the itinerary is not available. + if (isCreating && itineraryAvailability) { + ALL_DAYS.forEach(day => { + if (!isDayAvailable(itineraryAvailability[day])) { + setFieldValue(day, false) + } + }) + } } } + componentWillUnmount () { + this.props.clearItineraryAvailability() + } + render () { - const { errors, isCreating, touched, values: monitoredTrip } = this.props + const { errors, isCreating, itineraryAvailability, touched, values: monitoredTrip } = this.props const { itinerary } = monitoredTrip - const { itineraryCheckResult } = this.state if (!itinerary) { return
No itinerary to display.
@@ -123,11 +125,7 @@ class TripBasicsPane extends Component { What days do you take this trip?
{allDays.map(({ name, fullText, text }, index) => { - const isDayDisabled = - itineraryCheckResult && - itineraryCheckResult.status === 'success' && - (!itineraryCheckResult.data[name] || !itineraryCheckResult.data[name].isValid) - + const isDayDisabled = itineraryAvailability && !isDayAvailable(itineraryAvailability[name]) const boxClass = isDayDisabled ? 'alert-danger' : (monitoredTrip[name] ? 'bg-primary' : '') const notAvailableText = isDayDisabled ? `Trip not available on ${fullText}` : null @@ -146,7 +144,7 @@ class TripBasicsPane extends Component {
- {itineraryCheckResult + {itineraryAvailability ? ( <>Your trip is available on the days of the week as indicated above. ) : ( @@ -164,13 +162,17 @@ class TripBasicsPane extends Component { // Connect to redux store const mapStateToProps = (state, ownProps) => { + const { accessToken, itineraryAvailability } = state.user return { + accessToken, config: state.otp.config, - accessToken: state.user.accessToken + itineraryAvailability } } const mapDispatchToProps = { + checkItineraryAvailability: userActions.checkItineraryAvailability, + clearItineraryAvailability: userActions.clearItineraryAvailability } export default connect(mapStateToProps, mapDispatchToProps)(TripBasicsPane) diff --git a/lib/reducers/create-user-reducer.js b/lib/reducers/create-user-reducer.js index eb5a6b430..b02021f80 100644 --- a/lib/reducers/create-user-reducer.js +++ b/lib/reducers/create-user-reducer.js @@ -4,6 +4,7 @@ import update from 'immutability-helper' function createUserReducer () { const initialState = { accessToken: null, + itineraryAvailability: null, lastPhoneSmsRequest: { number: null, status: null, @@ -41,6 +42,18 @@ function createUserReducer () { }) } + case 'SET_ITINERARY_AVAILABILITY': { + return update(state, { + itineraryAvailability: { $set: action.payload } + }) + } + + case 'CLEAR_ITINERARY_AVAILABILITY': { + return update(state, { + itineraryAvailability: { $set: null } + }) + } + default: return state } From 214e266d7b2b56c0e667358d9a5cac689a23212e Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 29 Oct 2020 21:26:53 -0400 Subject: [PATCH 10/13] fix(NarrativeItineraries): Adjust Proptypes. --- lib/components/narrative/narrative-itineraries.js | 2 +- lib/components/narrative/tabbed-itineraries.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/components/narrative/narrative-itineraries.js b/lib/components/narrative/narrative-itineraries.js index 93b245fa6..56c7f9ee0 100644 --- a/lib/components/narrative/narrative-itineraries.js +++ b/lib/components/narrative/narrative-itineraries.js @@ -38,7 +38,7 @@ class NarrativeItineraries extends Component { static propTypes = { itineraries: PropTypes.array, itineraryClass: PropTypes.func, - pending: PropTypes.number, + pending: PropTypes.bool, activeItinerary: PropTypes.number, setActiveItinerary: PropTypes.func, setActiveLeg: PropTypes.func, diff --git a/lib/components/narrative/tabbed-itineraries.js b/lib/components/narrative/tabbed-itineraries.js index 02dc0f2ee..991c454f7 100644 --- a/lib/components/narrative/tabbed-itineraries.js +++ b/lib/components/narrative/tabbed-itineraries.js @@ -15,7 +15,7 @@ class TabbedItineraries extends Component { static propTypes = { itineraries: PropTypes.array, itineraryClass: PropTypes.func, - pending: PropTypes.bool, + pending: PropTypes.number, activeItinerary: PropTypes.number, setActiveItinerary: PropTypes.func, setActiveLeg: PropTypes.func, From efa6a6561c7d9f4a3633526cd9eb98b8524d6825 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 4 Nov 2020 11:42:54 -0500 Subject: [PATCH 11/13] refactor: Adjust to updated itinerary check backend. --- lib/actions/user.js | 20 ++++++++-------- lib/components/user/trip-basics-pane.js | 32 ++++++++++++------------- lib/reducers/create-user-reducer.js | 6 ++--- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/lib/actions/user.js b/lib/actions/user.js index e575adb05..9eafb3c15 100644 --- a/lib/actions/user.js +++ b/lib/actions/user.js @@ -5,17 +5,17 @@ import { secureFetch } from '../util/middleware' import { isNewUser } from '../util/user' // Middleware API paths. -const API_ITINERARY_CHECK_PATH = '/api/secure/itinerarycheck' +const API_MONITORED_TRIP_CHECK_SUBPATH = '/checkitinerary' const API_MONITORED_TRIP_PATH = '/api/secure/monitoredtrip' const API_OTPUSER_PATH = '/api/secure/user' -const API_OTPUSER_VERIFY_SMS_PATH = '/verify_sms' +const API_OTPUSER_VERIFY_SMS_SUBPATH = '/verify_sms' const setCurrentUser = createAction('SET_CURRENT_USER') const setCurrentUserMonitoredTrips = createAction('SET_CURRENT_USER_MONITORED_TRIPS') const setLastPhoneSmsRequest = createAction('SET_LAST_PHONE_SMS_REQUEST') export const setPathBeforeSignIn = createAction('SET_PATH_BEFORE_SIGNIN') -export const clearItineraryAvailability = createAction('CLEAR_ITINERARY_AVAILABILITY') -const setItineraryAvailability = createAction('SET_ITINERARY_AVAILABILITY') +export const clearItineraryExistence = createAction('CLEAR_ITINERARY_AVAILABILITY') +const setitineraryExistence = createAction('SET_ITINERARY_AVAILABILITY') function createNewUser (auth0User) { return { @@ -246,7 +246,7 @@ export function requestPhoneVerificationSms (newPhoneNumber) { // TODO: Should throttling be handled in the middleware? if (number !== newPhoneNumber || (now - timestamp) >= 60000) { const { accessToken, apiBaseUrl, apiKey, loggedInUser } = getMiddlewareVariables(state) - const requestUrl = `${apiBaseUrl}${API_OTPUSER_PATH}/${loggedInUser.id}${API_OTPUSER_VERIFY_SMS_PATH}/${encodeURIComponent(newPhoneNumber)}` + const requestUrl = `${apiBaseUrl}${API_OTPUSER_PATH}/${loggedInUser.id}${API_OTPUSER_VERIFY_SMS_SUBPATH}/${encodeURIComponent(newPhoneNumber)}` const { message, status } = await secureFetch(requestUrl, accessToken, apiKey, 'GET') @@ -276,7 +276,7 @@ export function requestPhoneVerificationSms (newPhoneNumber) { export function verifyPhoneNumber (code) { return async function (dispatch, getState) { const { accessToken, apiBaseUrl, apiKey, loggedInUser } = getMiddlewareVariables(getState()) - const requestUrl = `${apiBaseUrl}${API_OTPUSER_PATH}/${loggedInUser.id}${API_OTPUSER_VERIFY_SMS_PATH}/${code}` + const requestUrl = `${apiBaseUrl}${API_OTPUSER_PATH}/${loggedInUser.id}${API_OTPUSER_VERIFY_SMS_SUBPATH}/${code}` const { data, status } = await secureFetch(requestUrl, accessToken, apiKey, 'POST') @@ -300,20 +300,20 @@ export function verifyPhoneNumber (code) { /** * Check itinerary availability (existence) for the given monitored trip. */ -export function checkItineraryAvailability (trip) { +export function checkItineraryExistence (trip) { return async function (dispatch, getState) { const { accessToken, apiBaseUrl, apiKey } = getMiddlewareVariables(getState()) - const requestUrl = `${apiBaseUrl}${API_ITINERARY_CHECK_PATH}` + const requestUrl = `${apiBaseUrl}${API_MONITORED_TRIP_PATH}${API_MONITORED_TRIP_CHECK_SUBPATH}` // Empty state before performing the checks. - dispatch(clearItineraryAvailability()) + dispatch(clearItineraryExistence()) const { data, status } = await secureFetch(requestUrl, accessToken, apiKey, 'POST', { body: JSON.stringify(trip) }) if (status === 'success' && data) { - dispatch(setItineraryAvailability(data)) + dispatch(setitineraryExistence(data)) } else { alert('Error checking the availability of your selected trip.') } diff --git a/lib/components/user/trip-basics-pane.js b/lib/components/user/trip-basics-pane.js index ca2d42519..bf8a414a3 100644 --- a/lib/components/user/trip-basics-pane.js +++ b/lib/components/user/trip-basics-pane.js @@ -49,8 +49,8 @@ const allDays = [ /** * @returns true if there is a trip matching for the specified availability/existence check. */ -function isDayAvailable (dayAvailability) { - return dayAvailability && dayAvailability.isValid +function itineraryExists (dayCheck) { + return dayCheck && dayCheck.valid } /** @@ -60,19 +60,19 @@ function isDayAvailable (dayAvailability) { class TripBasicsPane extends Component { componentDidMount () { // Check itinerary availability (existence) for all days. - const { checkItineraryAvailability, values: monitoredTrip } = this.props - checkItineraryAvailability(monitoredTrip) + const { checkItineraryExistence, values: monitoredTrip } = this.props + checkItineraryExistence(monitoredTrip) } componentDidUpdate (prevProps) { - const { isCreating, itineraryAvailability, setFieldValue } = this.props + const { isCreating, itineraryExistence, setFieldValue } = this.props - if (itineraryAvailability !== prevProps.itineraryAvailability) { + if (itineraryExistence !== prevProps.itineraryExistence) { // For new trips only, // update the Formik state to uncheck days for which the itinerary is not available. - if (isCreating && itineraryAvailability) { + if (isCreating && itineraryExistence) { ALL_DAYS.forEach(day => { - if (!isDayAvailable(itineraryAvailability[day])) { + if (!itineraryExists(itineraryExistence[day])) { setFieldValue(day, false) } }) @@ -81,11 +81,11 @@ class TripBasicsPane extends Component { } componentWillUnmount () { - this.props.clearItineraryAvailability() + this.props.clearItineraryExistence() } render () { - const { errors, isCreating, itineraryAvailability, touched, values: monitoredTrip } = this.props + const { errors, isCreating, itineraryExistence, touched, values: monitoredTrip } = this.props const { itinerary } = monitoredTrip if (!itinerary) { @@ -125,7 +125,7 @@ class TripBasicsPane extends Component { What days do you take this trip?
{allDays.map(({ name, fullText, text }, index) => { - const isDayDisabled = itineraryAvailability && !isDayAvailable(itineraryAvailability[name]) + const isDayDisabled = itineraryExistence && !itineraryExists(itineraryExistence[name]) const boxClass = isDayDisabled ? 'alert-danger' : (monitoredTrip[name] ? 'bg-primary' : '') const notAvailableText = isDayDisabled ? `Trip not available on ${fullText}` : null @@ -144,7 +144,7 @@ class TripBasicsPane extends Component {
- {itineraryAvailability + {itineraryExistence ? ( <>Your trip is available on the days of the week as indicated above. ) : ( @@ -162,17 +162,17 @@ class TripBasicsPane extends Component { // Connect to redux store const mapStateToProps = (state, ownProps) => { - const { accessToken, itineraryAvailability } = state.user + const { accessToken, itineraryExistence } = state.user return { accessToken, config: state.otp.config, - itineraryAvailability + itineraryExistence } } const mapDispatchToProps = { - checkItineraryAvailability: userActions.checkItineraryAvailability, - clearItineraryAvailability: userActions.clearItineraryAvailability + checkItineraryExistence: userActions.checkItineraryExistence, + clearItineraryExistence: userActions.clearItineraryExistence } export default connect(mapStateToProps, mapDispatchToProps)(TripBasicsPane) diff --git a/lib/reducers/create-user-reducer.js b/lib/reducers/create-user-reducer.js index b02021f80..46cebe500 100644 --- a/lib/reducers/create-user-reducer.js +++ b/lib/reducers/create-user-reducer.js @@ -4,7 +4,7 @@ import update from 'immutability-helper' function createUserReducer () { const initialState = { accessToken: null, - itineraryAvailability: null, + itineraryExistence: null, lastPhoneSmsRequest: { number: null, status: null, @@ -44,13 +44,13 @@ function createUserReducer () { case 'SET_ITINERARY_AVAILABILITY': { return update(state, { - itineraryAvailability: { $set: action.payload } + itineraryExistence: { $set: action.payload } }) } case 'CLEAR_ITINERARY_AVAILABILITY': { return update(state, { - itineraryAvailability: { $set: null } + itineraryExistence: { $set: null } }) } From e7cb719f2534a49058b4a03f9ce1beafddf59ecf Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 4 Nov 2020 14:31:20 -0500 Subject: [PATCH 12/13] refactor(actions/user): Twaek comment/error msg for itinerary check. --- lib/actions/user.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/actions/user.js b/lib/actions/user.js index 9eafb3c15..f991acbd1 100644 --- a/lib/actions/user.js +++ b/lib/actions/user.js @@ -298,7 +298,7 @@ export function verifyPhoneNumber (code) { } /** - * Check itinerary availability (existence) for the given monitored trip. + * Check itinerary existence for the given monitored trip. */ export function checkItineraryExistence (trip) { return async function (dispatch, getState) { @@ -315,7 +315,7 @@ export function checkItineraryExistence (trip) { if (status === 'success' && data) { dispatch(setitineraryExistence(data)) } else { - alert('Error checking the availability of your selected trip.') + alert('Error checking whether your selected trip is possible.') } } } From 846892ab816dc32ccd6688b06042f030225c453a Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 10 Nov 2020 17:33:47 -0500 Subject: [PATCH 13/13] refactor(TripBasicsPane,actions/user): Address PR comments. --- lib/actions/user.js | 7 +++---- lib/components/user/trip-basics-pane.js | 26 ++++++++----------------- lib/reducers/create-user-reducer.js | 4 ++-- 3 files changed, 13 insertions(+), 24 deletions(-) diff --git a/lib/actions/user.js b/lib/actions/user.js index f991acbd1..83d54036c 100644 --- a/lib/actions/user.js +++ b/lib/actions/user.js @@ -5,7 +5,6 @@ import { secureFetch } from '../util/middleware' import { isNewUser } from '../util/user' // Middleware API paths. -const API_MONITORED_TRIP_CHECK_SUBPATH = '/checkitinerary' const API_MONITORED_TRIP_PATH = '/api/secure/monitoredtrip' const API_OTPUSER_PATH = '/api/secure/user' const API_OTPUSER_VERIFY_SMS_SUBPATH = '/verify_sms' @@ -14,8 +13,8 @@ const setCurrentUser = createAction('SET_CURRENT_USER') const setCurrentUserMonitoredTrips = createAction('SET_CURRENT_USER_MONITORED_TRIPS') const setLastPhoneSmsRequest = createAction('SET_LAST_PHONE_SMS_REQUEST') export const setPathBeforeSignIn = createAction('SET_PATH_BEFORE_SIGNIN') -export const clearItineraryExistence = createAction('CLEAR_ITINERARY_AVAILABILITY') -const setitineraryExistence = createAction('SET_ITINERARY_AVAILABILITY') +export const clearItineraryExistence = createAction('CLEAR_ITINERARY_EXISTENCE') +const setitineraryExistence = createAction('SET_ITINERARY_EXISTENCE') function createNewUser (auth0User) { return { @@ -303,7 +302,7 @@ export function verifyPhoneNumber (code) { export function checkItineraryExistence (trip) { return async function (dispatch, getState) { const { accessToken, apiBaseUrl, apiKey } = getMiddlewareVariables(getState()) - const requestUrl = `${apiBaseUrl}${API_MONITORED_TRIP_PATH}${API_MONITORED_TRIP_CHECK_SUBPATH}` + const requestUrl = `${apiBaseUrl}${API_MONITORED_TRIP_PATH}/checkitinerary` // Empty state before performing the checks. dispatch(clearItineraryExistence()) diff --git a/lib/components/user/trip-basics-pane.js b/lib/components/user/trip-basics-pane.js index bf8a414a3..928f7b697 100644 --- a/lib/components/user/trip-basics-pane.js +++ b/lib/components/user/trip-basics-pane.js @@ -12,7 +12,6 @@ import { connect } from 'react-redux' import styled from 'styled-components' import * as userActions from '../../actions/user' -import { ALL_DAYS } from '../../util/monitored-trip' import TripSummary from './trip-summary' // Styles. @@ -46,13 +45,6 @@ const allDays = [ { name: 'sunday', text: 'Sun.', fullText: 'Sundays' } ] -/** - * @returns true if there is a trip matching for the specified availability/existence check. - */ -function itineraryExists (dayCheck) { - return dayCheck && dayCheck.valid -} - /** * This component shows summary information for a trip * and lets the user edit the trip name and day. @@ -71,9 +63,9 @@ class TripBasicsPane extends Component { // For new trips only, // update the Formik state to uncheck days for which the itinerary is not available. if (isCreating && itineraryExistence) { - ALL_DAYS.forEach(day => { - if (!itineraryExists(itineraryExistence[day])) { - setFieldValue(day, false) + allDays.forEach(({ name }) => { + if (!itineraryExistence[name].valid) { + setFieldValue(name, false) } }) } @@ -100,10 +92,10 @@ class TripBasicsPane extends Component { // Show a combined error indicaton when no day is selected. let monitoredDaysValidationState = null - ALL_DAYS.forEach(day => { - if (touched[day]) { + allDays.forEach(({ name }) => { + if (touched[name]) { if (!monitoredDaysValidationState) { - monitoredDaysValidationState = errors[day] ? 'error' : null + monitoredDaysValidationState = errors[name] ? 'error' : null } } }) @@ -125,7 +117,7 @@ class TripBasicsPane extends Component { What days do you take this trip?
{allDays.map(({ name, fullText, text }, index) => { - const isDayDisabled = itineraryExistence && !itineraryExists(itineraryExistence[name]) + const isDayDisabled = itineraryExistence && !itineraryExistence[name].valid const boxClass = isDayDisabled ? 'alert-danger' : (monitoredTrip[name] ? 'bg-primary' : '') const notAvailableText = isDayDisabled ? `Trip not available on ${fullText}` : null @@ -162,10 +154,8 @@ class TripBasicsPane extends Component { // Connect to redux store const mapStateToProps = (state, ownProps) => { - const { accessToken, itineraryExistence } = state.user + const { itineraryExistence } = state.user return { - accessToken, - config: state.otp.config, itineraryExistence } } diff --git a/lib/reducers/create-user-reducer.js b/lib/reducers/create-user-reducer.js index 46cebe500..8e7123658 100644 --- a/lib/reducers/create-user-reducer.js +++ b/lib/reducers/create-user-reducer.js @@ -42,13 +42,13 @@ function createUserReducer () { }) } - case 'SET_ITINERARY_AVAILABILITY': { + case 'SET_ITINERARY_EXISTENCE': { return update(state, { itineraryExistence: { $set: action.payload } }) } - case 'CLEAR_ITINERARY_AVAILABILITY': { + case 'CLEAR_ITINERARY_EXISTENCE': { return update(state, { itineraryExistence: { $set: null } })