diff --git a/lib/components/app/batch-routing-panel.tsx b/lib/components/app/batch-routing-panel.tsx index 0ef568403..b7a1778bf 100644 --- a/lib/components/app/batch-routing-panel.tsx +++ b/lib/components/app/batch-routing-panel.tsx @@ -1,14 +1,6 @@ import { connect } from 'react-redux' import { CSSTransition, TransitionGroup } from 'react-transition-group' import { FormattedMessage, injectIntl, IntlShape } from 'react-intl' - -import { getActiveSearch, getShowUserSettings } from '../../util/state' -import { getPersistenceMode } from '../../util/user' -import AdvancedSettingsPanel from '../form/advanced-settings-panel' -import BatchSettings from '../form/batch-settings' -import InvisibleA11yLabel from '../util/invisible-a11y-label' -import LocationField from '../form/connected-location-field' -import NarrativeItineraries from '../narrative/narrative-itineraries' import React, { Component, FormEvent } from 'react' import { @@ -17,6 +9,13 @@ import { transitionDuration, TransitionStyles } from '../form/styled' +import { getActiveSearch, getShowUserSettings } from '../../util/state' +import { getPersistenceMode } from '../../util/user' +import AdvancedSettingsPanel from '../form/advanced-settings-panel' +import BatchSettings from '../form/batch-settings' +import InvisibleA11yLabel from '../util/invisible-a11y-label' +import LocationField from '../form/connected-location-field' +import NarrativeItineraries from '../narrative/narrative-itineraries' import SwitchButton from '../form/switch-button' import UserSettings from '../form/user-settings' import ViewerContainer from '../viewers/viewer-container' @@ -113,6 +112,7 @@ class BatchRoutingPanel extends Component { )} diff --git a/lib/components/form/advanced-settings-panel.tsx b/lib/components/form/advanced-settings-panel.tsx index c2b09213e..34afe9ca6 100644 --- a/lib/components/form/advanced-settings-panel.tsx +++ b/lib/components/form/advanced-settings-panel.tsx @@ -4,47 +4,42 @@ import { ModeSettingRenderer, populateSettingWithValue } from '@opentripplanner/trip-form' - -import { blue, getBaseColor } from '../util/colors' import { Close } from '@styled-icons/fa-solid' - import { connect } from 'react-redux' import { decodeQueryParams, DelimitedArrayParam } from 'serialize-query-params' import { FormattedMessage, useIntl } from 'react-intl' - -import { generateModeSettingValues } from '../../util/api' - +import { invisibleCss } from '@opentripplanner/trip-form/lib/MetroModeSelector' import { ModeButtonDefinition, ModeSetting, ModeSettingValues } from '@opentripplanner/types' +import { Search } from '@styled-icons/fa-solid/Search' +import React, { RefObject, useContext } from 'react' +import styled from 'styled-components' import * as apiActions from '../../actions/api' import * as formActions from '../../actions/form' import { AppReduxState } from '../../util/state-types' +import { blue, getBaseColor } from '../util/colors' import { ComponentContext } from '../../util/contexts' +import { generateModeSettingValues } from '../../util/api' import PageTitle from '../util/page-title' - -import React, { RefObject, useContext } from 'react' -import styled from 'styled-components' +import toast from 'react-hot-toast' import { addCustomSettingLabels, addModeButtonIcon, + alertUserTripPlan, onSettingsUpdate, pipe, populateSettingWithIcon, setModeButton } from './util' - -import { invisibleCss } from '@opentripplanner/trip-form/lib/MetroModeSelector' - import { setModeButtonEnabled } from './batch-settings' import { styledCheckboxCss } from './styled' const PanelOverlay = styled.div` - background-color: #fff; height: 100%; left: 0; padding: 1.5em; @@ -79,26 +74,57 @@ const HeaderContainer = styled.div` const Subheader = styled.h2` ${invisibleCss} ` -const baseColor = getBaseColor() -const accentColor = baseColor || blue[900] + +const PlanTripButton = styled.button` + align-items: center; + display: flex; + justify-content: center; + gap: 0.5em; + background-color: var(--main-base-color, ${blue[900]}); + border: 0; + width: 47%; + height: 51px; + color: white; + font-weight: 700; +` + +const ReturnToTripPlanButton = styled(PlanTripButton)` + border: 2px solid var(--main-base-color, ${blue[900]}); + background-color: white; + color: var(--main-base-color, ${blue[900]}); +` +const ButtonContainer = styled.div` + display: flex; + justify-content: space-between; + margin-top: 2em; +` const AdvancedSettingsPanel = ({ closeAdvancedSettings, + currentQuery, enabledModeButtons, innerRef, modeButtonOptions, modeSettingDefinitions, modeSettingValues, + onPlanTripClick, + routingQuery, setQueryParam }: { closeAdvancedSettings: () => void + currentQuery: any enabledModeButtons: string[] innerRef: RefObject modeButtonOptions: ModeButtonDefinition[] modeSettingDefinitions: ModeSetting[] modeSettingValues: ModeSettingValues + onPlanTripClick: () => void + routingQuery: () => void setQueryParam: (evt: any) => void }): JSX.Element => { + const baseColor = getBaseColor() + const accentColor = baseColor || blue[900] + const intl = useIntl() const closeButtonText = intl.formatMessage({ id: 'components.BatchSearchScreen.closeAdvancedPreferences' @@ -110,6 +136,16 @@ const AdvancedSettingsPanel = ({ // @ts-expect-error Context not typed const { ModeIcon } = useContext(ComponentContext) + const planTrip = () => { + alertUserTripPlan(intl, currentQuery, onPlanTripClick, routingQuery) + closeAdvancedSettings() + } + + const closeAndAnnounce = () => { + closeAdvancedSettings() + toast.success(intl.formatMessage({ id: 'actions.user.preferencesSaved' })) + } + const processSettings = (settings: ModeSetting[]) => settings.map( pipe( @@ -148,7 +184,7 @@ const AdvancedSettingsPanel = ({

{headerText}

@@ -181,6 +217,15 @@ const AdvancedSettingsPanel = ({ onSettingsUpdate(setQueryParam) )} /> + + + Back to Trip Plan + + + + + + ) } diff --git a/lib/components/form/batch-settings.tsx b/lib/components/form/batch-settings.tsx index aecaef103..1c50355a7 100644 --- a/lib/components/form/batch-settings.tsx +++ b/lib/components/form/batch-settings.tsx @@ -4,11 +4,7 @@ import { populateSettingWithValue } from '@opentripplanner/trip-form' import { connect } from 'react-redux' -import { - decodeQueryParams, - DelimitedArrayParam, - encodeQueryParams -} from 'use-query-params' +import { decodeQueryParams } from 'use-query-params' import { ModeButtonDefinition, ModeSetting, @@ -25,12 +21,12 @@ import { ComponentContext } from '../../util/contexts' import { generateModeSettingValues } from '../../util/api' import { getActiveSearch, hasValidLocation } from '../../util/state' import { getBaseColor, getDarkenedBaseColor } from '../util/colors' -import { RoutingQueryCallResult } from '../../actions/api-constants' import { StyledIconWrapper } from '../util/styledIcon' import { addCustomSettingLabels, addModeButtonIcon, + alertUserTripPlan, modesQueryParamConfig, onSettingsUpdate, pipe, @@ -117,47 +113,8 @@ function BatchSettings({ ) const _planTrip = useCallback(() => { - // Check for any validation issues in query. - const issues = [] - if (!hasValidLocation(currentQuery, 'from')) { - issues.push(intl.formatMessage({ id: 'components.BatchSettings.origin' })) - } - if (!hasValidLocation(currentQuery, 'to')) { - issues.push( - intl.formatMessage({ id: 'components.BatchSettings.destination' }) - ) - } - onPlanTripClick && onPlanTripClick() - if (issues.length > 0) { - // TODO: replace with less obtrusive validation. - window.alert( - intl.formatMessage( - { id: 'components.BatchSettings.validationMessage' }, - { issues: intl.formatList(issues, { type: 'conjunction' }) } - ) - ) - return - } - - // Plan trip. - updateQueryTimeIfLeavingNow() - const routingQueryResult = routingQuery() - - // If mode combination is not valid (i.e. produced no query), alert the user. - if (routingQueryResult === RoutingQueryCallResult.INVALID_MODE_SELECTION) { - window.alert( - intl.formatMessage({ - id: 'components.BatchSettings.invalidModeSelection' - }) - ) - } - }, [ - currentQuery, - intl, - onPlanTripClick, - routingQuery, - updateQueryTimeIfLeavingNow - ]) + alertUserTripPlan(intl, currentQuery, onPlanTripClick, routingQuery) + }, [currentQuery, intl, onPlanTripClick, routingQuery]) /** * Check whether the mode selector is showing a popup. diff --git a/lib/components/form/styled.ts b/lib/components/form/styled.ts index f9816f938..0ecf13634 100644 --- a/lib/components/form/styled.ts +++ b/lib/components/form/styled.ts @@ -1,4 +1,4 @@ -import { blue, getBaseColor, grey } from '../util/colors' +import { blue, grey } from '../util/colors' import { DateTimeSelector, SettingsSelectorPanel, @@ -235,7 +235,6 @@ export const StyledLocationField = styled(LocationField)` export const advancedPanelClassName = 'advanced-panel' export const mainPanelClassName = 'main-panel' export const transitionDuration = prefersReducedMotion ? 0 : 175 -const baseColor = getBaseColor() const wipeOffset = 7 @@ -339,7 +338,7 @@ export const styledCheckboxCss = css` &:checked + label { &::after { - background-color: ${baseColor || blue[700]}; + background-color: var(--main-base-color, ${blue[700]}); ${toggleTransition}; } diff --git a/lib/components/form/util.tsx b/lib/components/form/util.tsx index 12aeeead0..23b5ab8fe 100644 --- a/lib/components/form/util.tsx +++ b/lib/components/form/util.tsx @@ -4,6 +4,9 @@ import { ModeButtonDefinition, ModeSetting } from '@opentripplanner/types' import React from 'react' import { getFormattedMode } from '../../util/i18n' +import { hasValidLocation } from '../../util/state' +import { RoutingQueryCallResult } from '../../actions/api-constants' +import { updateQueryTimeIfLeavingNow } from '../../actions/form' // This method is used to daisy-chain a series of functions together on a given value export function pipe(...fns: Array<(arg: T) => T>) { @@ -69,3 +72,45 @@ export const setModeButton = encodeQueryParams(modesQueryParamConfig, { modeButtons: newButtons }) ) } + +export const alertUserTripPlan = ( + intl: IntlShape, + currentQuery: any, + onPlanTripClick: () => void, + routingQuery: () => any +): void => { + // Check for any validation issues in query. + const issues: string[] = [] + if (!hasValidLocation(currentQuery, 'from')) { + issues.push(intl.formatMessage({ id: 'components.BatchSettings.origin' })) + } + if (!hasValidLocation(currentQuery, 'to')) { + issues.push( + intl.formatMessage({ id: 'components.BatchSettings.destination' }) + ) + } + onPlanTripClick() + if (issues.length > 0) { + // TODO: replace with less obtrusive validation. + window.alert( + intl.formatMessage( + { id: 'components.BatchSettings.validationMessage' }, + { issues: intl.formatList(issues, { type: 'conjunction' }) } + ) + ) + return + } + + // Plan trip. + updateQueryTimeIfLeavingNow() + const routingQueryResult = routingQuery() + + // If mode combination is not valid (i.e. produced no query), alert the user. + if (routingQueryResult === RoutingQueryCallResult.INVALID_MODE_SELECTION) { + window.alert( + intl.formatMessage({ + id: 'components.BatchSettings.invalidModeSelection' + }) + ) + } +} diff --git a/lib/components/mobile/batch-search-screen.tsx b/lib/components/mobile/batch-search-screen.tsx index 49d500c71..d2aab6a9f 100644 --- a/lib/components/mobile/batch-search-screen.tsx +++ b/lib/components/mobile/batch-search-screen.tsx @@ -1,6 +1,8 @@ import { connect } from 'react-redux' +import { CSSTransition, TransitionGroup } from 'react-transition-group' import { injectIntl, IntlShape } from 'react-intl' import React, { Component } from 'react' +import toast from 'react-hot-toast' import * as uiActions from '../../actions/ui' import { @@ -9,8 +11,6 @@ import { transitionDuration, TransitionStyles } from '../form/styled' -import { CSSTransition, TransitionGroup } from 'react-transition-group' - import { MobileScreens } from '../../actions/ui-constants' import AdvancedSettingsPanel from '../form/advanced-settings-panel' import BatchSettings from '../form/batch-settings' @@ -117,6 +117,7 @@ class BatchSearchScreen extends Component { )}