Merged in fix/sw-1897-member-price-modal (pull request #1856)

fix(sw-1897): show member price modal immediately

* fix(sw-1897): show member price modal immediately

* Make checkbox fully controlled

* Remove action in the store that wasn't used


Approved-by: Hrishikesh Vaipurkar
This commit is contained in:
Niclas Edenvin
2025-04-25 09:55:24 +00:00
parent 0011cbe8d2
commit 3a958c4dd1
8 changed files with 87 additions and 66 deletions

View File

@@ -1,5 +1,6 @@
"use client"
import { useState } from "react"
import { useIntl } from "react-intl"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
@@ -9,6 +10,8 @@ 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"
@@ -18,7 +21,19 @@ export default function JoinScandicFriendsCard({
name = "join",
}: JoinScandicFriendsCardProps) {
const intl = useIntl()
const { room, roomNr } = useRoomContext()
const {
room,
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) {
return null
@@ -58,7 +73,11 @@ export default function JoinScandicFriendsCard({
return (
<div className={styles.cardContainer}>
<Checkbox name={name} className={styles.checkBox}>
<Checkbox
name={name}
className={styles.checkBox}
registerOptions={{ onChange, value: room.guest.join }}
>
<div>
<Caption type="label" textTransform="uppercase" color="red">
{saveOnJoiningLabel}
@@ -88,6 +107,10 @@ export default function JoinScandicFriendsCard({
</Caption>
))}
</div>
<MemberPriceModal
isOpen={isMemberPriceModalOpen}
setIsOpen={setIsMemberPriceModalOpen}
/>
</div>
)
}

View File

@@ -1,5 +1,6 @@
"use client"
import { useState } from "react"
import { useIntl } from "react-intl"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
@@ -15,6 +16,8 @@ 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"
@@ -25,7 +28,18 @@ export default function JoinScandicFriendsCard({
}: JoinScandicFriendsCardProps) {
const lang = useLang()
const intl = useIntl()
const { room } = useRoomContext()
const {
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) {
return null
@@ -64,7 +78,11 @@ export default function JoinScandicFriendsCard({
return (
<div className={styles.cardContainer}>
<Checkbox name={name} className={styles.checkBox}>
<Checkbox
name={name}
className={styles.checkBox}
registerOptions={{ onChange }}
>
<div>
<Caption type="label" textTransform="uppercase" color="red">
{saveOnJoiningLabel}
@@ -138,6 +156,10 @@ export default function JoinScandicFriendsCard({
)}
</Footnote>
</div>
<MemberPriceModal
isOpen={isMemberPriceModalOpen}
setIsOpen={setIsMemberPriceModalOpen}
/>
</div>
)
}

View File

@@ -1,6 +1,6 @@
"use client"
import { zodResolver } from "@hookform/resolvers/zod"
import { useCallback, useState } from "react"
import { useCallback } from "react"
import { FormProvider, useForm } from "react-hook-form"
import { useIntl } from "react-intl"
@@ -17,7 +17,6 @@ import { useRoomContext } from "@/contexts/Details/Room"
import { trackPaymentSectionOpen } from "@/utils/tracking/booking"
import JoinScandicFriendsCard from "./JoinScandicFriendsCard"
import MemberPriceModal from "./MemberPriceModal"
import { guestDetailsSchema, signedInDetailsSchema } from "./schema"
import Signup from "./Signup"
@@ -31,7 +30,6 @@ import type {
const formID = "enter-details"
export default function Details({ user }: DetailsProps) {
const intl = useIntl()
const [isMemberPriceModalOpen, setIsMemberPriceModalOpen] = useState(false)
const { canProceedToPayment, lastRoom, isMultiRoom } = useEnterDetailsStore(
(state) => ({
@@ -76,12 +74,9 @@ export default function Details({ user }: DetailsProps) {
const onSubmit = useCallback(
(values: DetailsSchema) => {
if ((values.join || values.membershipNo) && memberRate && !user) {
setIsMemberPriceModalOpen(true)
}
updateDetails(values)
},
[updateDetails, setIsMemberPriceModalOpen, memberRate, user]
[updateDetails]
)
return (
@@ -187,10 +182,6 @@ export default function Details({ user }: DetailsProps) {
</Button>
</footer>
) : null}
<MemberPriceModal
isOpen={isMemberPriceModalOpen}
setIsOpen={setIsMemberPriceModalOpen}
/>
</form>
</FormProvider>
)

View File

@@ -223,6 +223,40 @@ export function createDetailsStore(
})
)
},
updateJoin(join) {
return set(
produce((state: DetailsState) => {
const currentRoom = state.rooms[idx].room
currentRoom.guest.join = join
if (join) {
currentRoom.guest.membershipNo = undefined
}
currentRoom.roomPrice = getRoomPrice(
currentRoom.roomRate,
Boolean(
join ||
currentRoom.guest.membershipNo ||
(idx === 0 && isMember)
)
)
const nights = dt(state.booking.toDate).diff(
state.booking.fromDate,
"days"
)
state.totalPrice = calcTotalPrice(
state.rooms,
state.totalPrice.local.currency,
isMember,
nights
)
})
)
},
updateDetails(data) {
return set(
produce((state: DetailsState) => {
@@ -281,56 +315,6 @@ export function createDetailsStore(
state.rooms[idx].isComplete = true
}
writeToSessionStorage({
booking: state.booking,
rooms: state.rooms,
})
})
)
},
updateMultiroomDetails(data) {
return set(
produce((state: DetailsState) => {
state.rooms[idx].steps[StepEnum.details].isValid = true
state.rooms[idx].room.guest.countryCode = data.countryCode
state.rooms[idx].room.guest.email = data.email
state.rooms[idx].room.guest.firstName = data.firstName
state.rooms[idx].room.guest.join = data.join
state.rooms[idx].room.guest.lastName = data.lastName
if (data.join) {
state.rooms[idx].room.guest.membershipNo = undefined
} else {
state.rooms[idx].room.guest.membershipNo = data.membershipNo
}
state.rooms[idx].room.guest.phoneNumber = data.phoneNumber
const getMemberPrice = Boolean(data.join || data.membershipNo)
state.rooms[idx].room.roomPrice = getRoomPrice(
state.rooms[idx].room.roomRate,
getMemberPrice
)
const nights = dt(state.booking.toDate).diff(
state.booking.fromDate,
"days"
)
state.totalPrice = calcTotalPrice(
state.rooms,
state.totalPrice.local.currency,
getMemberPrice,
nights
)
const isAllStepsCompleted = checkRoomProgress(
state.rooms[idx].steps
)
if (isAllStepsCompleted) {
state.rooms[idx].isComplete = true
}
writeToSessionStorage({
booking: state.booking,
rooms: state.rooms,

View File

@@ -7,6 +7,7 @@ export interface RoomContextValue {
actions: {
updateBedType: (data: BedTypeSchema) => void
updateBreakfast: (data: BreakfastPackage | false) => void
updateJoin: (join: boolean) => void
updateDetails: (data: DetailsSchema) => void
}
isComplete: RoomState["isComplete"]

View File

@@ -61,8 +61,8 @@ export interface RoomState {
actions: {
updateBedType: (data: BedTypeSchema) => void
updateBreakfast: (data: BreakfastPackage | false) => void
updateJoin: (join: boolean) => void
updateDetails: (data: DetailsSchema) => void
updateMultiroomDetails: (data: MultiroomDetailsSchema) => void
}
isComplete: boolean
room: Room