"use client" import { zodResolver } from "@hookform/resolvers/zod" import { useCallback, useEffect, useMemo, useRef } from "react" import { FormProvider, useForm } from "react-hook-form" import { useIntl } from "react-intl" import { usePhoneNumberParsing } from "@scandic-hotels/common/hooks/usePhoneNumberParsing" import { getDefaultCountryFromLang } from "@scandic-hotels/common/utils/phone" import CountrySelect from "@scandic-hotels/design-system/Form/Country" import Phone from "@scandic-hotels/design-system/Form/Phone" import { Typography } from "@scandic-hotels/design-system/Typography" import { useFormTracking } from "@scandic-hotels/tracking/useFormTracking" import { useBookingFlowConfig } from "../../../../bookingFlowConfig/bookingFlowConfigContext" import { useRoomContext } from "../../../../contexts/EnterDetails/RoomContext" import useLang from "../../../../hooks/useLang" import { getFormattedCountryList } from "../../../../misc/getFormatedCountryList" import { useEnterDetailsStore } from "../../../../stores/enter-details" import BookingFlowInput from "../../../BookingFlowInput" import { getErrorMessage } from "../../../BookingFlowInput/errors" import MemberPriceModal from "../MemberPriceModal" import { SpecialRequests } from "../SpecialRequests" import JoinScandicFriendsCard from "./JoinScandicFriendsCard" import { PartnerSASJoinScandicFriendsCard } from "./PartnerSASJoinScandicFriendsCard" import { getMultiroomDetailsSchema } from "./schema" import styles from "./details.module.css" const formID = "enter-details" export default function Details() { const intl = useIntl() const lang = useLang() const config = useBookingFlowConfig() const refs = useRef>({}) const { addPreSubmitCallback, rooms } = useEnterDetailsStore((state) => ({ addPreSubmitCallback: state.actions.addPreSubmitCallback, rooms: state.rooms, })) const { actions: { updateDetails, updatePartialGuestData, setIncomplete }, idx, room, roomNr, } = useRoomContext() const initialData = room.guest /** * The data that each room needs from each other to do validations * across the rooms */ const crossValidationData = useMemo( () => rooms .filter((_, i) => i !== idx) .map((room) => ({ firstName: room.room.guest.firstName, lastName: room.room.guest.lastName, membershipNo: room.room.guest.membershipNo, })), [idx, rooms] ) const { phoneNumber, phoneNumberCC } = usePhoneNumberParsing( initialData.phoneNumber, initialData.phoneNumberCC ) const methods = useForm({ defaultValues: { countryCode: initialData.countryCode, email: initialData.email, firstName: initialData.firstName, join: initialData.join, lastName: initialData.lastName, membershipNo: initialData.membershipNo, phoneNumber, phoneNumberCC, specialRequest: { comment: room.specialRequest.comment, }, }, criteriaMode: "all", mode: "onBlur", resolver: zodResolver(getMultiroomDetailsSchema(crossValidationData)), reValidateMode: "onChange", }) const { handleSubmit, trigger, control, subscribe, formState: { isValid, errors }, setValue, watch, getValues, } = methods const { trackFormSubmit } = useFormTracking( "checkout", subscribe, control, ` - room ${roomNr}` ) useEffect(() => { async function callback() { await trigger() trackFormSubmit() const fieldOrder = [ "firstName", "lastName", "countryCode", "email", "phoneNumber", "membershipNo", ] for (const name of fieldOrder) { const fieldError = methods.formState.errors[ name as keyof typeof methods.formState.errors ] if (fieldError && refs.current[name]) { return refs.current[name] ?? undefined } } return } addPreSubmitCallback(`${idx}-details`, callback) }, [addPreSubmitCallback, idx, trigger, trackFormSubmit, methods]) const updateDetailsStore = useCallback(() => { if (isValid) { handleSubmit(updateDetails)() } else { updatePartialGuestData({ firstName: getValues("firstName")?.toString(), lastName: getValues("lastName")?.toString(), membershipNo: getValues("membershipNo")?.toString(), }) setIncomplete() } }, [ handleSubmit, isValid, setIncomplete, updateDetails, updatePartialGuestData, getValues, ]) useEffect(updateDetailsStore, [updateDetailsStore]) // Trigger validation of the room manually when another room changes its data. // Only do it if the field has a value, to avoid error states before the user // has filled anything in. useEffect(() => { const { firstName, lastName, membershipNo } = methods.getValues() if (firstName) { methods.trigger("firstName") } if (lastName) { methods.trigger("lastName") } if (membershipNo) { methods.trigger("membershipNo") } }, [crossValidationData, methods]) const countryCode = watch("countryCode") useEffect(() => { if (countryCode) { setValue("phoneNumberCC", countryCode.toLowerCase()) } }, [countryCode, setValue]) const guestIsGoingToJoin = methods.watch("join") const guestIsMember = methods.watch("membershipNo") const showMembershipIdInput = config.enterDetailsMembershipIdInputLocation === "form" && !guestIsGoingToJoin return (
{guestIsMember ? null : ( )}

{intl.formatMessage({ id: "enterDetails.roomInfo.title", defaultMessage: "Guest information", })}

{ refs.current.firstName = el }} >
{ refs.current.lastName = el }} >
{ refs.current.countryCode = el }} className={styles.fullWidth} >
{ refs.current.email = el }} className={styles.fullWidth} >
{ refs.current.phoneNumber = el }} className={styles.fullWidth} >
{showMembershipIdInput ? (
{ refs.current.membershipNo = el }} className={styles.fullWidth} >
) : null}
) } function JoinScandicCard({ updateDetailsStore, }: { updateDetailsStore: () => void }) { const config = useBookingFlowConfig() switch (config.enterDetailsMembershipIdInputLocation) { case "form": return case "join-card": return ( ) default: const _exhaustiveCheck: never = config.enterDetailsMembershipIdInputLocation return null } }