Files
web/apps/scandic-web/components/Forms/BookingWidget/FormContent/index.tsx
Anton Gunnarsson 4e1cb01b84 Merged in chore/cleanup-after-trpc-migration (pull request #2457)
Chore/cleanup after trpc migration

* Clean up TODOs

* Rename REDEMPTION constant to SEARCH_TYPE_REDEMPTION

* Update dependencies

Remove unused deps from scandic-web
Add missing deps to trpc package

* Update self-referencing imports

* Remove unused variables from scandic-web env

* Fix missing graphql-tag package

* Actually fix

* Remove unused env var


Approved-by: Christian Andolf
Approved-by: Linus Flood
2025-06-30 12:08:19 +00:00

216 lines
6.8 KiB
TypeScript

"use client"
import { usePathname } from "next/navigation"
import { useFormContext, useWatch } from "react-hook-form"
import { useIntl } from "react-intl"
import { dt } from "@scandic-hotels/common/dt"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { SEARCH_TYPE_REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
import { hotelreservation } from "@/constants/routes/hotelReservation"
import DatePicker from "@/components/DatePicker"
import GuestsRoomsPickerForm from "@/components/GuestsRoomsPicker"
import SkeletonShimmer from "@/components/SkeletonShimmer"
import Button from "@/components/TempDesignSystem/Button"
import useLang from "@/hooks/useLang"
import { RemoveExtraRooms } from "./BookingCode"
import { Search, SearchSkeleton } from "./Search"
import { isMultiRoomError } from "./utils"
import ValidationError from "./ValidationError"
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({
formId,
onSubmit,
isSearching,
}: BookingWidgetFormContentProps) {
const intl = useIntl()
const {
formState: { errors, isDirty },
} = useFormContext<BookingWidgetSchema>()
const lang = useLang()
const pathName = usePathname()
const isBookingFlow = pathName.includes(hotelreservation(lang))
const selectedDate = useWatch<BookingWidgetSchema, "date">({ name: "date" })
const nights = dt(selectedDate.toDate).diff(dt(selectedDate.fromDate), "days")
return (
<div className={styles.input}>
<div className={styles.inputContainer}>
<div className={styles.where}>
<Search
handlePressEnter={onSubmit}
selectOnBlur={true}
inputName="search"
includeTypes={["cities", "hotels"]}
/>
{errors.search && <ValidationError />}
</div>
<div className={styles.when}>
<Typography
variant="Body/Supporting text (caption)/smBold"
className={styles.label}
>
<label htmlFor="date">
{nights > 0
? intl.formatMessage(
{
defaultMessage:
"{totalNights, plural, one {# night} other {# nights}}",
},
{ totalNights: nights }
)
: intl.formatMessage({
defaultMessage: "Check in",
})}
</label>
</Typography>
<DatePicker name="date" />
</div>
<div className={styles.rooms}>
<Typography
variant="Body/Supporting text (caption)/smBold"
className={styles.label}
>
<label id="rooms-and-guests">
{intl.formatMessage({
defaultMessage: "Rooms & Guests",
})}
</label>
</Typography>
<GuestsRoomsPickerForm ariaLabelledBy="rooms-and-guests" />
</div>
</div>
<div className={`${styles.buttonContainer} ${styles.showOnTablet}`}>
<Button
className={styles.button}
form={formId}
intent="primary"
theme="base"
type="submit"
>
<span className={styles.icon}>
<MaterialIcon icon="search" color="Icon/Inverted" size={28} />
</span>
</Button>
</div>
<div className={`${styles.voucherContainer} ${styles.voucherRow}`}>
<Voucher />
</div>
<div className={`${styles.buttonContainer} ${styles.hideOnTablet}`}>
{isMultiRoomError(errors.bookingCode?.value?.message) ||
isMultiRoomError(errors[SEARCH_TYPE_REDEMPTION]?.message) ? (
<div className={styles.showOnMobile}>
<RemoveExtraRooms size="medium" fullWidth />
</div>
) : null}
<Button
className={styles.button}
form={formId}
intent="primary"
theme="base"
type="submit"
disabled={isSearching}
>
<Typography
variant="Body/Supporting text (caption)/smBold"
className={styles.buttonText}
>
<span>
{isDirty && isBookingFlow
? intl.formatMessage({ defaultMessage: "Update" })
: intl.formatMessage({ defaultMessage: "Search" })}
</span>
</Typography>
<span className={styles.icon}>
<MaterialIcon icon="search" color="Icon/Inverted" size={28} />
</span>
</Button>
</div>
</div>
)
}
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}>
<Typography
variant="Body/Supporting text (caption)/smBold"
className={styles.label}
>
<label>
{intl.formatMessage(
{
defaultMessage:
"{totalNights, plural, one {# night} other {# nights}}",
},
{ totalNights: 0 }
)}
</label>
</Typography>
<SkeletonShimmer width={"100%"} display={"block"} />
</div>
<div className={styles.rooms}>
<Typography
variant="Body/Supporting text (caption)/smBold"
className={styles.label}
>
<label id="rooms-and-guests">
{intl.formatMessage({
defaultMessage: "Rooms & Guests",
})}
</label>
</Typography>
<SkeletonShimmer width={"100%"} display={"block"} />
</div>
</div>
<div className={styles.voucherContainer}>
<VoucherSkeleton />
</div>
<div className={styles.buttonContainer}>
<Button
className={styles.button}
intent="primary"
theme="base"
type="submit"
disabled
>
<Typography
variant="Body/Supporting text (caption)/smBold"
className={styles.buttonText}
>
<span>
{intl.formatMessage({
defaultMessage: "Search",
})}
</span>
</Typography>
<span className={styles.icon}>
<MaterialIcon icon="search" color="Icon/Inverted" size={28} />
</span>
</Button>
</div>
</div>
)
}