Merged in chore/move-enter-details (pull request #2778)
Chore/move enter details Approved-by: Anton Gunnarsson
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.form {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x2);
|
||||
grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
|
||||
width: min(696px, 100%);
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
"use client"
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { useCallback, useEffect } from "react"
|
||||
import { FormProvider, useForm } from "react-hook-form"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { formatPrice } from "@scandic-hotels/common/utils/numberFormatting"
|
||||
import Body from "@scandic-hotels/design-system/Body"
|
||||
import RadioCard from "@scandic-hotels/design-system/Form/RadioCard"
|
||||
import BreakfastBuffetIcon from "@scandic-hotels/design-system/Icons/BreakfastBuffetIcon"
|
||||
import NoBreakfastBuffetIcon from "@scandic-hotels/design-system/Icons/NoBreakfastBuffetIcon"
|
||||
import { BreakfastPackageEnum } from "@scandic-hotels/trpc/enums/breakfast"
|
||||
|
||||
import { useRoomContext } from "../../../contexts/EnterDetails/RoomContext"
|
||||
import { useEnterDetailsStore } from "../../../stores/enter-details"
|
||||
import { useTrackingContext } from "../../../trackingContext"
|
||||
import { type BreakfastFormSchema, breakfastFormSchema } from "./schema"
|
||||
|
||||
import styles from "./breakfast.module.css"
|
||||
|
||||
export default function Breakfast() {
|
||||
const intl = useIntl()
|
||||
const packages = useEnterDetailsStore((state) => state.breakfastPackages)
|
||||
const hotelId = useEnterDetailsStore((state) => state.booking.hotelId)
|
||||
const {
|
||||
actions: { updateBreakfast },
|
||||
room,
|
||||
} = useRoomContext()
|
||||
|
||||
const { trackBreakfastSelection } = useTrackingContext()
|
||||
|
||||
const hasChildrenInRoom = !!room.childrenInRoom?.length
|
||||
const totalPriceForNoBreakfast = 0
|
||||
|
||||
const breakfastSelection = room?.breakfast
|
||||
? room.breakfast.code
|
||||
: room?.breakfast === false
|
||||
? "false"
|
||||
: undefined
|
||||
|
||||
const methods = useForm<BreakfastFormSchema>({
|
||||
criteriaMode: "all",
|
||||
mode: "all",
|
||||
resolver: zodResolver(breakfastFormSchema),
|
||||
reValidateMode: "onChange",
|
||||
values: breakfastSelection ? { breakfast: breakfastSelection } : undefined,
|
||||
})
|
||||
|
||||
const onSubmit = useCallback(
|
||||
(values: BreakfastFormSchema) => {
|
||||
const pkg = packages.find((p) => p.code === values.breakfast)
|
||||
if (pkg) {
|
||||
updateBreakfast(pkg)
|
||||
} else {
|
||||
updateBreakfast(false)
|
||||
}
|
||||
|
||||
trackBreakfastSelection({
|
||||
breakfastPackage: pkg ?? packages[0],
|
||||
hotelId,
|
||||
units: pkg ? room.adults : 0,
|
||||
})
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[packages, hotelId, room.adults, updateBreakfast]
|
||||
)
|
||||
|
||||
const selectedBreakfast = methods.watch("breakfast")
|
||||
const handleSubmit = methods.handleSubmit
|
||||
useEffect(() => {
|
||||
if (selectedBreakfast) {
|
||||
handleSubmit(onSubmit)()
|
||||
}
|
||||
}, [selectedBreakfast, handleSubmit, onSubmit])
|
||||
|
||||
return (
|
||||
<FormProvider {...methods}>
|
||||
<div className={styles.container}>
|
||||
{hasChildrenInRoom ? (
|
||||
<Body>
|
||||
{intl.formatMessage({
|
||||
defaultMessage:
|
||||
"Children's breakfast is always free as part of the adult's breakfast.",
|
||||
})}
|
||||
</Body>
|
||||
) : null}
|
||||
<form className={styles.form} onSubmit={methods.handleSubmit(onSubmit)}>
|
||||
{packages?.map((pkg) => (
|
||||
<RadioCard
|
||||
key={pkg.code}
|
||||
name="breakfast"
|
||||
value={pkg.code}
|
||||
Icon={BreakfastBuffetIcon}
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Breakfast buffet",
|
||||
})}
|
||||
subtitle={
|
||||
pkg.code === BreakfastPackageEnum.FREE_MEMBER_BREAKFAST
|
||||
? intl.formatMessage({
|
||||
defaultMessage: "Included",
|
||||
})
|
||||
: `+ ${formatPrice(intl, pkg.localPrice.price, pkg.localPrice.currency ?? "")}`
|
||||
}
|
||||
subtitleSecondary={intl.formatMessage({
|
||||
defaultMessage: "Per adult/night",
|
||||
})}
|
||||
description={
|
||||
hasChildrenInRoom
|
||||
? intl.formatMessage({
|
||||
defaultMessage: "Free for kids aged 12 and under.",
|
||||
})
|
||||
: undefined
|
||||
}
|
||||
descriptionSecondary={intl.formatMessage({
|
||||
defaultMessage:
|
||||
"Includes vegan, gluten-free, and other allergy-friendly options.",
|
||||
})}
|
||||
/>
|
||||
))}
|
||||
<RadioCard
|
||||
name="breakfast"
|
||||
value="false"
|
||||
Icon={NoBreakfastBuffetIcon}
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "No breakfast",
|
||||
})}
|
||||
subtitle={`+ ${formatPrice(intl, totalPriceForNoBreakfast, packages?.[0].localPrice.currency ?? "")}`}
|
||||
descriptionSecondary={
|
||||
hasChildrenInRoom
|
||||
? intl.formatMessage({
|
||||
defaultMessage:
|
||||
"Breakfast can be added after booking for an extra cost for adults and kids ages 4 and up.",
|
||||
})
|
||||
: intl.formatMessage({
|
||||
defaultMessage:
|
||||
"Breakfast can be added after booking for an additional fee.",
|
||||
})
|
||||
}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</FormProvider>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { z } from "zod"
|
||||
|
||||
export type BreakfastFormSchema = z.infer<typeof breakfastFormSchema>
|
||||
export const breakfastFormSchema = z.object({
|
||||
breakfast: z.string().or(z.literal("false")),
|
||||
})
|
||||
Reference in New Issue
Block a user