fix: trigger memberPrice modal for membership number too
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
"use client"
|
||||
|
||||
import { useEffect, useState } from "react"
|
||||
import { useFormContext, useWatch } from "react-hook-form"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import MagicWandIcon from "@scandic-hotels/design-system/Icons/MagicWandIcon"
|
||||
@@ -14,20 +16,35 @@ import { formatPrice } from "@/utils/numberFormatting"
|
||||
|
||||
import styles from "./modal.module.css"
|
||||
|
||||
import type { Dispatch, SetStateAction } from "react"
|
||||
|
||||
import { CurrencyEnum } from "@/types/enums/currency"
|
||||
|
||||
export default function MemberPriceModal({
|
||||
isOpen,
|
||||
setIsOpen,
|
||||
}: {
|
||||
isOpen: boolean
|
||||
setIsOpen: Dispatch<SetStateAction<boolean>>
|
||||
}) {
|
||||
const { room } = useRoomContext()
|
||||
export default function MemberPriceModal() {
|
||||
const {
|
||||
actions: { updatePriceForMembershipNo },
|
||||
room,
|
||||
} = useRoomContext()
|
||||
const memberRate = "member" in room.roomRate ? room.roomRate.member : null
|
||||
const intl = useIntl()
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const { getFieldState, trigger } = useFormContext()
|
||||
|
||||
const [join, membershipNo] = useWatch({ name: ["join", "membershipNo"] })
|
||||
|
||||
useEffect(() => {
|
||||
if (join) {
|
||||
setIsOpen(true)
|
||||
}
|
||||
}, [join])
|
||||
|
||||
useEffect(() => {
|
||||
trigger("membershipNo").then((isValid) => {
|
||||
const { isDirty } = getFieldState("membershipNo")
|
||||
updatePriceForMembershipNo(membershipNo, isValid)
|
||||
if (isValid && isDirty) {
|
||||
setIsOpen(true)
|
||||
}
|
||||
})
|
||||
}, [getFieldState, membershipNo, trigger, updatePriceForMembershipNo])
|
||||
|
||||
if (!memberRate) {
|
||||
return null
|
||||
@@ -36,7 +53,7 @@ export default function MemberPriceModal({
|
||||
const memberPrice = memberRate?.localPrice ?? memberRate?.requestedPrice
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onToggle={setIsOpen}>
|
||||
<Modal isOpen={isOpen} onToggle={() => setIsOpen(false)}>
|
||||
<div className={styles.modalContent}>
|
||||
<div className={styles.innerModalContent}>
|
||||
<MagicWandIcon width="265px" />
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||
@@ -10,8 +9,6 @@ import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
import { useRoomContext } from "@/contexts/Details/Room"
|
||||
import { formatPrice } from "@/utils/numberFormatting"
|
||||
|
||||
import MemberPriceModal from "../../MemberPriceModal"
|
||||
|
||||
import styles from "./joinScandicFriendsCard.module.css"
|
||||
|
||||
import type { JoinScandicFriendsCardProps } from "@/types/components/hotelReservation/enterDetails/details"
|
||||
@@ -26,13 +23,9 @@ export default function JoinScandicFriendsCard({
|
||||
roomNr,
|
||||
actions: { updateJoin },
|
||||
} = useRoomContext()
|
||||
const [isMemberPriceModalOpen, setIsMemberPriceModalOpen] = useState(false)
|
||||
|
||||
function onChange(event: { target: { value: boolean } }) {
|
||||
updateJoin(event.target.value)
|
||||
if (event.target.value) {
|
||||
setIsMemberPriceModalOpen(true)
|
||||
}
|
||||
}
|
||||
|
||||
if (!("member" in room.roomRate) || !room.roomRate.member) {
|
||||
@@ -106,10 +99,6 @@ export default function JoinScandicFriendsCard({
|
||||
</Caption>
|
||||
))}
|
||||
</div>
|
||||
<MemberPriceModal
|
||||
isOpen={isMemberPriceModalOpen}
|
||||
setIsOpen={setIsMemberPriceModalOpen}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import Phone from "@/components/TempDesignSystem/Form/Phone"
|
||||
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
|
||||
import { useRoomContext } from "@/contexts/Details/Room"
|
||||
|
||||
import MemberPriceModal from "../MemberPriceModal"
|
||||
import JoinScandicFriendsCard from "./JoinScandicFriendsCard"
|
||||
import { getMultiroomDetailsSchema } from "./schema"
|
||||
|
||||
@@ -52,11 +53,7 @@ export default function Details() {
|
||||
)
|
||||
|
||||
const methods = useForm({
|
||||
criteriaMode: "all",
|
||||
mode: "all",
|
||||
resolver: zodResolver(getMultiroomDetailsSchema(crossValidationData)),
|
||||
reValidateMode: "onChange",
|
||||
values: {
|
||||
defaultValues: {
|
||||
countryCode: initialData.countryCode,
|
||||
email: initialData.email,
|
||||
firstName: initialData.firstName,
|
||||
@@ -68,6 +65,10 @@ export default function Details() {
|
||||
comment: room.specialRequest.comment,
|
||||
},
|
||||
},
|
||||
criteriaMode: "all",
|
||||
mode: "all",
|
||||
resolver: zodResolver(getMultiroomDetailsSchema(crossValidationData)),
|
||||
reValidateMode: "onChange",
|
||||
})
|
||||
|
||||
const {
|
||||
@@ -189,6 +190,7 @@ export default function Details() {
|
||||
)}
|
||||
<SpecialRequests registerOptions={{ onBlur: updateDetailsStore }} />
|
||||
</div>
|
||||
<MemberPriceModal />
|
||||
</form>
|
||||
</FormProvider>
|
||||
)
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||
@@ -16,8 +14,6 @@ import { useRoomContext } from "@/contexts/Details/Room"
|
||||
import useLang from "@/hooks/useLang"
|
||||
import { formatPrice } from "@/utils/numberFormatting"
|
||||
|
||||
import MemberPriceModal from "../../MemberPriceModal"
|
||||
|
||||
import styles from "./joinScandicFriendsCard.module.css"
|
||||
|
||||
import type { JoinScandicFriendsCardProps } from "@/types/components/hotelReservation/enterDetails/details"
|
||||
@@ -32,13 +28,9 @@ export default function JoinScandicFriendsCard({
|
||||
room,
|
||||
actions: { updateJoin },
|
||||
} = useRoomContext()
|
||||
const [isMemberPriceModalOpen, setIsMemberPriceModalOpen] = useState(false)
|
||||
|
||||
function onChange(event: { target: { value: boolean } }) {
|
||||
updateJoin(event.target.value)
|
||||
if (event.target.value) {
|
||||
setIsMemberPriceModalOpen(true)
|
||||
}
|
||||
}
|
||||
|
||||
if (!("member" in room.roomRate) || !room.roomRate.member) {
|
||||
@@ -156,10 +148,6 @@ export default function JoinScandicFriendsCard({
|
||||
)}
|
||||
</Footnote>
|
||||
</div>
|
||||
<MemberPriceModal
|
||||
isOpen={isMemberPriceModalOpen}
|
||||
setIsOpen={setIsMemberPriceModalOpen}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import Phone from "@/components/TempDesignSystem/Form/Phone"
|
||||
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
|
||||
import { useRoomContext } from "@/contexts/Details/Room"
|
||||
|
||||
import MemberPriceModal from "../MemberPriceModal"
|
||||
import JoinScandicFriendsCard from "./JoinScandicFriendsCard"
|
||||
import { guestDetailsSchema, signedInDetailsSchema } from "./schema"
|
||||
import Signup from "./Signup"
|
||||
@@ -43,25 +44,25 @@ export default function Details({ user }: DetailsProps) {
|
||||
const memberRate = "member" in room.roomRate ? room.roomRate.member : null
|
||||
|
||||
const methods = useForm({
|
||||
criteriaMode: "all",
|
||||
mode: "all",
|
||||
resolver: zodResolver(user ? signedInDetailsSchema : guestDetailsSchema),
|
||||
reValidateMode: "onChange",
|
||||
values: {
|
||||
countryCode: user?.address?.countryCode ?? initialData.countryCode,
|
||||
defaultValues: {
|
||||
countryCode: user?.address?.countryCode || initialData.countryCode,
|
||||
dateOfBirth:
|
||||
"dateOfBirth" in initialData ? initialData.dateOfBirth : undefined,
|
||||
email: user?.email ?? initialData.email,
|
||||
firstName: user?.firstName ?? initialData.firstName,
|
||||
email: user?.email || initialData.email,
|
||||
firstName: user?.firstName || initialData.firstName,
|
||||
join: initialData.join,
|
||||
lastName: user?.lastName ?? initialData.lastName,
|
||||
lastName: user?.lastName || initialData.lastName,
|
||||
membershipNo: initialData.membershipNo,
|
||||
phoneNumber: user?.phoneNumber ?? initialData.phoneNumber,
|
||||
phoneNumber: user?.phoneNumber || initialData.phoneNumber,
|
||||
zipCode: "zipCode" in initialData ? initialData.zipCode : undefined,
|
||||
specialRequest: {
|
||||
comment: room.specialRequest.comment,
|
||||
},
|
||||
},
|
||||
criteriaMode: "all",
|
||||
mode: "all",
|
||||
resolver: zodResolver(user ? signedInDetailsSchema : guestDetailsSchema),
|
||||
reValidateMode: "onChange",
|
||||
})
|
||||
|
||||
const {
|
||||
@@ -168,6 +169,7 @@ export default function Details({ user }: DetailsProps) {
|
||||
)}
|
||||
<SpecialRequests registerOptions={{ onBlur: updateDetailsStore }} />
|
||||
</div>
|
||||
<MemberPriceModal />
|
||||
</form>
|
||||
</FormProvider>
|
||||
)
|
||||
|
||||
@@ -57,7 +57,7 @@ export default function Phone({
|
||||
rules: registerOptions,
|
||||
})
|
||||
|
||||
const defaultPhoneNumber = formState.defaultValues?.phoneNumber
|
||||
const defaultPhoneNumber = formState.defaultValues?.phoneNumber ?? ""
|
||||
|
||||
// If defaultPhoneNumber exists and is valid, parse it to get the country code,
|
||||
// otherwise set the default country from the lang.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client"
|
||||
import deepmerge from "deepmerge"
|
||||
import { useEffect, useRef } from "react"
|
||||
import { useEffect, useRef, useState } from "react"
|
||||
|
||||
import { dt } from "@/lib/dt"
|
||||
import { createDetailsStore } from "@/stores/enter-details"
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
|
||||
import { getMultiroomDetailsSchema } from "@/components/HotelReservation/EnterDetails/Details/Multiroom/schema"
|
||||
import { guestDetailsSchema } from "@/components/HotelReservation/EnterDetails/Details/RoomOne/schema"
|
||||
import LoadingSpinner from "@/components/LoadingSpinner"
|
||||
import { DetailsContext } from "@/contexts/Details"
|
||||
|
||||
import type { DetailsStore } from "@/types/contexts/enter-details"
|
||||
@@ -31,6 +32,11 @@ export default function EnterDetailsProvider({
|
||||
user,
|
||||
vat,
|
||||
}: DetailsProviderProps) {
|
||||
// This state is needed to be able to use defaultValues for
|
||||
// react-hook-form since values needs to be there on mount
|
||||
// and since we read from SessionStorage we need to delay
|
||||
// rendering the form until that has been done.
|
||||
const [hasInitializedStore, setHasInitializedStore] = useState(false)
|
||||
const storeRef = useRef<DetailsStore>()
|
||||
if (!storeRef.current) {
|
||||
const initialData: InitialState = {
|
||||
@@ -74,16 +80,19 @@ export default function EnterDetailsProvider({
|
||||
useEffect(() => {
|
||||
const storedValues = readFromSessionStorage()
|
||||
if (!storedValues) {
|
||||
setHasInitializedStore(true)
|
||||
return
|
||||
}
|
||||
const isSameBooking = checkIsSameBooking(storedValues.booking, booking)
|
||||
if (!isSameBooking) {
|
||||
clearSessionStorage()
|
||||
setHasInitializedStore(true)
|
||||
return
|
||||
}
|
||||
|
||||
const store = storeRef.current?.getState()
|
||||
if (!store) {
|
||||
setHasInitializedStore(true)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -209,11 +218,13 @@ export default function EnterDetailsProvider({
|
||||
rooms: filteredOutMissingRooms,
|
||||
totalPrice,
|
||||
})
|
||||
|
||||
setHasInitializedStore(true)
|
||||
}, [booking, rooms, user])
|
||||
|
||||
return (
|
||||
<DetailsContext.Provider value={storeRef.current}>
|
||||
{children}
|
||||
{hasInitializedStore ? children : <LoadingSpinner fullPage />}
|
||||
</DetailsContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -253,24 +253,18 @@ export function createDetailsStore(
|
||||
})
|
||||
)
|
||||
},
|
||||
updateJoin(join) {
|
||||
updatePriceForMembershipNo(membershipNo, isValid) {
|
||||
return set(
|
||||
produce((state: DetailsState) => {
|
||||
const currentRoom = state.rooms[idx].room
|
||||
|
||||
currentRoom.guest.join = join
|
||||
|
||||
if (join) {
|
||||
currentRoom.guest.membershipNo = undefined
|
||||
}
|
||||
currentRoom.guest.join = false
|
||||
currentRoom.guest.membershipNo = isValid ? membershipNo : ""
|
||||
|
||||
const isValidMembershipNo = isValid && !!membershipNo
|
||||
currentRoom.roomPrice = getRoomPrice(
|
||||
currentRoom.roomRate,
|
||||
Boolean(
|
||||
join ||
|
||||
currentRoom.guest.membershipNo ||
|
||||
(idx === 0 && isMember)
|
||||
)
|
||||
isValidMembershipNo
|
||||
)
|
||||
|
||||
const nights = dt(state.booking.toDate).diff(
|
||||
@@ -284,6 +278,43 @@ export function createDetailsStore(
|
||||
isMember,
|
||||
nights
|
||||
)
|
||||
|
||||
writeToSessionStorage({
|
||||
booking: state.booking,
|
||||
rooms: state.rooms,
|
||||
})
|
||||
})
|
||||
)
|
||||
},
|
||||
updateJoin(join) {
|
||||
return set(
|
||||
produce((state: DetailsState) => {
|
||||
const currentRoom = state.rooms[idx].room
|
||||
|
||||
currentRoom.guest.join = join
|
||||
|
||||
if (join) {
|
||||
currentRoom.guest.membershipNo = ""
|
||||
}
|
||||
|
||||
currentRoom.roomPrice = getRoomPrice(currentRoom.roomRate, join)
|
||||
|
||||
const nights = dt(state.booking.toDate).diff(
|
||||
state.booking.fromDate,
|
||||
"days"
|
||||
)
|
||||
|
||||
state.totalPrice = calcTotalPrice(
|
||||
state.rooms,
|
||||
state.totalPrice.local.currency,
|
||||
isMember,
|
||||
nights
|
||||
)
|
||||
|
||||
writeToSessionStorage({
|
||||
booking: state.booking,
|
||||
rooms: state.rooms,
|
||||
})
|
||||
})
|
||||
)
|
||||
},
|
||||
|
||||
@@ -1,16 +1,7 @@
|
||||
import type { BreakfastPackage } from "@/types/components/hotelReservation/breakfast"
|
||||
import type { BedTypeSchema } from "@/types/components/hotelReservation/enterDetails/bedType"
|
||||
import type { DetailsSchema } from "@/types/components/hotelReservation/enterDetails/details"
|
||||
import type { RoomState } from "@/types/stores/enter-details"
|
||||
|
||||
export interface RoomContextValue {
|
||||
actions: {
|
||||
setIncomplete: () => void
|
||||
updateBedType: (data: BedTypeSchema) => void
|
||||
updateBreakfast: (data: BreakfastPackage | false) => void
|
||||
updateJoin: (join: boolean) => void
|
||||
updateDetails: (data: DetailsSchema) => void
|
||||
}
|
||||
actions: RoomState["actions"]
|
||||
isComplete: RoomState["isComplete"]
|
||||
idx: number
|
||||
room: RoomState["room"]
|
||||
|
||||
@@ -65,6 +65,7 @@ export interface RoomState {
|
||||
updateBedType: (data: BedTypeSchema) => void
|
||||
updateBreakfast: (data: BreakfastPackage | false) => void
|
||||
updateJoin: (join: boolean) => void
|
||||
updatePriceForMembershipNo: (membershipNo: string, isValid: boolean) => void
|
||||
updateDetails: (data: DetailsSchema) => void
|
||||
}
|
||||
isComplete: boolean
|
||||
|
||||
Reference in New Issue
Block a user