feat: SW-963 Implemented error states and handling booking code and multiroom
This commit is contained in:
@@ -1,5 +1,14 @@
|
||||
.container {
|
||||
position: relative;
|
||||
display: grid;
|
||||
gap: var(--Spacing-x1);
|
||||
}
|
||||
|
||||
.bookingCode {
|
||||
height: 60px;
|
||||
background-color: var(--Base-Background-Primary-Normal);
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
padding: var(--Spacing-x1) var(--Spacing-x-one-and-half);
|
||||
}
|
||||
|
||||
.bookingCodeLabel {
|
||||
@@ -9,6 +18,11 @@
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.errorContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x-half);
|
||||
}
|
||||
.error {
|
||||
display: flex;
|
||||
gap: var(--Spacing-x-half);
|
||||
@@ -26,8 +40,6 @@
|
||||
|
||||
.bookingCodeRememberVisible {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
top: calc(100% + 16px);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -36,6 +48,10 @@
|
||||
margin-top: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.bookingCodeRememberVisible label {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 767px) {
|
||||
.hideOnMobile {
|
||||
display: none;
|
||||
@@ -43,6 +59,10 @@
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.bookingCode {
|
||||
height: auto;
|
||||
background-color: transparent;
|
||||
}
|
||||
.bookingCodeRememberVisible {
|
||||
align-items: center;
|
||||
background: var(--Base-Surface-Primary-light-Normal);
|
||||
@@ -54,7 +74,6 @@
|
||||
@media screen and (min-width: 768px) and (max-width: 1367px) {
|
||||
.container {
|
||||
display: flex;
|
||||
gap: var(--Spacing-x1);
|
||||
}
|
||||
.codePopover {
|
||||
background: var(--Base-Surface-Primary-light-Normal);
|
||||
@@ -67,16 +86,30 @@
|
||||
display: grid;
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
.bookingCodeRememberVisible {
|
||||
position: static;
|
||||
.overlayTrigger {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
display: block;
|
||||
left: 0;
|
||||
right: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1367px) {
|
||||
.container:hover,
|
||||
.container:focus-within,
|
||||
.container:has([data-focused="true"], [data-pressed="true"]) {
|
||||
background-color: var(--Base-Surface-Primary-light-Hover-alt);
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
}
|
||||
|
||||
.bookingCodeRememberVisible {
|
||||
padding: var(--Spacing-x2);
|
||||
width: 320px;
|
||||
position: absolute;
|
||||
top: calc(100% + 24px);
|
||||
left: calc(0% - var(--Spacing-x-one-and-half));
|
||||
left: calc(0% - var(--Spacing-x-half));
|
||||
width: 360px;
|
||||
box-shadow: 0px 0px 14px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react"
|
||||
import { Dialog, DialogTrigger, Popover } from "react-aria-components"
|
||||
import { useFormContext } from "react-hook-form"
|
||||
import { type FieldError,useFormContext } from "react-hook-form"
|
||||
import { useIntl } from "react-intl"
|
||||
import { useMediaQuery } from "usehooks-ts"
|
||||
|
||||
@@ -21,6 +21,7 @@ import type {
|
||||
BookingCodeSchema,
|
||||
BookingWidgetSchema,
|
||||
} from "@/types/components/bookingWidget"
|
||||
import type { ButtonProps } from "@/components/TempDesignSystem/Button/button"
|
||||
|
||||
export default function BookingCode() {
|
||||
const intl = useIntl()
|
||||
@@ -34,11 +35,9 @@ export default function BookingCode() {
|
||||
setValue,
|
||||
formState: { errors },
|
||||
getValues,
|
||||
register,
|
||||
} = useFormContext<BookingWidgetSchema>()
|
||||
|
||||
const bookingCode: BookingCodeSchema = getValues("bookingCode")
|
||||
const [isOpen, setIsOpen] = useState(!!bookingCode?.value)
|
||||
const [showRemember, setShowRemember] = useState(false)
|
||||
const [showRememberMobile, setShowRememberMobile] = useState(false)
|
||||
const codeError = errors["bookingCode"]?.value
|
||||
@@ -50,24 +49,21 @@ export default function BookingCode() {
|
||||
setValue("bookingCode.value", value, { shouldValidate: true })
|
||||
}
|
||||
|
||||
function toggleModal(isOpen: boolean) {
|
||||
if (!isOpen && !bookingCode?.value) {
|
||||
setValue("bookingCode.flag", false)
|
||||
setIsOpen(isOpen)
|
||||
} else if (!codeError || isOpen) {
|
||||
setIsOpen(isOpen)
|
||||
if (isOpen || bookingCode?.value) {
|
||||
setValue("bookingCode.flag", true)
|
||||
}
|
||||
}
|
||||
}
|
||||
const closeIfOutside = useCallback(
|
||||
(target: HTMLElement) => {
|
||||
if (ref.current && target && !ref.current.contains(target)) {
|
||||
if (
|
||||
ref.current &&
|
||||
target &&
|
||||
!ref.current.contains(target) &&
|
||||
target.getAttribute("value") !== "Remove extra rooms"
|
||||
) {
|
||||
setShowRemember(false)
|
||||
if (codeError) {
|
||||
setValue("bookingCode.value", "", { shouldValidate: true })
|
||||
}
|
||||
}
|
||||
},
|
||||
[setShowRemember, ref]
|
||||
[setShowRemember, setValue, ref, codeError]
|
||||
)
|
||||
|
||||
function showRememberCheck() {
|
||||
@@ -102,46 +98,10 @@ export default function BookingCode() {
|
||||
}, [closeIfOutside, showRemember])
|
||||
|
||||
return isTablet ? (
|
||||
<div className={styles.container}>
|
||||
<DialogTrigger isOpen={isOpen} onOpenChange={toggleModal}>
|
||||
<Button type="button" intent="text">
|
||||
<Checkbox
|
||||
checked={!!bookingCode?.value}
|
||||
{...register("bookingCode.flag", {
|
||||
onChange: function () {
|
||||
if (bookingCode?.value || isOpen) {
|
||||
setValue("bookingCode.flag", true)
|
||||
}
|
||||
},
|
||||
})}
|
||||
>
|
||||
<Caption color="uiTextMediumContrast" asChild>
|
||||
<span>{codeVoucher}</span>
|
||||
</Caption>
|
||||
</Checkbox>
|
||||
</Button>
|
||||
<Popover
|
||||
className={styles.codePopover}
|
||||
placement="bottom start"
|
||||
offset={36}
|
||||
>
|
||||
<Dialog>
|
||||
{({ close }) => (
|
||||
<div className={styles.popover}>
|
||||
<TabletCodeInput updateValue={updateBookingCodeFormValue} />
|
||||
<div className={styles.bookingCodeRememberVisible}>
|
||||
<CodeRemember
|
||||
bookingCodeValue={bookingCode?.value}
|
||||
onApplyClick={close}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Dialog>
|
||||
</Popover>
|
||||
</DialogTrigger>
|
||||
<CodeRulesModal />
|
||||
</div>
|
||||
<TabletBookingCode
|
||||
bookingCode={bookingCode}
|
||||
updateValue={updateBookingCodeFormValue}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
className={styles.container}
|
||||
@@ -149,32 +109,28 @@ export default function BookingCode() {
|
||||
onFocus={showRememberCheck}
|
||||
onBlur={(e) => closeIfOutside(e.nativeEvent.relatedTarget as HTMLElement)}
|
||||
>
|
||||
<div className={styles.bookingCodeLabel}>
|
||||
<Caption
|
||||
color={showRemember ? "uiTextActive" : "red"}
|
||||
type="bold"
|
||||
asChild
|
||||
>
|
||||
<span>{codeVoucher}</span>
|
||||
</Caption>
|
||||
<CodeRulesModal />
|
||||
<div className={styles.bookingCode}>
|
||||
<div className={styles.bookingCodeLabel}>
|
||||
<Caption
|
||||
color={showRemember ? "uiTextActive" : "red"}
|
||||
type="bold"
|
||||
asChild
|
||||
>
|
||||
<span>{codeVoucher}</span>
|
||||
</Caption>
|
||||
<CodeRulesModal />
|
||||
</div>
|
||||
<Input
|
||||
className="input"
|
||||
type="search"
|
||||
placeholder={addCode}
|
||||
name="bookingCode.value"
|
||||
id="booking-code"
|
||||
onChange={(event) => updateBookingCodeFormValue(event.target.value)}
|
||||
autoComplete="off"
|
||||
value={bookingCode?.value}
|
||||
/>
|
||||
</div>
|
||||
<Input
|
||||
className="input"
|
||||
type="search"
|
||||
placeholder={addCode}
|
||||
name="bookingCode.value"
|
||||
id="booking-code"
|
||||
onChange={(event) => updateBookingCodeFormValue(event.target.value)}
|
||||
defaultValue={bookingCode?.value}
|
||||
autoComplete="off"
|
||||
/>
|
||||
{codeError?.message ? (
|
||||
<Caption color="red" className={styles.error}>
|
||||
<ErrorCircleIcon color="red" className={styles.errorIcon} />
|
||||
{intl.formatMessage({ id: codeError.message })}
|
||||
</Caption>
|
||||
) : null}
|
||||
{isDesktop ? (
|
||||
<div
|
||||
className={
|
||||
@@ -183,25 +139,35 @@ export default function BookingCode() {
|
||||
: styles.bookingCodeRemember
|
||||
}
|
||||
>
|
||||
<CodeRemember
|
||||
bookingCodeValue={bookingCode?.value}
|
||||
onApplyClick={() => setShowRemember(false)}
|
||||
/>
|
||||
{codeError?.message ? (
|
||||
<BookingCodeError codeError={codeError} />
|
||||
) : (
|
||||
<CodeRemember
|
||||
bookingCodeValue={bookingCode?.value}
|
||||
onApplyClick={() => setShowRemember(false)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className={
|
||||
showRememberMobile
|
||||
? styles.bookingCodeRememberVisible
|
||||
: styles.bookingCodeRemember
|
||||
}
|
||||
>
|
||||
<Switch name="bookingCode.remember" className="mobile-switch">
|
||||
<Caption asChild>
|
||||
<span>{intl.formatMessage({ id: "Remember code" })}</span>
|
||||
</Caption>
|
||||
</Switch>
|
||||
</div>
|
||||
<>
|
||||
{codeError?.message ? (
|
||||
<BookingCodeError codeError={codeError} />
|
||||
) : (
|
||||
<div
|
||||
className={
|
||||
showRememberMobile
|
||||
? styles.bookingCodeRememberVisible
|
||||
: styles.bookingCodeRemember
|
||||
}
|
||||
>
|
||||
<Switch name="bookingCode.remember" className="mobile-switch">
|
||||
<Caption asChild>
|
||||
<span>{intl.formatMessage({ id: "Remember code" })}</span>
|
||||
</Caption>
|
||||
</Switch>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
@@ -259,3 +225,128 @@ function CodeRemember({ bookingCodeValue, onApplyClick }: CodeRememberProps) {
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function BookingCodeError({ codeError }: { codeError: FieldError }) {
|
||||
const intl = useIntl()
|
||||
const isMultiroomErr = codeError.message?.indexOf("Multi-room") !== -1
|
||||
|
||||
return (
|
||||
<div className={styles.errorContainer}>
|
||||
<Caption color={isMultiroomErr ? "blue" : "red"} className={styles.error}>
|
||||
<ErrorCircleIcon
|
||||
color={isMultiroomErr ? "blue" : "red"}
|
||||
className={styles.errorIcon}
|
||||
/>
|
||||
{intl.formatMessage({ id: codeError.message })}
|
||||
</Caption>
|
||||
{isMultiroomErr ? (
|
||||
<RemoveExtraRooms className={styles.hideOnMobile} />
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function RemoveExtraRooms({ ...props }: ButtonProps) {
|
||||
const intl = useIntl()
|
||||
const { getValues, setValue, trigger } = useFormContext<BookingWidgetSchema>()
|
||||
function removeExtraRooms() {
|
||||
// Timeout to delay the event scheduling issue with touch events on mobile
|
||||
window.setTimeout(() => {
|
||||
const rooms = getValues("rooms")[0]
|
||||
setValue("rooms", [rooms], { shouldValidate: true })
|
||||
trigger("bookingCode.value")
|
||||
}, 300)
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
value="Remove extra rooms"
|
||||
type="button"
|
||||
onClick={removeExtraRooms}
|
||||
size="small"
|
||||
intent="secondary"
|
||||
{...props}
|
||||
>
|
||||
{intl.formatMessage({ id: "Remove extra rooms" })}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
function TabletBookingCode({
|
||||
bookingCode,
|
||||
updateValue,
|
||||
}: {
|
||||
bookingCode: BookingCodeSchema
|
||||
updateValue: (value: string) => void
|
||||
}) {
|
||||
const intl = useIntl()
|
||||
const [isOpen, setIsOpen] = useState(!!bookingCode?.value)
|
||||
const {
|
||||
setValue,
|
||||
register,
|
||||
formState: { errors },
|
||||
} = useFormContext<BookingWidgetSchema>()
|
||||
const codeError = errors["bookingCode"]?.value
|
||||
const codeVoucher = intl.formatMessage({ id: "Code / Voucher" })
|
||||
|
||||
function toggleModal(isOpen: boolean) {
|
||||
if (!isOpen && !bookingCode?.value) {
|
||||
setValue("bookingCode.flag", false)
|
||||
setIsOpen(isOpen)
|
||||
} else if (!codeError || isOpen) {
|
||||
setIsOpen(isOpen)
|
||||
if (isOpen || bookingCode?.value) {
|
||||
setValue("bookingCode.flag", true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<DialogTrigger isOpen={isOpen} onOpenChange={toggleModal}>
|
||||
<Button type="button" intent="text">
|
||||
{/* For some reason Checkbox click triggers twice modal state change, which returns the modal back to old state. So we are using overlay as trigger for modal */}
|
||||
<div className={styles.overlayTrigger}></div>
|
||||
<Checkbox
|
||||
checked={!!bookingCode?.value}
|
||||
{...register("bookingCode.flag", {
|
||||
onChange: function () {
|
||||
if (bookingCode?.value || isOpen) {
|
||||
setValue("bookingCode.flag", true)
|
||||
}
|
||||
},
|
||||
})}
|
||||
>
|
||||
<Caption color="uiTextMediumContrast" type="bold" asChild>
|
||||
<span>{codeVoucher}</span>
|
||||
</Caption>
|
||||
</Checkbox>
|
||||
</Button>
|
||||
<Popover
|
||||
className={styles.codePopover}
|
||||
placement="bottom start"
|
||||
offset={36}
|
||||
>
|
||||
<Dialog>
|
||||
{({ close }) => (
|
||||
<div className={styles.popover}>
|
||||
<TabletCodeInput updateValue={updateValue} />
|
||||
<div className={styles.bookingCodeRememberVisible}>
|
||||
{codeError?.message ? (
|
||||
<RemoveExtraRooms />
|
||||
) : (
|
||||
<CodeRemember
|
||||
bookingCodeValue={bookingCode?.value}
|
||||
onApplyClick={close}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Dialog>
|
||||
</Popover>
|
||||
</DialogTrigger>
|
||||
<CodeRulesModal />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -11,12 +11,6 @@
|
||||
margin-top: var(--Spacing-x2);
|
||||
align-items: center;
|
||||
}
|
||||
.vouchers {
|
||||
width: 100%;
|
||||
display: block;
|
||||
padding: var(--Spacing-x1) var(--Spacing-x-one-and-half);
|
||||
border-radius: var(--Corner-radius-Small);
|
||||
}
|
||||
|
||||
.optionsContainer {
|
||||
display: flex;
|
||||
@@ -28,12 +22,6 @@
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 767px) {
|
||||
.vouchers {
|
||||
margin-bottom: var(--Spacing-x5);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.options {
|
||||
flex-direction: row;
|
||||
@@ -48,25 +36,9 @@
|
||||
grid-template-columns: auto auto;
|
||||
column-gap: var(--Spacing-x2);
|
||||
}
|
||||
.vouchers:hover,
|
||||
.vouchers:focus-within,
|
||||
.vouchers:has([data-focused="true"], [data-pressed="true"]) {
|
||||
background-color: var(--Base-Surface-Primary-light-Hover-alt);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1366px) {
|
||||
.vouchers {
|
||||
background-color: var(--Base-Background-Primary-Normal);
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1367px) {
|
||||
.vouchers {
|
||||
display: block;
|
||||
max-width: 200px;
|
||||
}
|
||||
.options {
|
||||
flex-direction: column;
|
||||
max-width: 190px;
|
||||
|
||||
@@ -16,6 +16,11 @@
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.buttonContainer {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x1);
|
||||
}
|
||||
|
||||
.showOnTablet {
|
||||
display: none;
|
||||
}
|
||||
@@ -33,7 +38,6 @@
|
||||
}
|
||||
|
||||
.rooms,
|
||||
.vouchers,
|
||||
.when,
|
||||
.where {
|
||||
background-color: var(--Base-Background-Primary-Normal);
|
||||
@@ -41,7 +45,6 @@
|
||||
}
|
||||
|
||||
.rooms,
|
||||
.vouchers,
|
||||
.when {
|
||||
padding: var(--Spacing-x1) var(--Spacing-x-one-and-half);
|
||||
}
|
||||
@@ -103,6 +106,10 @@
|
||||
justify-content: center;
|
||||
width: 118px;
|
||||
}
|
||||
|
||||
.showOnMobile {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) and (max-width: 1366px) {
|
||||
@@ -143,3 +150,9 @@
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1366px) {
|
||||
.input {
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"use client"
|
||||
import { useWatch } from "react-hook-form"
|
||||
import { useFormContext, useWatch } from "react-hook-form"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { dt } from "@/lib/dt"
|
||||
@@ -11,11 +11,13 @@ import SkeletonShimmer from "@/components/SkeletonShimmer"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
|
||||
import { RemoveExtraRooms } from "./BookingCode"
|
||||
import Search, { SearchSkeleton } from "./Search"
|
||||
import Voucher, { VoucherSkeleton } from "./Voucher"
|
||||
|
||||
import styles from "./formContent.module.css"
|
||||
|
||||
import type { BookingWidgetSchema } from "@/types/components/bookingWidget"
|
||||
import type { BookingWidgetFormContentProps } from "@/types/components/form/bookingwidget"
|
||||
|
||||
export default function FormContent({
|
||||
@@ -25,6 +27,10 @@ export default function FormContent({
|
||||
isSearching,
|
||||
}: BookingWidgetFormContentProps) {
|
||||
const intl = useIntl()
|
||||
const {
|
||||
formState: { errors },
|
||||
} = useFormContext<BookingWidgetSchema>()
|
||||
const bookingCodeError = errors["bookingCode"]?.value
|
||||
const selectedDate = useWatch({ name: "date" })
|
||||
|
||||
const roomsLabel = intl.formatMessage({ id: "Rooms & Guests" })
|
||||
@@ -79,6 +85,13 @@ export default function FormContent({
|
||||
<Voucher />
|
||||
</div>
|
||||
<div className={`${styles.buttonContainer} ${styles.hideOnTablet}`}>
|
||||
{bookingCodeError?.message?.indexOf("Multi-room") === 0 ? (
|
||||
<RemoveExtraRooms
|
||||
size="medium"
|
||||
fullWidth
|
||||
className={styles.showOnMobile}
|
||||
/>
|
||||
) : null}
|
||||
<Button
|
||||
className={styles.button}
|
||||
form={formId}
|
||||
|
||||
@@ -109,12 +109,24 @@ export const bookingWidgetSchema = z
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "Multiroom with voucher error",
|
||||
message: "Multi-room booking is not available with this booking code.",
|
||||
path: ["bookingCode.value"],
|
||||
})
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "Multiroom with voucher error",
|
||||
message: "Multi-room booking is not available with this booking code.",
|
||||
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: ["bookingCode.value"],
|
||||
})
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "Multi-room booking is not available with reward night.",
|
||||
path: ["rooms"],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,8 +4,17 @@ import { useCallback, useEffect } from "react"
|
||||
import { useFormContext, useWatch } from "react-hook-form"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { CloseLargeIcon, PlusCircleIcon, PlusIcon } from "../Icons"
|
||||
import { env } from "@/env/client"
|
||||
|
||||
import {
|
||||
CloseLargeIcon,
|
||||
ErrorCircleIcon,
|
||||
PlusCircleIcon,
|
||||
PlusIcon,
|
||||
} from "../Icons"
|
||||
import Button from "../TempDesignSystem/Button"
|
||||
import Caption from "../TempDesignSystem/Text/Caption"
|
||||
import { Tooltip } from "../TempDesignSystem/Tooltip"
|
||||
import { GuestsRoom } from "./GuestsRoom"
|
||||
|
||||
import styles from "./guests-rooms-picker.module.css"
|
||||
@@ -25,12 +34,22 @@ export default function GuestsRoomsPickerDialog({
|
||||
onClose,
|
||||
}: GuestsRoomsPickerDialogProps) {
|
||||
const intl = useIntl()
|
||||
const { getFieldState, trigger, setValue } =
|
||||
const { getFieldState, trigger, setValue, getValues } =
|
||||
useFormContext<BookingWidgetSchema>()
|
||||
const roomsValue = useWatch<BookingWidgetSchema, "rooms">({ 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")
|
||||
? intl.formatMessage({
|
||||
id: "Multi-room booking is not available with reward night.",
|
||||
})
|
||||
: getValues("bookingCode.value")?.toLowerCase().startsWith("vo") &&
|
||||
intl.formatMessage({
|
||||
id: "Multi-room booking is not available with this booking code.",
|
||||
})
|
||||
|
||||
const handleClose = useCallback(async () => {
|
||||
const isValid = await trigger("rooms")
|
||||
if (isValid) onClose()
|
||||
@@ -94,28 +113,59 @@ export default function GuestsRoomsPickerDialog({
|
||||
theme="base"
|
||||
fullWidth
|
||||
onPress={handleAddRoom}
|
||||
disabled={addRoomDisabledText ? true : false}
|
||||
>
|
||||
<PlusIcon />
|
||||
{addRoomLabel}
|
||||
</Button>
|
||||
{addRoomDisabledText ? (
|
||||
<Caption
|
||||
color="blue"
|
||||
className={styles.addRoomMobileDisabledText}
|
||||
>
|
||||
<ErrorCircleIcon color="blue" width={20} />
|
||||
{addRoomDisabledText}
|
||||
</Caption>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
<footer className={styles.footer}>
|
||||
{canAddRooms && (
|
||||
{addRoomDisabledText ? (
|
||||
<div className={styles.hideOnMobile}>
|
||||
<Button
|
||||
intent="text"
|
||||
variant="icon"
|
||||
wrapping
|
||||
theme="base"
|
||||
onPress={handleAddRoom}
|
||||
<Tooltip
|
||||
text={addRoomDisabledText}
|
||||
position={"bottom"}
|
||||
arrow="left"
|
||||
>
|
||||
<PlusCircleIcon />
|
||||
{addRoomLabel}
|
||||
</Button>
|
||||
<Button
|
||||
intent="text"
|
||||
variant="icon"
|
||||
wrapping
|
||||
theme="base"
|
||||
disabled
|
||||
>
|
||||
<PlusCircleIcon />
|
||||
{addRoomLabel}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
) : (
|
||||
canAddRooms && (
|
||||
<div className={styles.hideOnMobile}>
|
||||
<Button
|
||||
intent="text"
|
||||
variant="icon"
|
||||
wrapping
|
||||
theme="base"
|
||||
onPress={handleAddRoom}
|
||||
>
|
||||
<PlusCircleIcon />
|
||||
{addRoomLabel}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
<Button
|
||||
onPress={handleClose}
|
||||
|
||||
@@ -117,9 +117,22 @@
|
||||
|
||||
.addRoomMobileContainer {
|
||||
display: grid;
|
||||
padding-bottom: calc(var(--sticky-button-height) + 20px);
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.addRoomMobileContainer button {
|
||||
width: 150px;
|
||||
margin: 0 auto;
|
||||
padding-bottom: calc(var(--sticky-button-height) + 20px);
|
||||
}
|
||||
|
||||
.addRoomMobileContainer .addRoomMobileDisabledText {
|
||||
padding: var(--Spacing-x1) var(--Spacing-x2);
|
||||
background-color: var(--Base-Background-Primary-Normal);
|
||||
margin: 0 var(--Spacing-x2);
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
display: flex;
|
||||
gap: var(--Spacing-x1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,10 @@ export default function Checkbox({
|
||||
{({ isSelected }) => (
|
||||
<>
|
||||
<span className={styles.checkboxContainer}>
|
||||
<span className={styles.checkbox} tabIndex={0}>
|
||||
<span
|
||||
className={styles.checkbox}
|
||||
tabIndex={registerOptions?.disabled ? undefined : 0}
|
||||
>
|
||||
{isSelected && <CheckIcon color="white" />}
|
||||
</span>
|
||||
{children}
|
||||
|
||||
@@ -11,3 +11,7 @@
|
||||
gap: var(--Spacing-x-half);
|
||||
margin: var(--Spacing-x1) 0 0;
|
||||
}
|
||||
|
||||
.error svg {
|
||||
min-width: 20px;
|
||||
}
|
||||
|
||||
@@ -405,7 +405,8 @@
|
||||
"Month": "Måned",
|
||||
"Mon–Fri: Always open": "Man–Fre: Altid åben",
|
||||
"Mon–Fri: {openingTime}–{closingTime}": "Man–Fre: {openingTime}–{closingTime}",
|
||||
"Multiroom with voucher error": "Det er ikke muligt at booke flere værelser med gavekort.",
|
||||
"Multi-room booking is not available with reward night.": "Multi-værelse booking er ikke tilgængelig med belønning nat.",
|
||||
"Multi-room booking is not available with this booking code.": "Multi-værelse booking er ikke tilgængelig med denne reservationskode.",
|
||||
"Museum": "Museum",
|
||||
"Museums": "Museer",
|
||||
"My Add-on's": "Mine tilføjelser",
|
||||
@@ -546,6 +547,7 @@
|
||||
"Remember code": "Husk kode",
|
||||
"Remove": "Fjerne",
|
||||
"Remove card from member profile": "Fjern kortet fra medlemsprofilen",
|
||||
"Remove extra rooms": "Fjern ekstra rum",
|
||||
"Remove room": "Remove room",
|
||||
"Request bedtype": "Anmod om sengetype",
|
||||
"Reservation policy": "Reservation policy",
|
||||
|
||||
@@ -406,7 +406,8 @@
|
||||
"Month": "Monat",
|
||||
"Mon–Fri: Always open": "Mo–Fr: Immer geöffnet",
|
||||
"Mon–Fri: {openingTime}–{closingTime}": "Mo–Fr: {openingTime}–{closingTime}",
|
||||
"Multiroom with voucher error": "Die Buchung mehrerer Zimmer ist nicht mit einem Gutschein möglich",
|
||||
"Multi-room booking is not available with reward night.": "Mehrzimmerbuchungen sind mit Prämiennächten nicht möglich.",
|
||||
"Multi-room booking is not available with this booking code.": "Mehrzimmerbuchungen sind mit diesem Buchungscode nicht möglich.",
|
||||
"Museum": "Museum",
|
||||
"Museums": "Museen",
|
||||
"My Add-on's": "Meine Add-ons",
|
||||
@@ -545,6 +546,7 @@
|
||||
"Remember code": "Code merken",
|
||||
"Remove": "Entfernen",
|
||||
"Remove card from member profile": "Karte aus dem Mitgliedsprofil entfernen",
|
||||
"Remove extra rooms": "Zusätzliche Zimmer entfernen",
|
||||
"Remove room": "Remove room",
|
||||
"Request bedtype": "Bettentyp anfragen",
|
||||
"Reservation policy": "Reservation policy",
|
||||
|
||||
@@ -404,7 +404,8 @@
|
||||
"Month": "Month",
|
||||
"Mon–Fri: Always open": "Mon–Fri: Always open",
|
||||
"Mon–Fri: {openingTime}–{closingTime}": "Mon–Fri: {openingTime}–{closingTime}",
|
||||
"Multiroom with voucher error": "Multiroom booking is not compatible with a code or voucher",
|
||||
"Multi-room booking is not available with reward night.": "Multi-room booking is not available with reward night.",
|
||||
"Multi-room booking is not available with this booking code.": "Multi-room booking is not available with this booking code.",
|
||||
"Museum": "Museum",
|
||||
"Museums": "Museums",
|
||||
"My Add-on's": "My Add-on's",
|
||||
@@ -544,6 +545,7 @@
|
||||
"Remember code": "Remember code",
|
||||
"Remove": "Remove",
|
||||
"Remove card from member profile": "Remove card from member profile",
|
||||
"Remove extra rooms": "Remove extra rooms",
|
||||
"Remove room": "Remove room",
|
||||
"Request bedtype": "Request bedtype",
|
||||
"Reservation policy": "Reservation policy",
|
||||
|
||||
@@ -405,7 +405,8 @@
|
||||
"Month": "Kuukausi",
|
||||
"Mon–Fri: Always open": "Ma–Pe: Aina auki",
|
||||
"Mon–Fri: {openingTime}–{closingTime}": "Ma–Pe: {openingTime}-{closingTime}",
|
||||
"Multiroom with voucher error": "Lahjakortilla ei voi varata monta huonetta",
|
||||
"Multi-room booking is not available with reward night.": "Usean huoneen varaus ei ole saatavilla palkintoyönä.",
|
||||
"Multi-room booking is not available with this booking code.": "Usean huoneen varaus ei ole käytettävissä tällä varauskoodilla.",
|
||||
"Museum": "Museo",
|
||||
"Museums": "Museot",
|
||||
"My Add-on's": "Omat lisäosat",
|
||||
@@ -544,6 +545,7 @@
|
||||
"Remember code": "Muista koodi",
|
||||
"Remove": "Poistaa",
|
||||
"Remove card from member profile": "Poista kortti jäsenprofiilista",
|
||||
"Remove extra rooms": "Poista ylimääräiset huoneet",
|
||||
"Remove room": "Remove room",
|
||||
"Request bedtype": "Pyydä sänkytyyppiä",
|
||||
"Reservation policy": "Reservation policy",
|
||||
|
||||
@@ -404,7 +404,8 @@
|
||||
"Month": "Måned",
|
||||
"Mon–Fri: Always open": "Man–Fre: Alltid åpen",
|
||||
"Mon–Fri: {openingTime}–{closingTime}": "Man–Fre: {openingTime}–{closingTime}",
|
||||
"Multiroom with voucher error": "Bestilling av flere rom er ikke mulig når du bestiller med voucher",
|
||||
"Multi-room booking is not available with reward night.": "Multi-rom booking er ikke tilgjengelig med belønning natt.",
|
||||
"Multi-room booking is not available with this booking code.": "Bestilling av flere rom er ikke tilgjengelig med denne bestillingskoden.",
|
||||
"Museum": "Museum",
|
||||
"Museums": "Museums",
|
||||
"My Add-on's": "Mine tillegg",
|
||||
@@ -543,6 +544,7 @@
|
||||
"Remember code": "Husk kode",
|
||||
"Remove": "Fjerne",
|
||||
"Remove card from member profile": "Fjern kortet fra medlemsprofilen",
|
||||
"Remove extra rooms": "Fjern ekstra rom",
|
||||
"Remove room": "Remove room",
|
||||
"Request bedtype": "Be om sengetype",
|
||||
"Reservation policy": "Reservation policy",
|
||||
|
||||
@@ -404,7 +404,8 @@
|
||||
"Month": "Månad",
|
||||
"Mon–Fri: Always open": "Mån–Fre: Alltid öppet",
|
||||
"Mon–Fri: {openingTime}–{closingTime}": "Mån–Fre: {openingTime}–{closingTime}",
|
||||
"Multiroom with voucher error": "Flera rum kan inte bokas samtidigt med presentkort",
|
||||
"Multi-room booking is not available with reward night.": "Flerrumsbokning är inte tillgänglig med belöningsnatt.",
|
||||
"Multi-room booking is not available with this booking code.": "Flerrumsbokning är inte tillgänglig med denna bokningskod.",
|
||||
"Museum": "Museum",
|
||||
"Museums": "Museer",
|
||||
"My Add-on's": "Mina tillägg",
|
||||
@@ -543,6 +544,7 @@
|
||||
"Remember code": "Kom ihåg kod",
|
||||
"Remove": "Ta bort",
|
||||
"Remove card from member profile": "Ta bort kortet från medlemsprofilen",
|
||||
"Remove extra rooms": "Ta bort extra rum",
|
||||
"Remove room": "Remove room",
|
||||
"Request bedtype": "Request bedtype",
|
||||
"Reservation policy": "Reservation policy",
|
||||
|
||||
Reference in New Issue
Block a user