Files
web/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/BookingAlert/index.tsx
Tobias Johansson 15adca9eb1 Merged in fix/SW-2707-no-availability-message (pull request #2420)
fix(SW-2707): Added new error code and set alert again when errorCode is changed

* fix(SW-2707): Added new error code and set alert again when errorCode is changed


Approved-by: Michael Zetterberg
2025-06-25 11:13:47 +00:00

153 lines
4.0 KiB
TypeScript

"use client"
import { usePathname, useSearchParams } from "next/navigation"
import { useEffect, useRef, useState } from "react"
import { useIntl } from "react-intl"
import { BookingErrorCodeEnum } from "@/constants/booking"
import { selectRate } from "@/constants/routes/hotelReservation"
import { useEnterDetailsStore } from "@/stores/enter-details"
import Alert from "@/components/TempDesignSystem/Alert"
import useLang from "@/hooks/useLang"
import useStickyPosition from "@/hooks/useStickyPosition"
import styles from "./bookingAlert.module.css"
import { AlertTypeEnum } from "@/types/enums/alert"
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(() => {
setShowAlert(!!errorCode)
}, [errorCode])
function getErrorMessage(errorCode: string | null) {
switch (errorCode) {
case BookingErrorCodeEnum.TransactionCancelled:
return intl.formatMessage({
defaultMessage: "You have now cancelled your payment.",
})
case BookingErrorCodeEnum.AvailabilityError:
case BookingErrorCodeEnum.NoAvailabilityForRateAndRoomType:
return intl.formatMessage({
defaultMessage:
"Unfortunately, one of the rooms you selected is sold out. Please choose another room to proceed.",
})
default:
return intl.formatMessage({
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 ref = useRef<HTMLDivElement>(null)
const { getTopOffset } = useStickyPosition()
useEffect(() => {
if (isVisible) {
setShowAlert(true)
}
}, [isVisible, setShowAlert])
useEffect(() => {
const el = ref.current
if (showAlert && el) {
window.scrollTo({
top: el.offsetTop - getTopOffset(),
behavior: "smooth",
})
}
}, [showAlert, getTopOffset])
if (!showAlert) return null
const isAvailabilityError =
errorCode === BookingErrorCodeEnum.AvailabilityError ||
errorCode === BookingErrorCodeEnum.NoAvailabilityForRateAndRoomType
return (
<div className={styles.wrapper} ref={ref}>
<Alert
type={severityLevel}
variant="inline"
text={errorMessage}
close={discardAlert}
link={
isAvailabilityError
? {
title: intl.formatMessage({
defaultMessage: "Change room",
}),
url: selectRateReturnUrl,
}
: undefined
}
/>
</div>
)
}