From 51b70f303207987ad57dd5fa4a579af6f8d208e4 Mon Sep 17 00:00:00 2001 From: Hrishikesh Vaipurkar Date: Thu, 27 Feb 2025 11:41:04 +0100 Subject: [PATCH] feat: SW-1583 Enabled reward night in booking widget --- .../components/BookingWidget/Client.tsx | 3 +- .../FormContent/RewardNight/index.tsx | 103 ++++++++++++++++++ .../RewardNight/reward-night.module.css | 20 ++++ .../FormContent/Voucher/index.tsx | 91 +++++++++------- .../components/Forms/BookingWidget/index.tsx | 2 + .../components/Forms/BookingWidget/schema.ts | 25 ++++- .../components/GuestsRoomsPicker/Form.tsx | 65 ++++++----- .../TempDesignSystem/Form/Checkbox/index.tsx | 3 +- apps/scandic-web/i18n/dictionaries/en.json | 1 + .../types/components/bookingWidget/index.ts | 1 + .../types/components/checkbox/index.ts | 1 + apps/scandic-web/utils/url.ts | 1 + 12 files changed, 247 insertions(+), 69 deletions(-) create mode 100644 apps/scandic-web/components/Forms/BookingWidget/FormContent/RewardNight/index.tsx create mode 100644 apps/scandic-web/components/Forms/BookingWidget/FormContent/RewardNight/reward-night.module.css diff --git a/apps/scandic-web/components/BookingWidget/Client.tsx b/apps/scandic-web/components/BookingWidget/Client.tsx index c22ddd03e..b892ff11e 100644 --- a/apps/scandic-web/components/BookingWidget/Client.tsx +++ b/apps/scandic-web/components/BookingWidget/Client.tsx @@ -102,8 +102,7 @@ export default function BookingWidgetClient({ value: selectedBookingCode, remember: false, }, - redemption: false, - voucher: false, + redemption: bookingWidgetSearchData?.searchType === "redemption", rooms: defaultRoomsData, }, shouldFocusError: false, diff --git a/apps/scandic-web/components/Forms/BookingWidget/FormContent/RewardNight/index.tsx b/apps/scandic-web/components/Forms/BookingWidget/FormContent/RewardNight/index.tsx new file mode 100644 index 000000000..cff1a111f --- /dev/null +++ b/apps/scandic-web/components/Forms/BookingWidget/FormContent/RewardNight/index.tsx @@ -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() + const ref = useRef(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 ( +
closeOnBlur(e.nativeEvent)}> + { + validateBookingWidget(e.target.value) + }, + }} + > + + {reward} + + + {redemptionErr && ( +
+ + + {intl.formatMessage({ id: redemptionErr.message })} + + {isMultiRoomError ? ( + // ToDo: Replace with Remove extra rooms JSX element after SW-963 is merged + {"Remove extra rooms"} + ) : null} +
+ )} +
+ ) +} diff --git a/apps/scandic-web/components/Forms/BookingWidget/FormContent/RewardNight/reward-night.module.css b/apps/scandic-web/components/Forms/BookingWidget/FormContent/RewardNight/reward-night.module.css new file mode 100644 index 000000000..150e7aa86 --- /dev/null +++ b/apps/scandic-web/components/Forms/BookingWidget/FormContent/RewardNight/reward-night.module.css @@ -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; +} diff --git a/apps/scandic-web/components/Forms/BookingWidget/FormContent/Voucher/index.tsx b/apps/scandic-web/components/Forms/BookingWidget/FormContent/Voucher/index.tsx index 7d37ab319..d7088e525 100644 --- a/apps/scandic-web/components/Forms/BookingWidget/FormContent/Voucher/index.tsx +++ b/apps/scandic-web/components/Forms/BookingWidget/FormContent/Voucher/index.tsx @@ -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() {
-
- - - - {bonus} - - - {/* Out of scope for this release */} - -
-
- - - - {reward} - - - {/* Out of scope for this release */} - -
+ {env.NEXT_PUBLIC_HIDE_FOR_NEXT_RELEASE ? ( + <> +
+ + + + {bonus} + + + {/* Out of scope for this release */} + +
+
+ + + + {reward} + + + {/* Out of scope for this release */} + +
+ + ) : ( +
+ +
+ )}
) @@ -87,16 +98,18 @@ export function VoucherSkeleton() {
+ {env.NEXT_PUBLIC_HIDE_FOR_NEXT_RELEASE ? null : ( +
+ + + {bonus} + + +
+ )}
- - - {bonus} - - -
-
- - + + {reward} diff --git a/apps/scandic-web/components/Forms/BookingWidget/index.tsx b/apps/scandic-web/components/Forms/BookingWidget/index.tsx index 69093a94d..40ca7578a 100644 --- a/apps/scandic-web/components/Forms/BookingWidget/index.tsx +++ b/apps/scandic-web/components/Forms/BookingWidget/index.tsx @@ -49,6 +49,8 @@ export default function Form({ ...(data.bookingCode?.value ? { bookingCode: data.bookingCode.value } : {}), + // Followed current url structure to keep searchType=redemption param incase of reward night + ...(data.redemption ? { searchType: "redemption" } : {}), }) onClose() diff --git a/apps/scandic-web/components/Forms/BookingWidget/schema.ts b/apps/scandic-web/components/Forms/BookingWidget/schema.ts index d3b267d29..716ee1c3f 100644 --- a/apps/scandic-web/components/Forms/BookingWidget/schema.ts +++ b/apps/scandic-web/components/Forms/BookingWidget/schema.ts @@ -91,7 +91,6 @@ export const bookingWidgetSchema = z redemption: z.boolean().default(false), rooms: guestRoomsSchema, search: z.string({ coerce: true }).min(1, "Required"), - voucher: z.boolean().default(false), hotel: z.number().optional(), city: z.string().optional(), }) @@ -130,4 +129,28 @@ export const bookingWidgetSchema = z path: ["rooms"], }) } + if (value.rooms.length > 1 && value.redemption) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "Multi-room booking is not available with reward night.", + path: ["redemption"], + }) + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "Multi-room booking is not available with reward night.", + path: ["rooms"], + }) + } + if (value.bookingCode?.value && value.redemption) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "Code and voucher is not available with reward night.", + path: ["redemption"], + }) + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "Code and voucher is not available with reward night.", + path: ["bookingCode.value"], + }) + } }) diff --git a/apps/scandic-web/components/GuestsRoomsPicker/Form.tsx b/apps/scandic-web/components/GuestsRoomsPicker/Form.tsx index 5659b8397..a9178d3d1 100644 --- a/apps/scandic-web/components/GuestsRoomsPicker/Form.tsx +++ b/apps/scandic-web/components/GuestsRoomsPicker/Form.tsx @@ -37,9 +37,8 @@ export default function GuestsRoomsPickerDialog({ const roomsValue = useWatch({ name: "rooms" }) const addRoomLabel = intl.formatMessage({ id: "Add room" }) const doneLabel = intl.formatMessage({ id: "Done" }) - - // Disable add room if booking code is voucher or reward night is enebaled - const addRoomDisabledText = getValues("redemption") + // Disable add room if booking code is either voucher or corporate cheque, or reward night is enabled + const addRoomDisabledTextForSpecialRate = getValues("redemption") ? intl.formatMessage({ id: "Multi-room booking is not available with reward night.", }) @@ -102,39 +101,52 @@ export default function GuestsRoomsPickerDialog({ /> ))} - {canAddRooms && ( + {addRoomDisabledTextForSpecialRate ? (
- - {addRoomDisabledText ? ( - - - {addRoomDisabledText} - - ) : null} + + {addRoomLabel} + +
+ ) : ( + canAddRooms && ( +
+ +
+ ) )}