Skip to content

Commit

Permalink
Merge pull request #1078 from opentripplanner/use-existing-trip-exist…
Browse files Browse the repository at this point in the history
…ence

Use existing trip existence
  • Loading branch information
binh-dam-ibigroup authored Nov 29, 2023
2 parents 2a6d4df + 3edbb0b commit c223ab4
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 84 deletions.
4 changes: 2 additions & 2 deletions lib/components/user/monitored-trip/saved-trip-editor.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { FormattedMessage, useIntl } from 'react-intl'
import React, { ComponentType } from 'react'

import { MonitoredTrip } from '../types'
import BackLink from '../back-link'
import PageTitle from '../../util/page-title'
import StackedPanesWithSave from '../stacked-panes-with-save'
Expand All @@ -9,8 +10,7 @@ interface Props {
isCreating: boolean
onCancel: () => void
panes: Record<string, ComponentType>
// TODO: Combine monitored trip types
values: Record<string, unknown>
values: MonitoredTrip
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/* eslint-disable react/prop-types */
import { Button, Panel } from 'react-bootstrap'
import { connect } from 'react-redux'
import { FormattedMessage, injectIntl, useIntl } from 'react-intl'
import { FormattedMessage, injectIntl, IntlShape, useIntl } from 'react-intl'
import { Pause } from '@styled-icons/fa-solid/Pause'
import { PencilAlt } from '@styled-icons/fa-solid/PencilAlt'
import { Play } from '@styled-icons/fa-solid/Play'
Expand All @@ -11,8 +10,10 @@ import React, { Component } from 'react'

import * as uiActions from '../../../actions/ui'
import * as userActions from '../../../actions/user'
import { AppReduxState } from '../../../util/state-types'
import { IconWithText } from '../../util/styledIcon'
import { InlineLoading } from '../../narrative/loading'
import { MonitoredTrip } from '../types'
import {
PageHeading,
TripHeader,
Expand All @@ -29,15 +30,34 @@ import withLoggedInUserSupport from '../with-logged-in-user-support'

import TripSummaryPane from './trip-summary-pane'

interface ItemProps {
confirmAndDeleteUserMonitoredTrip: (id: string, intl: IntlShape) => void
intl: IntlShape
routeTo: (url: any) => void
togglePauseTrip: (trip: MonitoredTrip, intl: IntlShape) => void
trip: MonitoredTrip
}

interface ItemState {
pendingRequest: 'pause' | 'delete' | false
}

interface Props {
trips?: MonitoredTrip[]
}

/**
* This class manages events and rendering for one item in the saved trip list.
*/
class TripListItem extends Component {
state = {
pendingRequest: false
class TripListItem extends Component<ItemProps, ItemState> {
constructor(props: ItemProps) {
super(props)
this.state = {
pendingRequest: false
}
}

componentDidUpdate = (prevProps) => {
componentDidUpdate = (prevProps: ItemProps) => {
if (prevProps.trip.isActive !== this.props.trip.isActive) {
this.setState({ pendingRequest: false })
}
Expand Down Expand Up @@ -160,7 +180,7 @@ const ConnectedTripListItem = connect(
/**
* This component displays the list of saved trips for the logged-in user.
*/
const SavedTripList = ({ trips }) => {
const SavedTripList = ({ trips }: Props) => {
const intl = useIntl()
let content

Expand Down Expand Up @@ -212,17 +232,15 @@ const SavedTripList = ({ trips }) => {

// connect to the redux store

const mapStateToProps = (state) => {
const mapStateToProps = (state: AppReduxState) => {
return {
trips: state.user.loggedInUserMonitoredTrips
}
}

const mapDispatchToProps = {}

export default withLoggedInUserSupport(
withAuthenticationRequired(
connect(mapStateToProps, mapDispatchToProps)(SavedTripList),
connect(mapStateToProps)(SavedTripList),
RETURN_TO_CURRENT_ROUTE
),
true
Expand Down
55 changes: 22 additions & 33 deletions lib/components/user/monitored-trip/trip-basics-pane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
} from 'react-bootstrap'
import { Field, FormikProps } from 'formik'
import { FormattedMessage, injectIntl } from 'react-intl'
import { Itinerary } from '@opentripplanner/types'
import { Prompt } from 'react-router'
// @ts-expect-error FormikErrorFocus does not support TypeScript yet.
import FormikErrorFocus from 'formik-error-focus'
Expand All @@ -18,9 +17,14 @@ import styled from 'styled-components'
import type { IntlShape, WrappedComponentProps } from 'react-intl'

import * as userActions from '../../../actions/user'
import {
ALL_DAYS,
getFormattedDayOfWeekPlural
} from '../../../util/monitored-trip'
import { AppReduxState } from '../../../util/state-types'
import { FieldSet } from '../styled'
import { getErrorStates } from '../../../util/ui'
import { getFormattedDayOfWeekPlural } from '../../../util/monitored-trip'
import { ItineraryExistence, MonitoredTrip } from '../types'
import FormattedDayOfWeek from '../../util/formatted-day-of-week'
import FormattedDayOfWeekCompact from '../../util/formatted-day-of-week-compact'
import FormattedValidationError from '../../util/formatted-validation-error'
Expand All @@ -29,41 +33,21 @@ import InvisibleA11yLabel from '../../util/invisible-a11y-label'
import TripStatus from './trip-status'
import TripSummary from './trip-summary'

interface Fields {
friday: boolean
itinerary: Itinerary
monday: boolean
saturday: boolean
sunday: boolean
thursday: boolean
tripName: string
tuesday: boolean
wednesday: boolean
}

type TripBasicsProps = WrappedComponentProps &
FormikProps<Fields> & {
FormikProps<MonitoredTrip> & {
canceled: boolean
checkItineraryExistence: (monitoredTrip: unknown, intl: IntlShape) => void
checkItineraryExistence: (
monitoredTrip: MonitoredTrip,
intl: IntlShape
) => void
clearItineraryExistence: () => void
isCreating: boolean
itineraryExistence: Record<string, { valid: boolean }>
itineraryExistence?: ItineraryExistence
}

// FIXME: move to shared types file
type ErrorStates = 'success' | 'warning' | 'error' | null | undefined

// FIXME: combine back with monitored trips when that is typed.
const ALL_DAYS = [
'monday',
'tuesday',
'wednesday',
'thursday',
'friday',
'saturday',
'sunday'
] as const

// Styles.
const AvailableDays = styled(FieldSet)`
& > span {
Expand Down Expand Up @@ -144,9 +128,11 @@ class TripBasicsPane extends Component<TripBasicsProps> {
}

componentDidMount() {
// Check itinerary availability (existence) for all days.
// Check itinerary availability (existence) for all days if not already done.
const { checkItineraryExistence, intl, values: monitoredTrip } = this.props
checkItineraryExistence(monitoredTrip, intl)
if (!monitoredTrip.itineraryExistence) {
checkItineraryExistence(monitoredTrip, intl)
}
}

componentDidUpdate(prevProps: TripBasicsProps) {
Expand All @@ -169,6 +155,8 @@ class TripBasicsPane extends Component<TripBasicsProps> {
values: monitoredTrip
} = this.props
const { itinerary } = monitoredTrip
const finalItineraryExistence =
monitoredTrip.itineraryExistence || itineraryExistence

// Prevent user from leaving when form has been changed,
// but don't show it when they click submit or cancel.
Expand Down Expand Up @@ -238,7 +226,8 @@ class TripBasicsPane extends Component<TripBasicsProps> {
</legend>
{ALL_DAYS.map((day) => {
const isDayDisabled =
itineraryExistence && !itineraryExistence[day].valid
finalItineraryExistence &&
!finalItineraryExistence[day]?.valid
const boxClass = isDayDisabled
? 'alert-danger'
: monitoredTrip[day]
Expand Down Expand Up @@ -278,7 +267,7 @@ class TripBasicsPane extends Component<TripBasicsProps> {
})}
</AvailableDays>
<HelpBlock role="status">
{itineraryExistence ? (
{finalItineraryExistence ? (
<FormattedMessage id="components.TripBasicsPane.tripIsAvailableOnDaysIndicated" />
) : (
<ProgressBar
Expand Down Expand Up @@ -307,7 +296,7 @@ class TripBasicsPane extends Component<TripBasicsProps> {

// Connect to redux store

const mapStateToProps = (state: any) => {
const mapStateToProps = (state: AppReduxState) => {
const { itineraryExistence } = state.user
return {
itineraryExistence
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import styled from 'styled-components'

import { FieldSet } from '../styled'
import { IconWithText } from '../../util/styledIcon'
import { MonitoredTrip } from '../types'

// Element styles
const SettingsList = styled.ul`
Expand Down Expand Up @@ -152,12 +153,7 @@ function DurationOptions({
return <Options defaultValue={defaultValue} options={options} />
}

interface Fields {
arrivalVarianceMinutesThreshold: number
departureVarianceMinutesThreshold: number
}

interface Props extends FormikProps<Fields> {
interface Props extends FormikProps<MonitoredTrip> {
notificationChannel: string
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React from 'react'
import { FormattedList, FormattedMessage } from 'react-intl'
import React from 'react'

import { dayFieldsToArray } from '../../../util/monitored-trip'
import { MonitoredTripProps } from '../types'
import FormattedDayOfWeek from '../../util/formatted-day-of-week'
import Strong from '../../util/strong-text'

Expand All @@ -10,22 +11,24 @@ import TripSummary from './trip-summary'
/**
* Displays the summary information of a monitored trip.
*/
const TripSummaryPane = ({ monitoredTrip }) => {
const TripSummaryPane = ({
monitoredTrip
}: MonitoredTripProps): JSX.Element => {
const { itinerary, leadTimeInMinutes } = monitoredTrip

if (!itinerary) {
return (
<div>
<FormattedMessage id='common.itineraryDescriptions.noItineraryToDisplay' />
<FormattedMessage id="common.itineraryDescriptions.noItineraryToDisplay" />
</div>
)
} else {
const days = (
<FormattedList
type='conjunction'
value={dayFieldsToArray(monitoredTrip).map(
d => <FormattedDayOfWeek day={d} key={d} />
)}
type="conjunction"
value={dayFieldsToArray(monitoredTrip).map((d) => (
<FormattedDayOfWeek day={d} key={d} />
))}
/>
)

Expand All @@ -35,21 +38,22 @@ const TripSummaryPane = ({ monitoredTrip }) => {
<TripSummary monitoredTrip={monitoredTrip} />
<p>
<FormattedMessage
id='components.TripSummaryPane.happensOnDays'
id="components.TripSummaryPane.happensOnDays"
values={{ days, strong: Strong }}
/>
</p>
<p>
{monitoredTrip.isActive
? <FormattedMessage
id={'components.TripSummaryPane.notifications'}
{monitoredTrip.isActive ? (
<FormattedMessage
id="components.TripSummaryPane.notifications"
values={{ leadTimeInMinutes, strong: Strong }}
/>
: <FormattedMessage
id={'components.TripSummaryPane.notificationsDisabled'}
) : (
<FormattedMessage
id="components.TripSummaryPane.notificationsDisabled"
values={{ leadTimeInMinutes, strong: Strong }}
/>
}
)}
</p>
</>
)
Expand Down
14 changes: 2 additions & 12 deletions lib/components/user/monitored-trip/trip-summary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,15 @@ import React, { useContext } from 'react'
import styled from 'styled-components'

import { ComponentContext } from '../../../util/contexts'
import { MonitoredTripProps } from '../types'
import FormattedDuration from '../../util/formatted-duration'
import InvisibleA11yLabel from '../../util/invisible-a11y-label'

const SummaryContainer = styled.div`
margin-bottom: 10px;
`

interface Props {
// TODO: use a more complete definition of monitored trip.
monitoredTrip: {
itinerary: {
duration: number
endTime: number
startTime: number
}
}
}

const TripSummary = ({ monitoredTrip }: Props): JSX.Element => {
const TripSummary = ({ monitoredTrip }: MonitoredTripProps): JSX.Element => {
const { itinerary } = monitoredTrip
const { duration, endTime, startTime } = itinerary
// @ts-expect-error TODO: add ModesAndRoutes to ItineraryBody attribute of ComponentContext
Expand Down
28 changes: 28 additions & 0 deletions lib/components/user/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import { Itinerary } from '@opentripplanner/types'

import { DaysOfWeek } from '../../util/monitored-trip'

/**
* A user-saved, favorite location.
*/
Expand Down Expand Up @@ -30,3 +34,27 @@ export interface User {
export type EditedUser = Omit<User, 'notificationChannel'> & {
notificationChannel: string[]
}

export interface ItineraryExistenceDay {
valid: boolean
}

export type ItineraryExistence = Record<DaysOfWeek, ItineraryExistenceDay>

export type MonitoredTrip = Record<DaysOfWeek, boolean> & {
arrivalVarianceMinutesThreshold: number
departureVarianceMinutesThreshold: number
excludeFederalHolidays?: boolean
id: string
isActive: boolean
itinerary: Itinerary
itineraryExistence?: ItineraryExistence
leadTimeInMinutes: number
queryParams: string
tripName: string
userId: string
}

export interface MonitoredTripProps {
monitoredTrip: MonitoredTrip
}
Loading

0 comments on commit c223ab4

Please sign in to comment.