Refactor booking widget skeleton
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import { env } from "@/env/server"
|
import { env } from "@/env/server"
|
||||||
|
|
||||||
import BookingWidgetSkeleton from "@/components/BookingWidget/BookingWidgetSkeleton"
|
import { BookingWidgetSkeleton } from "@/components/BookingWidget/Client"
|
||||||
|
|
||||||
export default function LoadingBookingWidget() {
|
export default function LoadingBookingWidget() {
|
||||||
if (env.HIDE_FOR_NEXT_RELEASE) {
|
if (env.HIDE_FOR_NEXT_RELEASE) {
|
||||||
|
|||||||
@@ -1,90 +0,0 @@
|
|||||||
"use client"
|
|
||||||
import { useIntl } from "react-intl"
|
|
||||||
|
|
||||||
import { SearchIcon } from "@/components/Icons"
|
|
||||||
|
|
||||||
import { SearchSkeleton } from "../Forms/BookingWidget/FormContent/Search"
|
|
||||||
import { VoucherSkeleton } from "../Forms/BookingWidget/FormContent/Voucher"
|
|
||||||
import { bookingWidgetVariants } from "../Forms/BookingWidget/variants"
|
|
||||||
import SkeletonShimmer from "../SkeletonShimmer"
|
|
||||||
import Button from "../TempDesignSystem/Button"
|
|
||||||
import Caption from "../TempDesignSystem/Text/Caption"
|
|
||||||
|
|
||||||
import formStyles from "../Forms/BookingWidget/form.module.css"
|
|
||||||
import formContentStyles from "../Forms/BookingWidget/FormContent/formContent.module.css"
|
|
||||||
import widgetStyles from "./bookingWidget.module.css"
|
|
||||||
|
|
||||||
export default function BookingWidgetSkeleton() {
|
|
||||||
const intl = useIntl()
|
|
||||||
|
|
||||||
const classNames = bookingWidgetVariants({
|
|
||||||
type: "full",
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
|
||||||
<section className={widgetStyles.containerDesktop}>
|
|
||||||
<section className={classNames}>
|
|
||||||
<form className={formStyles.form}>
|
|
||||||
<div className={formContentStyles.input}>
|
|
||||||
<div className={formContentStyles.inputContainer}>
|
|
||||||
<div className={formContentStyles.where}>
|
|
||||||
<SearchSkeleton />
|
|
||||||
</div>
|
|
||||||
<div className={formContentStyles.when}>
|
|
||||||
<Caption color="red" type="bold">
|
|
||||||
{intl.formatMessage(
|
|
||||||
{ id: "booking.nights" },
|
|
||||||
{ totalNights: 0 }
|
|
||||||
)}
|
|
||||||
</Caption>
|
|
||||||
<SkeletonShimmer />
|
|
||||||
</div>
|
|
||||||
<div className={formContentStyles.rooms}>
|
|
||||||
<Caption color="red" type="bold" asChild>
|
|
||||||
<span>{intl.formatMessage({ id: "Guests & Rooms" })}</span>
|
|
||||||
</Caption>
|
|
||||||
<SkeletonShimmer />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className={formContentStyles.voucherContainer}>
|
|
||||||
<VoucherSkeleton />
|
|
||||||
</div>
|
|
||||||
<div className={formContentStyles.buttonContainer}>
|
|
||||||
<Button
|
|
||||||
className={formContentStyles.button}
|
|
||||||
intent="primary"
|
|
||||||
theme="base"
|
|
||||||
type="submit"
|
|
||||||
disabled
|
|
||||||
>
|
|
||||||
<Caption
|
|
||||||
color="white"
|
|
||||||
type="bold"
|
|
||||||
className={formContentStyles.buttonText}
|
|
||||||
asChild
|
|
||||||
>
|
|
||||||
<span>{intl.formatMessage({ id: "Search" })}</span>
|
|
||||||
</Caption>
|
|
||||||
<span className={formContentStyles.icon}>
|
|
||||||
<SearchIcon color="white" width={28} height={28} />
|
|
||||||
</span>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</section>
|
|
||||||
{/* <section className={styles.containerMobile} data-open={isOpen}>
|
|
||||||
<button
|
|
||||||
className={styles.close}
|
|
||||||
onClick={closeMobileSearch}
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<CloseLargeIcon />
|
|
||||||
</button>
|
|
||||||
<Form locations={locations} type={type} />
|
|
||||||
</section>
|
|
||||||
<div className={styles.backdrop} onClick={closeMobileSearch} />
|
|
||||||
<MobileToggleButton openMobileSearch={openMobileSearch} /> */}
|
|
||||||
</section>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -6,14 +6,18 @@ import { FormProvider, useForm } from "react-hook-form"
|
|||||||
import { dt } from "@/lib/dt"
|
import { dt } from "@/lib/dt"
|
||||||
import { StickyElementNameEnum } from "@/stores/sticky-position"
|
import { StickyElementNameEnum } from "@/stores/sticky-position"
|
||||||
|
|
||||||
import Form from "@/components/Forms/BookingWidget"
|
import Form, {
|
||||||
|
BookingWidgetFormSkeleton,
|
||||||
|
} from "@/components/Forms/BookingWidget"
|
||||||
import { bookingWidgetSchema } from "@/components/Forms/BookingWidget/schema"
|
import { bookingWidgetSchema } from "@/components/Forms/BookingWidget/schema"
|
||||||
import { CloseLargeIcon } from "@/components/Icons"
|
import { CloseLargeIcon } from "@/components/Icons"
|
||||||
import useStickyPosition from "@/hooks/useStickyPosition"
|
import useStickyPosition from "@/hooks/useStickyPosition"
|
||||||
import { debounce } from "@/utils/debounce"
|
import { debounce } from "@/utils/debounce"
|
||||||
import { getFormattedUrlQueryParams } from "@/utils/url"
|
import { getFormattedUrlQueryParams } from "@/utils/url"
|
||||||
|
|
||||||
import MobileToggleButton from "./MobileToggleButton"
|
import MobileToggleButton, {
|
||||||
|
MobileToggleButtonSkeleton,
|
||||||
|
} from "./MobileToggleButton"
|
||||||
|
|
||||||
import styles from "./bookingWidget.module.css"
|
import styles from "./bookingWidget.module.css"
|
||||||
|
|
||||||
@@ -36,7 +40,6 @@ export default function BookingWidgetClient({
|
|||||||
name: StickyElementNameEnum.BOOKING_WIDGET,
|
name: StickyElementNameEnum.BOOKING_WIDGET,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
const bookingWidgetSearchData: BookingWidgetSearchParams | undefined =
|
const bookingWidgetSearchData: BookingWidgetSearchParams | undefined =
|
||||||
searchParams
|
searchParams
|
||||||
? (getFormattedUrlQueryParams(new URLSearchParams(searchParams), {
|
? (getFormattedUrlQueryParams(new URLSearchParams(searchParams), {
|
||||||
@@ -79,9 +82,7 @@ export default function BookingWidgetClient({
|
|||||||
const methods = useForm<BookingWidgetSchema>({
|
const methods = useForm<BookingWidgetSchema>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
search: selectedLocation?.name ?? "",
|
search: selectedLocation?.name ?? "",
|
||||||
location: selectedLocation
|
location: selectedLocation ? JSON.stringify(selectedLocation) : undefined,
|
||||||
? JSON.stringify(selectedLocation)
|
|
||||||
: undefined,
|
|
||||||
date: {
|
date: {
|
||||||
// UTC is required to handle requests from far away timezones https://scandichotels.atlassian.net/browse/SWAP-6375 & PET-507
|
// UTC is required to handle requests from far away timezones https://scandichotels.atlassian.net/browse/SWAP-6375 & PET-507
|
||||||
// This is specifically to handle timezones falling in different dates.
|
// This is specifically to handle timezones falling in different dates.
|
||||||
@@ -147,9 +148,11 @@ export default function BookingWidgetClient({
|
|||||||
? JSON.parse(sessionStorageSearchData)
|
? JSON.parse(sessionStorageSearchData)
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
!(selectedLocation?.name) && initialSelectedLocation?.name &&
|
!selectedLocation?.name &&
|
||||||
|
initialSelectedLocation?.name &&
|
||||||
methods.setValue("search", initialSelectedLocation.name)
|
methods.setValue("search", initialSelectedLocation.name)
|
||||||
!selectedLocation && sessionStorageSearchData &&
|
!selectedLocation &&
|
||||||
|
sessionStorageSearchData &&
|
||||||
methods.setValue("location", encodeURIComponent(sessionStorageSearchData))
|
methods.setValue("location", encodeURIComponent(sessionStorageSearchData))
|
||||||
}, [methods, selectedLocation])
|
}, [methods, selectedLocation])
|
||||||
|
|
||||||
@@ -173,3 +176,14 @@ export default function BookingWidgetClient({
|
|||||||
</FormProvider>
|
</FormProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function BookingWidgetSkeleton() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<section className={styles.containerDesktop}>
|
||||||
|
<BookingWidgetFormSkeleton />
|
||||||
|
</section>
|
||||||
|
<MobileToggleButtonSkeleton />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { dt } from "@/lib/dt"
|
|||||||
import { StickyElementNameEnum } from "@/stores/sticky-position"
|
import { StickyElementNameEnum } from "@/stores/sticky-position"
|
||||||
|
|
||||||
import { EditIcon, SearchIcon } from "@/components/Icons"
|
import { EditIcon, SearchIcon } from "@/components/Icons"
|
||||||
|
import SkeletonShimmer from "@/components/SkeletonShimmer"
|
||||||
import Divider from "@/components/TempDesignSystem/Divider"
|
import Divider from "@/components/TempDesignSystem/Divider"
|
||||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||||
@@ -24,7 +25,6 @@ import type { Location } from "@/types/trpc/routers/hotel/locations"
|
|||||||
export default function MobileToggleButton({
|
export default function MobileToggleButton({
|
||||||
openMobileSearch,
|
openMobileSearch,
|
||||||
}: BookingWidgetToggleButtonProps) {
|
}: BookingWidgetToggleButtonProps) {
|
||||||
const [hasMounted, setHasMounted] = useState(false)
|
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const lang = useLang()
|
const lang = useLang()
|
||||||
const d = useWatch({ name: "date" })
|
const d = useWatch({ name: "date" })
|
||||||
@@ -46,14 +46,6 @@ export default function MobileToggleButton({
|
|||||||
const selectedFromDate = dt(d.fromDate).locale(lang).format("D MMM")
|
const selectedFromDate = dt(d.fromDate).locale(lang).format("D MMM")
|
||||||
const selectedToDate = dt(d.toDate).locale(lang).format("D MMM")
|
const selectedToDate = dt(d.toDate).locale(lang).format("D MMM")
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setHasMounted(true)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
if (!hasMounted) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const locationAndDateIsSet = parsedLocation && d
|
const locationAndDateIsSet = parsedLocation && d
|
||||||
|
|
||||||
const totalRooms = rooms.length
|
const totalRooms = rooms.length
|
||||||
@@ -133,3 +125,33 @@ export default function MobileToggleButton({
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function MobileToggleButtonSkeleton() {
|
||||||
|
const intl = useIntl()
|
||||||
|
const bookingWidgetMobileRef = useRef(null)
|
||||||
|
useStickyPosition({
|
||||||
|
ref: bookingWidgetMobileRef,
|
||||||
|
name: StickyElementNameEnum.BOOKING_WIDGET_MOBILE,
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.partial} ref={bookingWidgetMobileRef}>
|
||||||
|
<div>
|
||||||
|
<Caption type="bold" color="red">
|
||||||
|
{intl.formatMessage({ id: "Where to" })}
|
||||||
|
</Caption>
|
||||||
|
<SkeletonShimmer height="24px" />
|
||||||
|
</div>
|
||||||
|
<Divider color="baseSurfaceSubtleNormal" variant="vertical" />
|
||||||
|
<div>
|
||||||
|
<Caption type="bold" color="red">
|
||||||
|
{intl.formatMessage({ id: "booking.nights" }, { totalNights: 0 })}
|
||||||
|
</Caption>
|
||||||
|
<SkeletonShimmer height="24px" />
|
||||||
|
</div>
|
||||||
|
<div className={styles.icon}>
|
||||||
|
<SearchIcon color="white" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { useState } from "react"
|
|
||||||
import { useWatch } from "react-hook-form"
|
import { useWatch } from "react-hook-form"
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
@@ -9,11 +8,12 @@ import DatePicker from "@/components/DatePicker"
|
|||||||
import GuestsRoomsPickerForm from "@/components/GuestsRoomsPicker"
|
import GuestsRoomsPickerForm from "@/components/GuestsRoomsPicker"
|
||||||
import GuestsRoomsProvider from "@/components/GuestsRoomsPicker/Provider/GuestsRoomsProvider"
|
import GuestsRoomsProvider from "@/components/GuestsRoomsPicker/Provider/GuestsRoomsProvider"
|
||||||
import { SearchIcon } from "@/components/Icons"
|
import { SearchIcon } from "@/components/Icons"
|
||||||
|
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 Search from "./Search"
|
import Search, { SearchSkeleton } from "./Search"
|
||||||
import Voucher from "./Voucher"
|
import Voucher, { VoucherSkeleton } from "./Voucher"
|
||||||
|
|
||||||
import styles from "./formContent.module.css"
|
import styles from "./formContent.module.css"
|
||||||
|
|
||||||
@@ -90,3 +90,53 @@ export default function FormContent({
|
|||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function BookingWidgetFormContentSkeleton() {
|
||||||
|
const intl = useIntl()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.input}>
|
||||||
|
<div className={styles.inputContainer}>
|
||||||
|
<div className={styles.where}>
|
||||||
|
<SearchSkeleton />
|
||||||
|
</div>
|
||||||
|
<div className={styles.when}>
|
||||||
|
<Caption color="red" type="bold">
|
||||||
|
{intl.formatMessage({ id: "booking.nights" }, { totalNights: 0 })}
|
||||||
|
</Caption>
|
||||||
|
<SkeletonShimmer />
|
||||||
|
</div>
|
||||||
|
<div className={styles.rooms}>
|
||||||
|
<Caption color="red" type="bold" asChild>
|
||||||
|
<span>{intl.formatMessage({ id: "Guests & Rooms" })}</span>
|
||||||
|
</Caption>
|
||||||
|
<SkeletonShimmer />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.voucherContainer}>
|
||||||
|
<VoucherSkeleton />
|
||||||
|
</div>
|
||||||
|
<div className={styles.buttonContainer}>
|
||||||
|
<Button
|
||||||
|
className={styles.button}
|
||||||
|
intent="primary"
|
||||||
|
theme="base"
|
||||||
|
type="submit"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
<Caption
|
||||||
|
color="white"
|
||||||
|
type="bold"
|
||||||
|
className={styles.buttonText}
|
||||||
|
asChild
|
||||||
|
>
|
||||||
|
<span>{intl.formatMessage({ id: "Search" })}</span>
|
||||||
|
</Caption>
|
||||||
|
<span className={styles.icon}>
|
||||||
|
<SearchIcon color="white" width={28} height={28} />
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { selectHotel, selectRate } from "@/constants/routes/hotelReservation"
|
|||||||
|
|
||||||
import useLang from "@/hooks/useLang"
|
import useLang from "@/hooks/useLang"
|
||||||
|
|
||||||
import FormContent from "./FormContent"
|
import FormContent, { BookingWidgetFormContentSkeleton } from "./FormContent"
|
||||||
import { bookingWidgetVariants } from "./variants"
|
import { bookingWidgetVariants } from "./variants"
|
||||||
|
|
||||||
import styles from "./form.module.css"
|
import styles from "./form.module.css"
|
||||||
@@ -69,3 +69,17 @@ export default function Form({ locations, type }: BookingWidgetFormProps) {
|
|||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function BookingWidgetFormSkeleton() {
|
||||||
|
const classNames = bookingWidgetVariants({
|
||||||
|
type: "full",
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className={classNames}>
|
||||||
|
<form className={styles.form}>
|
||||||
|
<BookingWidgetFormContentSkeleton />
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user