feat: SW-1583 Enabled reward night in booking widget
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
"use client"
|
||||
|
||||
import { useCallback, useEffect, useRef } from "react"
|
||||
import { useFormContext } from "react-hook-form"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { ErrorCircleIcon } from "@/components/Icons"
|
||||
import Checkbox from "@/components/TempDesignSystem/Form/Checkbox"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
|
||||
import styles from "./reward-night.module.css"
|
||||
|
||||
import type { BookingWidgetSchema } from "@/types/components/bookingWidget"
|
||||
|
||||
export function RewardNight() {
|
||||
const intl = useIntl()
|
||||
const {
|
||||
setValue,
|
||||
getValues,
|
||||
formState: { errors },
|
||||
trigger,
|
||||
} = useFormContext<BookingWidgetSchema>()
|
||||
const ref = useRef<HTMLDivElement | null>(null)
|
||||
const reward = intl.formatMessage({ id: "Book Reward Night" })
|
||||
const redemptionErr = errors["redemption"]
|
||||
const bookingCode = getValues("bookingCode.value")
|
||||
const isMultiRoomError = redemptionErr?.message?.indexOf("Multi-room") === 0
|
||||
const errorInfoColor = isMultiRoomError ? "red" : "blue"
|
||||
|
||||
function validateBookingWidget(value: boolean) {
|
||||
trigger("redemption")
|
||||
if (value && bookingCode) {
|
||||
setValue("bookingCode.value", "", { shouldValidate: true })
|
||||
setTimeout(() => {
|
||||
trigger("redemption")
|
||||
}, 5000)
|
||||
}
|
||||
}
|
||||
|
||||
const resetOnMultiroomError = useCallback(() => {
|
||||
if (isMultiRoomError) {
|
||||
setValue("redemption", false, { shouldValidate: true })
|
||||
}
|
||||
}, [isMultiRoomError, setValue])
|
||||
|
||||
function closeOnBlur(evt: FocusEvent) {
|
||||
const target = evt.relatedTarget as HTMLElement
|
||||
if (ref.current && target && !ref.current.contains(target)) {
|
||||
resetOnMultiroomError()
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const clearIfOutside = function (evt: Event) {
|
||||
const target = evt.target as HTMLElement
|
||||
if (ref.current && target && !ref.current.contains(target)) {
|
||||
resetOnMultiroomError()
|
||||
}
|
||||
}
|
||||
document.body.addEventListener("click", clearIfOutside)
|
||||
return () => {
|
||||
document.body.removeEventListener("click", clearIfOutside)
|
||||
}
|
||||
}, [resetOnMultiroomError, ref])
|
||||
|
||||
return (
|
||||
<div ref={ref} onBlur={(e) => closeOnBlur(e.nativeEvent)}>
|
||||
<Checkbox
|
||||
hideError
|
||||
name="redemption"
|
||||
registerOptions={{
|
||||
onChange: (e) => {
|
||||
validateBookingWidget(e.target.value)
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Caption color="uiTextMediumContrast" asChild>
|
||||
<span>{reward}</span>
|
||||
</Caption>
|
||||
</Checkbox>
|
||||
{redemptionErr && (
|
||||
<div className={styles.errorContainer}>
|
||||
<Caption
|
||||
className={styles.error}
|
||||
type="regular"
|
||||
color={errorInfoColor}
|
||||
>
|
||||
<ErrorCircleIcon
|
||||
color={errorInfoColor}
|
||||
className={styles.errorIcon}
|
||||
/>
|
||||
{intl.formatMessage({ id: redemptionErr.message })}
|
||||
</Caption>
|
||||
{isMultiRoomError ? (
|
||||
// ToDo: Replace with Remove extra rooms JSX element after SW-963 is merged
|
||||
<Body>{"Remove extra rooms"}</Body>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
.errorContainer {
|
||||
background: var(--Base-Surface-Primary-light-Normal);
|
||||
border-radius: var(--Spacing-x-one-and-half);
|
||||
display: grid;
|
||||
gap: var(--Spacing-x1);
|
||||
padding: var(--Spacing-x2);
|
||||
position: absolute;
|
||||
top: calc(100% + 16px);
|
||||
width: 320px;
|
||||
}
|
||||
|
||||
.error {
|
||||
display: flex;
|
||||
gap: var(--Spacing-x-half);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.errorIcon {
|
||||
min-width: 20px;
|
||||
}
|
||||
@@ -2,12 +2,15 @@
|
||||
import { FormProvider, useForm } from "react-hook-form"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { env } from "@/env/client"
|
||||
|
||||
import SkeletonShimmer from "@/components/SkeletonShimmer"
|
||||
import Checkbox from "@/components/TempDesignSystem/Form/Checkbox"
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
import { Tooltip } from "@/components/TempDesignSystem/Tooltip"
|
||||
|
||||
import BookingCode from "../BookingCode"
|
||||
import { RewardNight } from "../RewardNight"
|
||||
|
||||
import styles from "./voucher.module.css"
|
||||
|
||||
@@ -31,36 +34,44 @@ export default function Voucher() {
|
||||
<BookingCode />
|
||||
</div>
|
||||
<div className={styles.options}>
|
||||
<div className={styles.option}>
|
||||
<Tooltip
|
||||
heading={disabledBookingOptionsHeader}
|
||||
text={disabledBookingOptionsText}
|
||||
position="bottom"
|
||||
arrow="left"
|
||||
>
|
||||
<Checkbox name="useBonus" registerOptions={{ disabled: true }}>
|
||||
<Caption color="disabled" asChild>
|
||||
<span>{bonus}</span>
|
||||
</Caption>
|
||||
</Checkbox>
|
||||
{/* <InfoCircleIcon color="white" className={styles.infoIcon} /> Out of scope for this release */}
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className={styles.option}>
|
||||
<Tooltip
|
||||
heading={disabledBookingOptionsHeader}
|
||||
text={disabledBookingOptionsText}
|
||||
position="bottom"
|
||||
arrow="left"
|
||||
>
|
||||
<Checkbox name="useReward" registerOptions={{ disabled: true }}>
|
||||
<Caption color="disabled" asChild>
|
||||
<span>{reward}</span>
|
||||
</Caption>
|
||||
</Checkbox>
|
||||
{/* <InfoCircleIcon color="white" className={styles.infoIcon} /> Out of scope for this release */}
|
||||
</Tooltip>
|
||||
</div>
|
||||
{env.NEXT_PUBLIC_HIDE_FOR_NEXT_RELEASE ? (
|
||||
<>
|
||||
<div className={styles.option}>
|
||||
<Tooltip
|
||||
heading={disabledBookingOptionsHeader}
|
||||
text={disabledBookingOptionsText}
|
||||
position="bottom"
|
||||
arrow="left"
|
||||
>
|
||||
<Checkbox name="useBonus" registerOptions={{ disabled: true }}>
|
||||
<Caption color="disabled" asChild>
|
||||
<span>{bonus}</span>
|
||||
</Caption>
|
||||
</Checkbox>
|
||||
{/* <InfoCircleIcon color="white" className={styles.infoIcon} /> Out of scope for this release */}
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className={styles.option}>
|
||||
<Tooltip
|
||||
heading={disabledBookingOptionsHeader}
|
||||
text={disabledBookingOptionsText}
|
||||
position="bottom"
|
||||
arrow="left"
|
||||
>
|
||||
<Checkbox name="useReward" registerOptions={{ disabled: true }}>
|
||||
<Caption color="disabled" asChild>
|
||||
<span>{reward}</span>
|
||||
</Caption>
|
||||
</Checkbox>
|
||||
{/* <InfoCircleIcon color="white" className={styles.infoIcon} /> Out of scope for this release */}
|
||||
</Tooltip>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className={styles.option}>
|
||||
<RewardNight />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
@@ -87,16 +98,18 @@ export function VoucherSkeleton() {
|
||||
<SkeletonShimmer width={"100%"} />
|
||||
</div>
|
||||
<div className={styles.options}>
|
||||
{env.NEXT_PUBLIC_HIDE_FOR_NEXT_RELEASE ? null : (
|
||||
<div className={styles.option}>
|
||||
<Checkbox name="useBonus" registerOptions={{ disabled: true }}>
|
||||
<Caption color="disabled" asChild>
|
||||
<span>{bonus}</span>
|
||||
</Caption>
|
||||
</Checkbox>
|
||||
</div>
|
||||
)}
|
||||
<div className={styles.option}>
|
||||
<Checkbox name="useBonus" registerOptions={{ disabled: true }}>
|
||||
<Caption color="disabled" asChild>
|
||||
<span>{bonus}</span>
|
||||
</Caption>
|
||||
</Checkbox>
|
||||
</div>
|
||||
<div className={styles.option}>
|
||||
<Checkbox name="useReward" registerOptions={{ disabled: true }}>
|
||||
<Caption color="disabled" asChild>
|
||||
<Checkbox name="redemption">
|
||||
<Caption color="uiTextMediumContrast" asChild>
|
||||
<span>{reward}</span>
|
||||
</Caption>
|
||||
</Checkbox>
|
||||
|
||||
Reference in New Issue
Block a user