UNPKG

@kiwicom/smart-faq

Version:

Smart FAQ

296 lines (270 loc) 8.51 kB
// @flow import idx from 'idx'; import * as React from 'react'; import { withRouter } from 'react-router-dom'; import { graphql, createFragmentContainer } from 'react-relay'; import differenceInHours from 'date-fns/difference_in_hours'; import { Stack, Button, ButtonLink, TextLink } from '@kiwicom/orbit-components'; import { BaggageChecked, Ticket, Insurance, } from '@kiwicom/orbit-components/lib/icons'; import Translate from '@kiwicom/nitro/lib/components/Translate'; import { isUrgentBooking, updateFAQSection, getDepartureTimeByType, } from '../common/booking/utils'; import OneWayTrip from './bookingTypes/OneWayTrip'; import ReturnTrip from './bookingTypes/ReturnTrip'; import MulticityOverlayTrip from './bookingTypes/MulticityOverlayTrip'; import Contact from './bookingItem/Contact'; import Notification from './bookingItem/Notification'; import Header from './bookingItem/Header'; import { simpleTracker } from '../../shared/helpers/analytics/trackers'; import ScrollableContent from '../common/ScrollableContent'; import bookingTypes from '../common/booking/bookingTypes'; import { URGENCY_THRESHOLD } from '../helpers/dateUtils'; import { replaceWithCurrentDomain, addDeepLink } from '../helpers/UrlHelpers'; import type { NearestBooking_booking } from './__generated__/NearestBookingQuery.graphql'; import FAQExtraInfoButton from '../../shared/StaticFAQ/FAQExtraInfo/FAQExtraInfoButton'; import { BookingState } from '../context/BookingState'; import features from '../../feature-toggles.json'; import { track } from '../../shared/cuckoo/tracker'; type ContextProps = { onSetFAQSection: (isUrgent: boolean, isPastBooking: boolean) => void, }; type ComponentProps = { +booking: NearestBooking_booking, onSetFAQSection: (isUrgent: boolean, isPastBooking: boolean) => void, }; type Props = ComponentProps; const goToMMB = () => { simpleTracker('smartFAQBookingOverview', { action: 'goToMMB', }); track('BookingOverview', 'goToMMB'); }; const clickEticket = () => { simpleTracker('smartFAQBookingOverview', { action: 'clickOnEticket', }); track('BookingOverview', 'clickOnEticket'); }; const addInsurance = () => { simpleTracker('smartFAQBookingOverview', { action: 'addInsurance', }); track('BookingOverview', 'addInsurance'); }; class BookingDetail extends React.Component<Props> { componentDidMount() { updateFAQSection(this.props); } componentDidUpdate() { updateFAQSection(this.props); } renderByType = (booking: NearestBooking_booking) => { if (booking.type === bookingTypes.ONE_WAY) { return <OneWayTrip booking={booking} />; } if (booking.type === bookingTypes.RETURN) { return <ReturnTrip booking={booking} />; } if (booking.type === bookingTypes.MULTICITY) { return <MulticityOverlayTrip booking={booking} />; } return null; }; getArrivalByType = (booking: NearestBooking_booking) => { let date = null; if (booking.type === bookingTypes.ONE_WAY) { date = idx(booking, _ => _.trip.arrival.time); } if (booking.type === bookingTypes.RETURN) { date = idx(booking, _ => _.inbound.arrival.time); } if (booking.type === bookingTypes.MULTICITY) { date = idx(booking, _ => _.end.time); } return date ? new Date(date) : null; }; decideIfIsFutureAndUrgent = (time: ?Date) => { const timeDelta = time ? differenceInHours(time, new Date()) : null; const isUrgent = timeDelta !== null && URGENCY_THRESHOLD > timeDelta && timeDelta >= 0; return { timeDelta, isFuture: timeDelta !== null && timeDelta > 0, isUrgent, }; }; render() { const { booking } = this.props; const eTicketLink = idx(booking, _ => _.assets.ticketUrl); const departureTime = getDepartureTimeByType(booking); const arrivalTime = this.getArrivalByType(booking); const departureInfo = this.decideIfIsFutureAndUrgent(departureTime); const arrivalInfo = this.decideIfIsFutureAndUrgent(arrivalTime); const { timeDelta, isFuture } = departureInfo; const isUrgent = isUrgentBooking(booking.isPastBooking, departureTime); const isInsuranceAvailable = Boolean( (idx(booking, _ => _.availableServices.insurance.passengers) || []) .length, ); return ( <ScrollableContent dataCy="nearestBooking" styles="width: 100%; padding:40px; background-color: #ffffff; box-shadow: inset -1px 0 0 0 #e8edf1;" > <div data-test={arrivalInfo.isFuture ? 'upcoming' : 'past'}> <Header booking={booking} isFuture={arrivalInfo.isFuture} /> </div> {features.notification && (isFuture && booking.status === 'CONFIRMED' && timeDelta && ( <Notification hoursLeft={timeDelta} isUrgent={isUrgent} /> ))} <Stack direction="row" wrap spaceAfter="normal" dataTest="booking-sfaq-buttons" > {features.baggage_info && ( <FAQExtraInfoButton category="baggage" icon={<BaggageChecked />}> <Translate t={__('smartfaq.single_booking_page.booking_detail.baggage')} /> </FAQExtraInfoButton> )} <FAQExtraInfoButton category="boarding-passes" icon={<Ticket />} dataTest="btn-boarding-passes" > <Translate t={__( 'smartfaq.single_booking_page.booking_detail.boarding_passes', )} /> </FAQExtraInfoButton> {isInsuranceAvailable && ( <ButtonLink onClick={addInsurance} external iconLeft={<Insurance />} href={replaceWithCurrentDomain( addDeepLink(booking.directAccessURL, 'insurance'), )} > <Translate t={__('smartfaq.single_booking_page.booking_detail.insurance')} /> </ButtonLink> )} </Stack> <div data-test="booking-sfaq-itinerary"> {this.renderByType(booking)} </div> <Stack direction="column"> <Button type="secondary" external onClick={goToMMB} href={replaceWithCurrentDomain(booking.directAccessURL)} dataTest="btn-manage-booking" > <Translate t={__( 'smartfaq.single_booking_page.booking_detail.manage_my_booking', )} /> </Button> {eTicketLink && ( <TextLink href={eTicketLink} type="secondary" external onClick={clickEticket} size="normal" > <Translate t={__( 'smartfaq.single_booking_page.booking_detail.download_e_ticket', )} /> </TextLink> )} </Stack> {isUrgent && <Contact booking={booking} dataTest="contact" />} </ScrollableContent> ); } } export const RawBookingDetail = BookingDetail; const BookingDetailWithFAQHandler = (props: ComponentProps) => ( <BookingState.Consumer> {({ onSetFAQSection }: ContextProps) => ( <BookingDetail {...props} onSetFAQSection={onSetFAQSection} /> )} </BookingState.Consumer> ); export default createFragmentContainer( withRouter(BookingDetailWithFAQHandler), graphql` fragment BookingDetail_booking on BookingInterface { type: __typename status assets { ticketUrl } availableServices { insurance { passengers { databaseId } } } directAccessURL isPastBooking ...Header_booking ... on BookingOneWay { ...OneWayTrip_booking trip { departure { time } arrival { time } } } ... on BookingReturn { ...ReturnTrip_booking outbound { departure { time } } inbound { arrival { time } } } ... on BookingMulticity { ...MulticityOverlayTrip_booking start { time } end { time } } ...Contact_booking } `, );