diff --git a/app/[lang]/(live)/(public)/hotelreservation/[step]/layout.tsx b/app/[lang]/(live)/(public)/hotelreservation/[step]/layout.tsx index 5f62c69c9..0e8edd50e 100644 --- a/app/[lang]/(live)/(public)/hotelreservation/[step]/layout.tsx +++ b/app/[lang]/(live)/(public)/hotelreservation/[step]/layout.tsx @@ -1,6 +1,10 @@ import { redirect } from "next/navigation" -import { serverClient } from "@/lib/trpc/server" +import { + getCreditCardsSafely, + getHotelData, + getProfileSafely, +} from "@/lib/trpc/memoizedRequests" import EnterDetailsProvider from "@/components/HotelReservation/EnterDetails/Provider" import SelectedRoom from "@/components/HotelReservation/EnterDetails/SelectedRoom" @@ -14,15 +18,20 @@ import styles from "./layout.module.css" import { StepEnum } from "@/types/components/enterDetails/step" import type { LangParams, LayoutArgs } from "@/types/params" +function preload(id: string, lang: string) { + void getHotelData(id, lang) + void getProfileSafely() + void getCreditCardsSafely() +} + export default async function StepLayout({ children, params, }: React.PropsWithChildren>) { setLang(params.lang) - const hotel = await serverClient().hotel.hotelData.get({ - hotelId: "811", - language: params.lang, - }) + preload("811", params.lang) + + const hotel = await getHotelData("811", params.lang) if (!hotel?.data) { redirect(`/${params.lang}`) diff --git a/app/[lang]/(live)/(public)/hotelreservation/[step]/page.tsx b/app/[lang]/(live)/(public)/hotelreservation/[step]/page.tsx index 5f2666c8e..8e8d3c891 100644 --- a/app/[lang]/(live)/(public)/hotelreservation/[step]/page.tsx +++ b/app/[lang]/(live)/(public)/hotelreservation/[step]/page.tsx @@ -1,14 +1,17 @@ import { notFound } from "next/navigation" -import { getProfileSafely } from "@/lib/trpc/memoizedRequests" -import { serverClient } from "@/lib/trpc/server" +import { + getCreditCardsSafely, + getHotelData, + getProfileSafely, +} from "@/lib/trpc/memoizedRequests" import BedType from "@/components/HotelReservation/EnterDetails/BedType" import Breakfast from "@/components/HotelReservation/EnterDetails/Breakfast" import Details from "@/components/HotelReservation/EnterDetails/Details" import HistoryStateManager from "@/components/HotelReservation/EnterDetails/HistoryStateManager" +import Payment from "@/components/HotelReservation/EnterDetails/Payment" import SectionAccordion from "@/components/HotelReservation/EnterDetails/SectionAccordion" -import Payment from "@/components/HotelReservation/SelectRate/Payment" import { getIntl } from "@/i18n" import { StepEnum } from "@/types/components/enterDetails/step" @@ -25,12 +28,9 @@ export default async function StepPage({ const intl = await getIntl() - const hotel = await serverClient().hotel.hotelData.get({ - hotelId: "811", - language: lang, - }) - + const hotel = await getHotelData("811", lang) const user = await getProfileSafely() + const savedCreditCards = await getCreditCardsSafely() if (!isValidStep(step) || !hotel) { return notFound() @@ -65,7 +65,14 @@ export default async function StepPage({ step={StepEnum.payment} label={intl.formatMessage({ id: "Select payment method" })} > - + ) diff --git a/components/Blocks/TextCols/textcols.module.css b/components/Blocks/TextCols/textcols.module.css index b3e9e2824..8c3a1bda1 100644 --- a/components/Blocks/TextCols/textcols.module.css +++ b/components/Blocks/TextCols/textcols.module.css @@ -2,7 +2,6 @@ display: flex; flex-direction: column; gap: var(--Spacing-x3); - padding: var(--Spacing-x3) var(--Spacing-x4); } .column { diff --git a/components/Blocks/UspGrid/uspgrid.module.css b/components/Blocks/UspGrid/uspgrid.module.css index 042acd9b3..91c6f8fd8 100644 --- a/components/Blocks/UspGrid/uspgrid.module.css +++ b/components/Blocks/UspGrid/uspgrid.module.css @@ -1,7 +1,6 @@ .grid { display: grid; gap: var(--Spacing-x3); - padding: var(--Spacing-x3) var(--Spacing-x4); } @media screen and (min-width: 767px) { .grid { diff --git a/components/ContentType/ContentPage/contentPage.module.css b/components/ContentType/ContentPage/contentPage.module.css index aa0036f83..c0c94aa42 100644 --- a/components/ContentType/ContentPage/contentPage.module.css +++ b/components/ContentType/ContentPage/contentPage.module.css @@ -37,6 +37,7 @@ display: grid; padding: var(--Spacing-x4) var(--Spacing-x2) 0; gap: var(--Spacing-x4); + align-items: start; } .mainContent { diff --git a/components/ContentType/ContentPage/index.tsx b/components/ContentType/ContentPage/index.tsx index 5c5c63893..b6a556dc5 100644 --- a/components/ContentType/ContentPage/index.tsx +++ b/components/ContentType/ContentPage/index.tsx @@ -44,6 +44,7 @@ export default async function ContentPage() { ) : null} diff --git a/components/DeprecatedJsonToHtml/renderOptions.tsx b/components/DeprecatedJsonToHtml/renderOptions.tsx index a73efe396..c22f469e0 100644 --- a/components/DeprecatedJsonToHtml/renderOptions.tsx +++ b/components/DeprecatedJsonToHtml/renderOptions.tsx @@ -397,6 +397,7 @@ export const renderOptions: RenderOptions = { height={365} src={image.url} width={width} + focalPoint={image.focalPoint} {...props} /> {image.meta.caption} diff --git a/components/Hero/hero.ts b/components/Hero/hero.ts index 29fd1b8d1..07cc2cb1c 100644 --- a/components/Hero/hero.ts +++ b/components/Hero/hero.ts @@ -1,4 +1,7 @@ +import type { FocalPoint } from "@/types/components/image" + export interface HeroProps { alt: string src: string + focalPoint?: FocalPoint } diff --git a/components/Hero/index.tsx b/components/Hero/index.tsx index fade3e6b0..0c1df4108 100644 --- a/components/Hero/index.tsx +++ b/components/Hero/index.tsx @@ -4,7 +4,7 @@ import { HeroProps } from "./hero" import styles from "./hero.module.css" -export default async function Hero({ alt, src }: HeroProps) { +export default async function Hero({ alt, src, focalPoint }: HeroProps) { return ( ) } diff --git a/components/HotelReservation/EnterDetails/Payment/PaymentOption/index.tsx b/components/HotelReservation/EnterDetails/Payment/PaymentOption/index.tsx new file mode 100644 index 000000000..c076a074b --- /dev/null +++ b/components/HotelReservation/EnterDetails/Payment/PaymentOption/index.tsx @@ -0,0 +1,49 @@ +import Image from "next/image" +import { useFormContext } from "react-hook-form" + +import { PAYMENT_METHOD_ICONS, PaymentMethodEnum } from "@/constants/booking" + +import Body from "@/components/TempDesignSystem/Text/Body" +import Caption from "@/components/TempDesignSystem/Text/Caption" + +import { PaymentOptionProps } from "./paymentOption" + +import styles from "./paymentOption.module.css" + +export default function PaymentOption({ + name, + value, + label, + cardNumber, + registerOptions = {}, +}: PaymentOptionProps) { + const { register } = useFormContext() + + return ( + + ) +} diff --git a/components/HotelReservation/SelectRate/Payment/PaymentOption/paymentOption.module.css b/components/HotelReservation/EnterDetails/Payment/PaymentOption/paymentOption.module.css similarity index 100% rename from components/HotelReservation/SelectRate/Payment/PaymentOption/paymentOption.module.css rename to components/HotelReservation/EnterDetails/Payment/PaymentOption/paymentOption.module.css diff --git a/components/HotelReservation/SelectRate/Payment/PaymentOption/paymentOption.ts b/components/HotelReservation/EnterDetails/Payment/PaymentOption/paymentOption.ts similarity index 65% rename from components/HotelReservation/SelectRate/Payment/PaymentOption/paymentOption.ts rename to components/HotelReservation/EnterDetails/Payment/PaymentOption/paymentOption.ts index 5d77f6560..151a18860 100644 --- a/components/HotelReservation/SelectRate/Payment/PaymentOption/paymentOption.ts +++ b/components/HotelReservation/EnterDetails/Payment/PaymentOption/paymentOption.ts @@ -1,10 +1,10 @@ import { RegisterOptions } from "react-hook-form" -import { PaymentMethodEnum } from "@/constants/booking" - export interface PaymentOptionProps { name: string - value: PaymentMethodEnum + value: string label: string + cardNumber?: string registerOptions?: RegisterOptions + onChange?: () => void } diff --git a/components/HotelReservation/SelectRate/Payment/index.tsx b/components/HotelReservation/EnterDetails/Payment/index.tsx similarity index 55% rename from components/HotelReservation/SelectRate/Payment/index.tsx rename to components/HotelReservation/EnterDetails/Payment/index.tsx index b1bddd84c..3d582e7e3 100644 --- a/components/HotelReservation/SelectRate/Payment/index.tsx +++ b/components/HotelReservation/EnterDetails/Payment/index.tsx @@ -23,6 +23,7 @@ import LoadingSpinner from "@/components/LoadingSpinner" import Button from "@/components/TempDesignSystem/Button" import Checkbox from "@/components/TempDesignSystem/Checkbox" import Link from "@/components/TempDesignSystem/Link" +import Body from "@/components/TempDesignSystem/Text/Body" import Caption from "@/components/TempDesignSystem/Text/Caption" import { toast } from "@/components/TempDesignSystem/Toasts" import { useHandleBookingStatus } from "@/hooks/booking/useHandleBookingStatus" @@ -38,7 +39,15 @@ import { PaymentProps } from "@/types/components/hotelReservation/selectRate/sec const maxRetries = 40 const retryInterval = 2000 -export default function Payment({ hotel }: PaymentProps) { +function isPaymentMethodEnum(value: string): value is PaymentMethodEnum { + return Object.values(PaymentMethodEnum).includes(value as PaymentMethodEnum) +} + +export default function Payment({ + hotelId, + otherPaymentOptions, + savedCreditCards, +}: PaymentProps) { const router = useRouter() const lang = useLang() const intl = useIntl() @@ -46,7 +55,9 @@ export default function Payment({ hotel }: PaymentProps) { const methods = useForm({ defaultValues: { - paymentMethod: PaymentMethodEnum.card, + paymentMethod: savedCreditCards?.length + ? savedCreditCards[0].id + : PaymentMethodEnum.card, smsConfirmation: false, termsAndConditions: false, }, @@ -87,8 +98,17 @@ export default function Payment({ hotel }: PaymentProps) { }, [confirmationNumber, bookingStatus, router]) function handleSubmit(data: PaymentFormData) { + // set payment method to card if saved card is submitted + const paymentMethod = isPaymentMethodEnum(data.paymentMethod) + ? data.paymentMethod + : PaymentMethodEnum.card + + const savedCreditCard = savedCreditCards?.find( + (card) => card.id === data.paymentMethod + ) + initiateBooking.mutate({ - hotelId: hotel.operaId, + hotelId: hotelId, checkInDate: "2024-12-10", checkOutDate: "2024-12-11", rooms: [ @@ -116,7 +136,14 @@ export default function Payment({ hotel }: PaymentProps) { }, ], payment: { - paymentMethod: data.paymentMethod, + paymentMethod, + card: savedCreditCard + ? { + alias: savedCreditCard.alias, + expiryDate: savedCreditCard.expirationDate, + cardType: savedCreditCard.cardType, + } + : undefined, cardHolder: { email: "test.user@scandichotels.com", name: "Test User", @@ -143,65 +170,94 @@ export default function Payment({ hotel }: PaymentProps) { className={styles.paymentContainer} onSubmit={methods.handleSubmit(handleSubmit)} > -
- - {hotel.merchantInformationData.alternatePaymentOptions.map( - (paymentMethod) => ( + {savedCreditCards?.length ? ( +
+ + {intl.formatMessage({ id: "MY SAVED CARDS" })} + +
+ {savedCreditCards?.map((savedCreditCard) => ( + + ))} +
+
+ ) : null} +
+ {savedCreditCards?.length ? ( + + {intl.formatMessage({ id: "OTHER PAYMENT METHODS" })} + + ) : null} +
+ + {otherPaymentOptions.map((paymentMethod) => ( - ) - )} -
- - - {intl.formatMessage({ - id: "I would like to get my booking confirmation via sms", - })} - - + ))} +
+ +
+ + + {intl.formatMessage({ + id: "I would like to get my booking confirmation via sms", + })} + + - - - - {intl.formatMessage( - { - id: "booking.terms", - }, - { - termsLink: (str) => ( - - {str} - - ), - privacyLink: (str) => ( - - {str} - - ), - } - )} - - + + + + {intl.formatMessage( + { + id: "booking.terms", + }, + { + termsLink: (str) => ( + + {str} + + ), + privacyLink: (str) => ( + + {str} + + ), + } + )} + + +