feat: SW-963 Implemented error states and handling booking code and multiroom
This commit is contained in:
@@ -1,5 +1,14 @@
|
|||||||
.container {
|
.container {
|
||||||
position: relative;
|
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 {
|
.bookingCodeLabel {
|
||||||
@@ -9,6 +18,11 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.errorContainer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--Spacing-x-half);
|
||||||
|
}
|
||||||
.error {
|
.error {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: var(--Spacing-x-half);
|
gap: var(--Spacing-x-half);
|
||||||
@@ -26,8 +40,6 @@
|
|||||||
|
|
||||||
.bookingCodeRememberVisible {
|
.bookingCodeRememberVisible {
|
||||||
display: flex;
|
display: flex;
|
||||||
position: absolute;
|
|
||||||
top: calc(100% + 16px);
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,6 +48,10 @@
|
|||||||
margin-top: var(--Spacing-x2);
|
margin-top: var(--Spacing-x2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bookingCodeRememberVisible label {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 767px) {
|
@media screen and (max-width: 767px) {
|
||||||
.hideOnMobile {
|
.hideOnMobile {
|
||||||
display: none;
|
display: none;
|
||||||
@@ -43,6 +59,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 768px) {
|
@media screen and (min-width: 768px) {
|
||||||
|
.bookingCode {
|
||||||
|
height: auto;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
.bookingCodeRememberVisible {
|
.bookingCodeRememberVisible {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: var(--Base-Surface-Primary-light-Normal);
|
background: var(--Base-Surface-Primary-light-Normal);
|
||||||
@@ -54,7 +74,6 @@
|
|||||||
@media screen and (min-width: 768px) and (max-width: 1367px) {
|
@media screen and (min-width: 768px) and (max-width: 1367px) {
|
||||||
.container {
|
.container {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: var(--Spacing-x1);
|
|
||||||
}
|
}
|
||||||
.codePopover {
|
.codePopover {
|
||||||
background: var(--Base-Surface-Primary-light-Normal);
|
background: var(--Base-Surface-Primary-light-Normal);
|
||||||
@@ -67,16 +86,30 @@
|
|||||||
display: grid;
|
display: grid;
|
||||||
gap: var(--Spacing-x2);
|
gap: var(--Spacing-x2);
|
||||||
}
|
}
|
||||||
.bookingCodeRememberVisible {
|
.overlayTrigger {
|
||||||
position: static;
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
display: block;
|
||||||
|
left: 0;
|
||||||
|
right: 24px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 1367px) {
|
@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 {
|
.bookingCodeRememberVisible {
|
||||||
padding: var(--Spacing-x2);
|
padding: var(--Spacing-x2);
|
||||||
width: 320px;
|
position: absolute;
|
||||||
top: calc(100% + 24px);
|
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 { useCallback, useEffect, useRef, useState } from "react"
|
||||||
import { Dialog, DialogTrigger, Popover } from "react-aria-components"
|
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 { useIntl } from "react-intl"
|
||||||
import { useMediaQuery } from "usehooks-ts"
|
import { useMediaQuery } from "usehooks-ts"
|
||||||
|
|
||||||
@@ -21,6 +21,7 @@ import type {
|
|||||||
BookingCodeSchema,
|
BookingCodeSchema,
|
||||||
BookingWidgetSchema,
|
BookingWidgetSchema,
|
||||||
} from "@/types/components/bookingWidget"
|
} from "@/types/components/bookingWidget"
|
||||||
|
import type { ButtonProps } from "@/components/TempDesignSystem/Button/button"
|
||||||
|
|
||||||
export default function BookingCode() {
|
export default function BookingCode() {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
@@ -34,11 +35,9 @@ export default function BookingCode() {
|
|||||||
setValue,
|
setValue,
|
||||||
formState: { errors },
|
formState: { errors },
|
||||||
getValues,
|
getValues,
|
||||||
register,
|
|
||||||
} = useFormContext<BookingWidgetSchema>()
|
} = useFormContext<BookingWidgetSchema>()
|
||||||
|
|
||||||
const bookingCode: BookingCodeSchema = getValues("bookingCode")
|
const bookingCode: BookingCodeSchema = getValues("bookingCode")
|
||||||
const [isOpen, setIsOpen] = useState(!!bookingCode?.value)
|
|
||||||
const [showRemember, setShowRemember] = useState(false)
|
const [showRemember, setShowRemember] = useState(false)
|
||||||
const [showRememberMobile, setShowRememberMobile] = useState(false)
|
const [showRememberMobile, setShowRememberMobile] = useState(false)
|
||||||
const codeError = errors["bookingCode"]?.value
|
const codeError = errors["bookingCode"]?.value
|
||||||
@@ -50,24 +49,21 @@ export default function BookingCode() {
|
|||||||
setValue("bookingCode.value", value, { shouldValidate: true })
|
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(
|
const closeIfOutside = useCallback(
|
||||||
(target: HTMLElement) => {
|
(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)
|
setShowRemember(false)
|
||||||
|
if (codeError) {
|
||||||
|
setValue("bookingCode.value", "", { shouldValidate: true })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[setShowRemember, ref]
|
[setShowRemember, setValue, ref, codeError]
|
||||||
)
|
)
|
||||||
|
|
||||||
function showRememberCheck() {
|
function showRememberCheck() {
|
||||||
@@ -102,46 +98,10 @@ export default function BookingCode() {
|
|||||||
}, [closeIfOutside, showRemember])
|
}, [closeIfOutside, showRemember])
|
||||||
|
|
||||||
return isTablet ? (
|
return isTablet ? (
|
||||||
<div className={styles.container}>
|
<TabletBookingCode
|
||||||
<DialogTrigger isOpen={isOpen} onOpenChange={toggleModal}>
|
bookingCode={bookingCode}
|
||||||
<Button type="button" intent="text">
|
updateValue={updateBookingCodeFormValue}
|
||||||
<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>
|
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
className={styles.container}
|
className={styles.container}
|
||||||
@@ -149,32 +109,28 @@ export default function BookingCode() {
|
|||||||
onFocus={showRememberCheck}
|
onFocus={showRememberCheck}
|
||||||
onBlur={(e) => closeIfOutside(e.nativeEvent.relatedTarget as HTMLElement)}
|
onBlur={(e) => closeIfOutside(e.nativeEvent.relatedTarget as HTMLElement)}
|
||||||
>
|
>
|
||||||
<div className={styles.bookingCodeLabel}>
|
<div className={styles.bookingCode}>
|
||||||
<Caption
|
<div className={styles.bookingCodeLabel}>
|
||||||
color={showRemember ? "uiTextActive" : "red"}
|
<Caption
|
||||||
type="bold"
|
color={showRemember ? "uiTextActive" : "red"}
|
||||||
asChild
|
type="bold"
|
||||||
>
|
asChild
|
||||||
<span>{codeVoucher}</span>
|
>
|
||||||
</Caption>
|
<span>{codeVoucher}</span>
|
||||||
<CodeRulesModal />
|
</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>
|
</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 ? (
|
{isDesktop ? (
|
||||||
<div
|
<div
|
||||||
className={
|
className={
|
||||||
@@ -183,25 +139,35 @@ export default function BookingCode() {
|
|||||||
: styles.bookingCodeRemember
|
: styles.bookingCodeRemember
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<CodeRemember
|
{codeError?.message ? (
|
||||||
bookingCodeValue={bookingCode?.value}
|
<BookingCodeError codeError={codeError} />
|
||||||
onApplyClick={() => setShowRemember(false)}
|
) : (
|
||||||
/>
|
<CodeRemember
|
||||||
|
bookingCodeValue={bookingCode?.value}
|
||||||
|
onApplyClick={() => setShowRemember(false)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<>
|
||||||
className={
|
{codeError?.message ? (
|
||||||
showRememberMobile
|
<BookingCodeError codeError={codeError} />
|
||||||
? styles.bookingCodeRememberVisible
|
) : (
|
||||||
: styles.bookingCodeRemember
|
<div
|
||||||
}
|
className={
|
||||||
>
|
showRememberMobile
|
||||||
<Switch name="bookingCode.remember" className="mobile-switch">
|
? styles.bookingCodeRememberVisible
|
||||||
<Caption asChild>
|
: styles.bookingCodeRemember
|
||||||
<span>{intl.formatMessage({ id: "Remember code" })}</span>
|
}
|
||||||
</Caption>
|
>
|
||||||
</Switch>
|
<Switch name="bookingCode.remember" className="mobile-switch">
|
||||||
</div>
|
<Caption asChild>
|
||||||
|
<span>{intl.formatMessage({ id: "Remember code" })}</span>
|
||||||
|
</Caption>
|
||||||
|
</Switch>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</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);
|
margin-top: var(--Spacing-x2);
|
||||||
align-items: center;
|
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 {
|
.optionsContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -28,12 +22,6 @@
|
|||||||
height: 24px;
|
height: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 767px) {
|
|
||||||
.vouchers {
|
|
||||||
margin-bottom: var(--Spacing-x5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 768px) {
|
@media screen and (min-width: 768px) {
|
||||||
.options {
|
.options {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@@ -48,25 +36,9 @@
|
|||||||
grid-template-columns: auto auto;
|
grid-template-columns: auto auto;
|
||||||
column-gap: var(--Spacing-x2);
|
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) {
|
@media screen and (min-width: 1367px) {
|
||||||
.vouchers {
|
|
||||||
display: block;
|
|
||||||
max-width: 200px;
|
|
||||||
}
|
|
||||||
.options {
|
.options {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
max-width: 190px;
|
max-width: 190px;
|
||||||
|
|||||||
@@ -16,6 +16,11 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.buttonContainer {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--Spacing-x1);
|
||||||
|
}
|
||||||
|
|
||||||
.showOnTablet {
|
.showOnTablet {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@@ -33,7 +38,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.rooms,
|
.rooms,
|
||||||
.vouchers,
|
|
||||||
.when,
|
.when,
|
||||||
.where {
|
.where {
|
||||||
background-color: var(--Base-Background-Primary-Normal);
|
background-color: var(--Base-Background-Primary-Normal);
|
||||||
@@ -41,7 +45,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.rooms,
|
.rooms,
|
||||||
.vouchers,
|
|
||||||
.when {
|
.when {
|
||||||
padding: var(--Spacing-x1) var(--Spacing-x-one-and-half);
|
padding: var(--Spacing-x1) var(--Spacing-x-one-and-half);
|
||||||
}
|
}
|
||||||
@@ -103,6 +106,10 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 118px;
|
width: 118px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.showOnMobile {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 768px) and (max-width: 1366px) {
|
@media screen and (min-width: 768px) and (max-width: 1366px) {
|
||||||
@@ -143,3 +150,9 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 1366px) {
|
||||||
|
.input {
|
||||||
|
gap: var(--Spacing-x2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { useWatch } from "react-hook-form"
|
import { useFormContext, useWatch } from "react-hook-form"
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import { dt } from "@/lib/dt"
|
import { dt } from "@/lib/dt"
|
||||||
@@ -11,11 +11,13 @@ import SkeletonShimmer from "@/components/SkeletonShimmer"
|
|||||||
import Button from "@/components/TempDesignSystem/Button"
|
import Button from "@/components/TempDesignSystem/Button"
|
||||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||||
|
|
||||||
|
import { RemoveExtraRooms } from "./BookingCode"
|
||||||
import Search, { SearchSkeleton } from "./Search"
|
import Search, { SearchSkeleton } from "./Search"
|
||||||
import Voucher, { VoucherSkeleton } from "./Voucher"
|
import Voucher, { VoucherSkeleton } from "./Voucher"
|
||||||
|
|
||||||
import styles from "./formContent.module.css"
|
import styles from "./formContent.module.css"
|
||||||
|
|
||||||
|
import type { BookingWidgetSchema } from "@/types/components/bookingWidget"
|
||||||
import type { BookingWidgetFormContentProps } from "@/types/components/form/bookingwidget"
|
import type { BookingWidgetFormContentProps } from "@/types/components/form/bookingwidget"
|
||||||
|
|
||||||
export default function FormContent({
|
export default function FormContent({
|
||||||
@@ -25,6 +27,10 @@ export default function FormContent({
|
|||||||
isSearching,
|
isSearching,
|
||||||
}: BookingWidgetFormContentProps) {
|
}: BookingWidgetFormContentProps) {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
|
const {
|
||||||
|
formState: { errors },
|
||||||
|
} = useFormContext<BookingWidgetSchema>()
|
||||||
|
const bookingCodeError = errors["bookingCode"]?.value
|
||||||
const selectedDate = useWatch({ name: "date" })
|
const selectedDate = useWatch({ name: "date" })
|
||||||
|
|
||||||
const roomsLabel = intl.formatMessage({ id: "Rooms & Guests" })
|
const roomsLabel = intl.formatMessage({ id: "Rooms & Guests" })
|
||||||
@@ -79,6 +85,13 @@ export default function FormContent({
|
|||||||
<Voucher />
|
<Voucher />
|
||||||
</div>
|
</div>
|
||||||
<div className={`${styles.buttonContainer} ${styles.hideOnTablet}`}>
|
<div className={`${styles.buttonContainer} ${styles.hideOnTablet}`}>
|
||||||
|
{bookingCodeError?.message?.indexOf("Multi-room") === 0 ? (
|
||||||
|
<RemoveExtraRooms
|
||||||
|
size="medium"
|
||||||
|
fullWidth
|
||||||
|
className={styles.showOnMobile}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
<Button
|
<Button
|
||||||
className={styles.button}
|
className={styles.button}
|
||||||
form={formId}
|
form={formId}
|
||||||
|
|||||||
@@ -109,12 +109,24 @@ export const bookingWidgetSchema = z
|
|||||||
) {
|
) {
|
||||||
ctx.addIssue({
|
ctx.addIssue({
|
||||||
code: z.ZodIssueCode.custom,
|
code: z.ZodIssueCode.custom,
|
||||||
message: "Multiroom with voucher error",
|
message: "Multi-room booking is not available with this booking code.",
|
||||||
path: ["bookingCode.value"],
|
path: ["bookingCode.value"],
|
||||||
})
|
})
|
||||||
ctx.addIssue({
|
ctx.addIssue({
|
||||||
code: z.ZodIssueCode.custom,
|
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"],
|
path: ["rooms"],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,17 @@ import { useCallback, useEffect } from "react"
|
|||||||
import { useFormContext, useWatch } from "react-hook-form"
|
import { useFormContext, useWatch } from "react-hook-form"
|
||||||
import { useIntl } from "react-intl"
|
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 Button from "../TempDesignSystem/Button"
|
||||||
|
import Caption from "../TempDesignSystem/Text/Caption"
|
||||||
|
import { Tooltip } from "../TempDesignSystem/Tooltip"
|
||||||
import { GuestsRoom } from "./GuestsRoom"
|
import { GuestsRoom } from "./GuestsRoom"
|
||||||
|
|
||||||
import styles from "./guests-rooms-picker.module.css"
|
import styles from "./guests-rooms-picker.module.css"
|
||||||
@@ -25,12 +34,22 @@ export default function GuestsRoomsPickerDialog({
|
|||||||
onClose,
|
onClose,
|
||||||
}: GuestsRoomsPickerDialogProps) {
|
}: GuestsRoomsPickerDialogProps) {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const { getFieldState, trigger, setValue } =
|
const { getFieldState, trigger, setValue, getValues } =
|
||||||
useFormContext<BookingWidgetSchema>()
|
useFormContext<BookingWidgetSchema>()
|
||||||
const roomsValue = useWatch<BookingWidgetSchema, "rooms">({ name: "rooms" })
|
const roomsValue = useWatch<BookingWidgetSchema, "rooms">({ name: "rooms" })
|
||||||
const addRoomLabel = intl.formatMessage({ id: "Add room" })
|
const addRoomLabel = intl.formatMessage({ id: "Add room" })
|
||||||
const doneLabel = intl.formatMessage({ id: "Done" })
|
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 handleClose = useCallback(async () => {
|
||||||
const isValid = await trigger("rooms")
|
const isValid = await trigger("rooms")
|
||||||
if (isValid) onClose()
|
if (isValid) onClose()
|
||||||
@@ -94,28 +113,59 @@ export default function GuestsRoomsPickerDialog({
|
|||||||
theme="base"
|
theme="base"
|
||||||
fullWidth
|
fullWidth
|
||||||
onPress={handleAddRoom}
|
onPress={handleAddRoom}
|
||||||
|
disabled={addRoomDisabledText ? true : false}
|
||||||
>
|
>
|
||||||
<PlusIcon />
|
<PlusIcon />
|
||||||
{addRoomLabel}
|
{addRoomLabel}
|
||||||
</Button>
|
</Button>
|
||||||
|
{addRoomDisabledText ? (
|
||||||
|
<Caption
|
||||||
|
color="blue"
|
||||||
|
className={styles.addRoomMobileDisabledText}
|
||||||
|
>
|
||||||
|
<ErrorCircleIcon color="blue" width={20} />
|
||||||
|
{addRoomDisabledText}
|
||||||
|
</Caption>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<footer className={styles.footer}>
|
<footer className={styles.footer}>
|
||||||
{canAddRooms && (
|
{addRoomDisabledText ? (
|
||||||
<div className={styles.hideOnMobile}>
|
<div className={styles.hideOnMobile}>
|
||||||
<Button
|
<Tooltip
|
||||||
intent="text"
|
text={addRoomDisabledText}
|
||||||
variant="icon"
|
position={"bottom"}
|
||||||
wrapping
|
arrow="left"
|
||||||
theme="base"
|
|
||||||
onPress={handleAddRoom}
|
|
||||||
>
|
>
|
||||||
<PlusCircleIcon />
|
<Button
|
||||||
{addRoomLabel}
|
intent="text"
|
||||||
</Button>
|
variant="icon"
|
||||||
|
wrapping
|
||||||
|
theme="base"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
<PlusCircleIcon />
|
||||||
|
{addRoomLabel}
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
canAddRooms && (
|
||||||
|
<div className={styles.hideOnMobile}>
|
||||||
|
<Button
|
||||||
|
intent="text"
|
||||||
|
variant="icon"
|
||||||
|
wrapping
|
||||||
|
theme="base"
|
||||||
|
onPress={handleAddRoom}
|
||||||
|
>
|
||||||
|
<PlusCircleIcon />
|
||||||
|
{addRoomLabel}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
)}
|
)}
|
||||||
<Button
|
<Button
|
||||||
onPress={handleClose}
|
onPress={handleClose}
|
||||||
|
|||||||
@@ -117,9 +117,22 @@
|
|||||||
|
|
||||||
.addRoomMobileContainer {
|
.addRoomMobileContainer {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
padding-bottom: calc(var(--sticky-button-height) + 20px);
|
||||||
|
gap: var(--Spacing-x2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.addRoomMobileContainer button {
|
||||||
width: 150px;
|
width: 150px;
|
||||||
margin: 0 auto;
|
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 }) => (
|
{({ isSelected }) => (
|
||||||
<>
|
<>
|
||||||
<span className={styles.checkboxContainer}>
|
<span className={styles.checkboxContainer}>
|
||||||
<span className={styles.checkbox} tabIndex={0}>
|
<span
|
||||||
|
className={styles.checkbox}
|
||||||
|
tabIndex={registerOptions?.disabled ? undefined : 0}
|
||||||
|
>
|
||||||
{isSelected && <CheckIcon color="white" />}
|
{isSelected && <CheckIcon color="white" />}
|
||||||
</span>
|
</span>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -11,3 +11,7 @@
|
|||||||
gap: var(--Spacing-x-half);
|
gap: var(--Spacing-x-half);
|
||||||
margin: var(--Spacing-x1) 0 0;
|
margin: var(--Spacing-x1) 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.error svg {
|
||||||
|
min-width: 20px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -405,7 +405,8 @@
|
|||||||
"Month": "Måned",
|
"Month": "Måned",
|
||||||
"Mon–Fri: Always open": "Man–Fre: Altid åben",
|
"Mon–Fri: Always open": "Man–Fre: Altid åben",
|
||||||
"Mon–Fri: {openingTime}–{closingTime}": "Man–Fre: {openingTime}–{closingTime}",
|
"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",
|
"Museum": "Museum",
|
||||||
"Museums": "Museer",
|
"Museums": "Museer",
|
||||||
"My Add-on's": "Mine tilføjelser",
|
"My Add-on's": "Mine tilføjelser",
|
||||||
@@ -546,6 +547,7 @@
|
|||||||
"Remember code": "Husk kode",
|
"Remember code": "Husk kode",
|
||||||
"Remove": "Fjerne",
|
"Remove": "Fjerne",
|
||||||
"Remove card from member profile": "Fjern kortet fra medlemsprofilen",
|
"Remove card from member profile": "Fjern kortet fra medlemsprofilen",
|
||||||
|
"Remove extra rooms": "Fjern ekstra rum",
|
||||||
"Remove room": "Remove room",
|
"Remove room": "Remove room",
|
||||||
"Request bedtype": "Anmod om sengetype",
|
"Request bedtype": "Anmod om sengetype",
|
||||||
"Reservation policy": "Reservation policy",
|
"Reservation policy": "Reservation policy",
|
||||||
|
|||||||
@@ -406,7 +406,8 @@
|
|||||||
"Month": "Monat",
|
"Month": "Monat",
|
||||||
"Mon–Fri: Always open": "Mo–Fr: Immer geöffnet",
|
"Mon–Fri: Always open": "Mo–Fr: Immer geöffnet",
|
||||||
"Mon–Fri: {openingTime}–{closingTime}": "Mo–Fr: {openingTime}–{closingTime}",
|
"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",
|
"Museum": "Museum",
|
||||||
"Museums": "Museen",
|
"Museums": "Museen",
|
||||||
"My Add-on's": "Meine Add-ons",
|
"My Add-on's": "Meine Add-ons",
|
||||||
@@ -545,6 +546,7 @@
|
|||||||
"Remember code": "Code merken",
|
"Remember code": "Code merken",
|
||||||
"Remove": "Entfernen",
|
"Remove": "Entfernen",
|
||||||
"Remove card from member profile": "Karte aus dem Mitgliedsprofil entfernen",
|
"Remove card from member profile": "Karte aus dem Mitgliedsprofil entfernen",
|
||||||
|
"Remove extra rooms": "Zusätzliche Zimmer entfernen",
|
||||||
"Remove room": "Remove room",
|
"Remove room": "Remove room",
|
||||||
"Request bedtype": "Bettentyp anfragen",
|
"Request bedtype": "Bettentyp anfragen",
|
||||||
"Reservation policy": "Reservation policy",
|
"Reservation policy": "Reservation policy",
|
||||||
|
|||||||
@@ -404,7 +404,8 @@
|
|||||||
"Month": "Month",
|
"Month": "Month",
|
||||||
"Mon–Fri: Always open": "Mon–Fri: Always open",
|
"Mon–Fri: Always open": "Mon–Fri: Always open",
|
||||||
"Mon–Fri: {openingTime}–{closingTime}": "Mon–Fri: {openingTime}–{closingTime}",
|
"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",
|
"Museum": "Museum",
|
||||||
"Museums": "Museums",
|
"Museums": "Museums",
|
||||||
"My Add-on's": "My Add-on's",
|
"My Add-on's": "My Add-on's",
|
||||||
@@ -544,6 +545,7 @@
|
|||||||
"Remember code": "Remember code",
|
"Remember code": "Remember code",
|
||||||
"Remove": "Remove",
|
"Remove": "Remove",
|
||||||
"Remove card from member profile": "Remove card from member profile",
|
"Remove card from member profile": "Remove card from member profile",
|
||||||
|
"Remove extra rooms": "Remove extra rooms",
|
||||||
"Remove room": "Remove room",
|
"Remove room": "Remove room",
|
||||||
"Request bedtype": "Request bedtype",
|
"Request bedtype": "Request bedtype",
|
||||||
"Reservation policy": "Reservation policy",
|
"Reservation policy": "Reservation policy",
|
||||||
|
|||||||
@@ -405,7 +405,8 @@
|
|||||||
"Month": "Kuukausi",
|
"Month": "Kuukausi",
|
||||||
"Mon–Fri: Always open": "Ma–Pe: Aina auki",
|
"Mon–Fri: Always open": "Ma–Pe: Aina auki",
|
||||||
"Mon–Fri: {openingTime}–{closingTime}": "Ma–Pe: {openingTime}-{closingTime}",
|
"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",
|
"Museum": "Museo",
|
||||||
"Museums": "Museot",
|
"Museums": "Museot",
|
||||||
"My Add-on's": "Omat lisäosat",
|
"My Add-on's": "Omat lisäosat",
|
||||||
@@ -544,6 +545,7 @@
|
|||||||
"Remember code": "Muista koodi",
|
"Remember code": "Muista koodi",
|
||||||
"Remove": "Poistaa",
|
"Remove": "Poistaa",
|
||||||
"Remove card from member profile": "Poista kortti jäsenprofiilista",
|
"Remove card from member profile": "Poista kortti jäsenprofiilista",
|
||||||
|
"Remove extra rooms": "Poista ylimääräiset huoneet",
|
||||||
"Remove room": "Remove room",
|
"Remove room": "Remove room",
|
||||||
"Request bedtype": "Pyydä sänkytyyppiä",
|
"Request bedtype": "Pyydä sänkytyyppiä",
|
||||||
"Reservation policy": "Reservation policy",
|
"Reservation policy": "Reservation policy",
|
||||||
|
|||||||
@@ -404,7 +404,8 @@
|
|||||||
"Month": "Måned",
|
"Month": "Måned",
|
||||||
"Mon–Fri: Always open": "Man–Fre: Alltid åpen",
|
"Mon–Fri: Always open": "Man–Fre: Alltid åpen",
|
||||||
"Mon–Fri: {openingTime}–{closingTime}": "Man–Fre: {openingTime}–{closingTime}",
|
"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",
|
"Museum": "Museum",
|
||||||
"Museums": "Museums",
|
"Museums": "Museums",
|
||||||
"My Add-on's": "Mine tillegg",
|
"My Add-on's": "Mine tillegg",
|
||||||
@@ -543,6 +544,7 @@
|
|||||||
"Remember code": "Husk kode",
|
"Remember code": "Husk kode",
|
||||||
"Remove": "Fjerne",
|
"Remove": "Fjerne",
|
||||||
"Remove card from member profile": "Fjern kortet fra medlemsprofilen",
|
"Remove card from member profile": "Fjern kortet fra medlemsprofilen",
|
||||||
|
"Remove extra rooms": "Fjern ekstra rom",
|
||||||
"Remove room": "Remove room",
|
"Remove room": "Remove room",
|
||||||
"Request bedtype": "Be om sengetype",
|
"Request bedtype": "Be om sengetype",
|
||||||
"Reservation policy": "Reservation policy",
|
"Reservation policy": "Reservation policy",
|
||||||
|
|||||||
@@ -404,7 +404,8 @@
|
|||||||
"Month": "Månad",
|
"Month": "Månad",
|
||||||
"Mon–Fri: Always open": "Mån–Fre: Alltid öppet",
|
"Mon–Fri: Always open": "Mån–Fre: Alltid öppet",
|
||||||
"Mon–Fri: {openingTime}–{closingTime}": "Mån–Fre: {openingTime}–{closingTime}",
|
"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",
|
"Museum": "Museum",
|
||||||
"Museums": "Museer",
|
"Museums": "Museer",
|
||||||
"My Add-on's": "Mina tillägg",
|
"My Add-on's": "Mina tillägg",
|
||||||
@@ -543,6 +544,7 @@
|
|||||||
"Remember code": "Kom ihåg kod",
|
"Remember code": "Kom ihåg kod",
|
||||||
"Remove": "Ta bort",
|
"Remove": "Ta bort",
|
||||||
"Remove card from member profile": "Ta bort kortet från medlemsprofilen",
|
"Remove card from member profile": "Ta bort kortet från medlemsprofilen",
|
||||||
|
"Remove extra rooms": "Ta bort extra rum",
|
||||||
"Remove room": "Remove room",
|
"Remove room": "Remove room",
|
||||||
"Request bedtype": "Request bedtype",
|
"Request bedtype": "Request bedtype",
|
||||||
"Reservation policy": "Reservation policy",
|
"Reservation policy": "Reservation policy",
|
||||||
|
|||||||
Reference in New Issue
Block a user