fix: Upgrade booking-flow eslint config * Upgrade booking-flow eslint config Approved-by: Bianca Widstam
219 lines
6.2 KiB
TypeScript
219 lines
6.2 KiB
TypeScript
"use client"
|
|
|
|
import { usePathname, useSearchParams } from "next/navigation"
|
|
import { useCallback, useEffect, useRef, useState } from "react"
|
|
import { useIntl } from "react-intl"
|
|
|
|
import { AlertTypeEnum } from "@scandic-hotels/common/constants/alert"
|
|
import { selectRate } from "@scandic-hotels/common/constants/routes/hotelReservation"
|
|
import useStickyPosition from "@scandic-hotels/common/hooks/useStickyPosition"
|
|
import { Alert } from "@scandic-hotels/design-system/Alert"
|
|
import { trackNoAvailability } from "@scandic-hotels/tracking/NoAvailabilityTracking"
|
|
import { SEARCH_TYPE_REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
|
|
import { BookingErrorCodeEnum } from "@scandic-hotels/trpc/enums/bookingErrorCode"
|
|
|
|
import useLang from "../../../../hooks/useLang"
|
|
import { useEnterDetailsStore } from "../../../../stores/enter-details"
|
|
import { mapPackageToLabel } from "../../../../utils/getRoomFeatureDescription"
|
|
|
|
import styles from "./bookingAlert.module.css"
|
|
|
|
function useBookingErrorAlert() {
|
|
const updateSearchParams = useEnterDetailsStore(
|
|
(state) => state.actions.updateSeachParamString
|
|
)
|
|
|
|
const intl = useIntl()
|
|
const lang = useLang()
|
|
const searchParams = useSearchParams()
|
|
const pathname = usePathname()
|
|
|
|
const errorCode = searchParams.get("errorCode")
|
|
const errorMessage = getErrorMessage(errorCode)
|
|
const severityLevel =
|
|
errorCode === BookingErrorCodeEnum.TransactionCancelled
|
|
? AlertTypeEnum.Warning
|
|
: AlertTypeEnum.Alarm
|
|
|
|
const [showAlert, setShowAlert] = useState(!!errorCode)
|
|
|
|
const selectRateReturnUrl = getSelectRateReturnUrl()
|
|
|
|
useEffect(() => {
|
|
// eslint-disable-next-line react-hooks/set-state-in-effect
|
|
setShowAlert(!!errorCode)
|
|
}, [errorCode])
|
|
|
|
function getErrorMessage(errorCode: string | null) {
|
|
switch (errorCode) {
|
|
case BookingErrorCodeEnum.TransactionCancelled:
|
|
return intl.formatMessage({
|
|
id: "enterDetails.bookingAlert.transactionCancelled",
|
|
defaultMessage: "You have now cancelled your payment.",
|
|
})
|
|
case BookingErrorCodeEnum.AvailabilityError:
|
|
case BookingErrorCodeEnum.NoAvailabilityForRateAndRoomType:
|
|
return intl.formatMessage({
|
|
id: "error.availabilityErrorMessage",
|
|
defaultMessage:
|
|
"Unfortunately, one of the rooms you selected is sold out. Please choose another room to proceed.",
|
|
})
|
|
default:
|
|
return intl.formatMessage({
|
|
id: "enterDetails.bookingAlert.genericError",
|
|
defaultMessage:
|
|
"We had an issue processing your booking. Please try again. No charges have been made.",
|
|
})
|
|
}
|
|
}
|
|
|
|
function discardAlert() {
|
|
setShowAlert(false)
|
|
const queryParams = new URLSearchParams(searchParams.toString())
|
|
queryParams.delete("errorCode")
|
|
updateSearchParams(queryParams.toString())
|
|
|
|
window.history.replaceState({}, "", `${pathname}?${queryParams.toString()}`)
|
|
}
|
|
|
|
function getSelectRateReturnUrl() {
|
|
const queryParams = new URLSearchParams(searchParams.toString())
|
|
queryParams.delete("errorCode")
|
|
return `${selectRate(lang)}?${queryParams.toString()}`
|
|
}
|
|
|
|
return {
|
|
showAlert,
|
|
errorCode,
|
|
errorMessage,
|
|
severityLevel,
|
|
discardAlert,
|
|
setShowAlert,
|
|
selectRateReturnUrl,
|
|
}
|
|
}
|
|
|
|
interface BookingAlertProps {
|
|
isVisible?: boolean
|
|
}
|
|
|
|
export default function BookingAlert({ isVisible = false }: BookingAlertProps) {
|
|
const intl = useIntl()
|
|
|
|
const {
|
|
showAlert,
|
|
errorCode,
|
|
errorMessage,
|
|
severityLevel,
|
|
discardAlert,
|
|
setShowAlert,
|
|
selectRateReturnUrl,
|
|
} = useBookingErrorAlert()
|
|
|
|
const trackNoAvailabilityError = useNoAvailabilityTracking()
|
|
|
|
const isAvailabilityError =
|
|
errorCode === BookingErrorCodeEnum.AvailabilityError ||
|
|
errorCode === BookingErrorCodeEnum.NoAvailabilityForRateAndRoomType
|
|
|
|
const ref = useRef<HTMLDivElement>(null)
|
|
const topOffset = useStickyPosition().getTopOffset()
|
|
|
|
useEffect(() => {
|
|
if (isVisible) {
|
|
setShowAlert(true)
|
|
}
|
|
}, [isVisible, setShowAlert])
|
|
|
|
useEffect(() => {
|
|
const el = ref.current
|
|
|
|
if (showAlert && el) {
|
|
document.documentElement.style.overflow = ""
|
|
window.scrollTo({
|
|
top: el.offsetTop - topOffset,
|
|
behavior: "smooth",
|
|
})
|
|
}
|
|
}, [showAlert, topOffset])
|
|
|
|
useEffect(() => {
|
|
if (showAlert && isAvailabilityError) {
|
|
trackNoAvailabilityError()
|
|
}
|
|
}, [showAlert, isAvailabilityError, trackNoAvailabilityError])
|
|
|
|
if (!showAlert) return null
|
|
|
|
return (
|
|
<div className={styles.wrapper} ref={ref}>
|
|
<Alert
|
|
type={severityLevel}
|
|
variant="inline"
|
|
text={errorMessage}
|
|
close={discardAlert}
|
|
link={
|
|
isAvailabilityError
|
|
? {
|
|
title: intl.formatMessage({
|
|
id: "enterDetails.bookingAlert.changeRoomLink",
|
|
defaultMessage: "Change room",
|
|
}),
|
|
url: selectRateReturnUrl,
|
|
}
|
|
: undefined
|
|
}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function useNoAvailabilityTracking() {
|
|
const { fromDate, toDate, hotelId, bookingCode, searchType, rooms } =
|
|
useEnterDetailsStore((state) => state.booking)
|
|
const hotelName = useEnterDetailsStore((state) => state.hotelName)
|
|
const lang = useLang()
|
|
|
|
const specialRoomType = rooms
|
|
?.map((room) => {
|
|
const packages = room.packages
|
|
?.map((pkg) => mapPackageToLabel(pkg))
|
|
.join(",")
|
|
return packages ?? ""
|
|
})
|
|
.join("|")
|
|
|
|
const track = useCallback(
|
|
() =>
|
|
trackNoAvailability({
|
|
specialRoomType,
|
|
lang,
|
|
searchTerm: hotelName,
|
|
fromDate,
|
|
toDate,
|
|
hotelId,
|
|
noOfRooms: rooms.length,
|
|
searchType: "hotel",
|
|
rewardNight: searchType === SEARCH_TYPE_REDEMPTION ? "yes" : "no",
|
|
bookingCode: bookingCode ?? "n/a",
|
|
pageId: "details",
|
|
pageName: "hotelreservation|details",
|
|
pageType: "bookingenterdetailspage",
|
|
siteSections: "hotelreservation|details",
|
|
domain: typeof window !== "undefined" ? window.location.host : "",
|
|
}),
|
|
[
|
|
specialRoomType,
|
|
lang,
|
|
hotelName,
|
|
fromDate,
|
|
toDate,
|
|
hotelId,
|
|
rooms.length,
|
|
searchType,
|
|
bookingCode,
|
|
]
|
|
)
|
|
return track
|
|
}
|