Merge branch 'master' into feature/tracking
This commit is contained in:
@@ -1,5 +0,0 @@
|
||||
import LoadingSpinner from "@/components/LoadingSpinner"
|
||||
|
||||
export default function Loading() {
|
||||
return <LoadingSpinner />
|
||||
}
|
||||
@@ -1,7 +1,42 @@
|
||||
.main {
|
||||
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||
display: grid;
|
||||
gap: var(--Spacing-x5);
|
||||
grid-template-areas: "header" "booking";
|
||||
margin: 0 auto;
|
||||
min-height: 100dvh;
|
||||
padding-top: var(--Spacing-x5);
|
||||
width: var(--max-width-page);
|
||||
}
|
||||
|
||||
.booking {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x5);
|
||||
margin: 0 auto;
|
||||
width: min(calc(100dvw - (var(--Spacing-x3) * 2)), 948px);
|
||||
grid-area: booking;
|
||||
padding-bottom: var(--Spacing-x9);
|
||||
}
|
||||
|
||||
.aside {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1367px) {
|
||||
.main {
|
||||
grid-template-areas:
|
||||
"header receipt"
|
||||
"booking receipt";
|
||||
grid-template-columns: 1fr 340px;
|
||||
grid-template-rows: auto 1fr;
|
||||
padding-top: var(--Spacing-x9);
|
||||
}
|
||||
|
||||
.mobileReceipt {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.aside {
|
||||
display: grid;
|
||||
grid-area: receipt;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
import { Suspense } from "react"
|
||||
|
||||
import { getBookingConfirmation } from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import Details from "@/components/HotelReservation/BookingConfirmation/Details"
|
||||
import Header from "@/components/HotelReservation/BookingConfirmation/Header"
|
||||
import TotalPrice from "@/components/HotelReservation/BookingConfirmation/TotalPrice"
|
||||
import HotelDetails from "@/components/HotelReservation/BookingConfirmation/HotelDetails"
|
||||
import PaymentDetails from "@/components/HotelReservation/BookingConfirmation/PaymentDetails"
|
||||
import Promos from "@/components/HotelReservation/BookingConfirmation/Promos"
|
||||
import Receipt from "@/components/HotelReservation/BookingConfirmation/Receipt"
|
||||
import Rooms from "@/components/HotelReservation/BookingConfirmation/Rooms"
|
||||
import SidePanel from "@/components/HotelReservation/SidePanel"
|
||||
import LoadingSpinner from "@/components/LoadingSpinner"
|
||||
import Divider from "@/components/TempDesignSystem/Divider"
|
||||
import { setLang } from "@/i18n/serverContext"
|
||||
|
||||
import styles from "./page.module.css"
|
||||
@@ -18,10 +26,27 @@ export default async function BookingConfirmationPage({
|
||||
const { confirmationNumber } = searchParams
|
||||
|
||||
return (
|
||||
<div className={styles.main}>
|
||||
<Header confirmationNumber={confirmationNumber} />
|
||||
<Details confirmationNumber={confirmationNumber} />
|
||||
<TotalPrice confirmationNumber={confirmationNumber} />
|
||||
</div>
|
||||
<main className={styles.main}>
|
||||
<Suspense fallback={<LoadingSpinner fullPage />}>
|
||||
<Header confirmationNumber={searchParams.confirmationNumber} />
|
||||
<div className={styles.booking}>
|
||||
<Rooms confirmationNumber={searchParams.confirmationNumber} />
|
||||
<PaymentDetails
|
||||
confirmationNumber={searchParams.confirmationNumber}
|
||||
/>
|
||||
<Divider color="primaryLightSubtle" />
|
||||
<HotelDetails confirmationNumber={searchParams.confirmationNumber} />
|
||||
<Promos />
|
||||
<div className={styles.mobileReceipt}>
|
||||
<Receipt confirmationNumber={searchParams.confirmationNumber} />
|
||||
</div>
|
||||
</div>
|
||||
<aside className={styles.aside}>
|
||||
<SidePanel variant="receipt">
|
||||
<Receipt confirmationNumber={searchParams.confirmationNumber} />
|
||||
</SidePanel>
|
||||
</aside>
|
||||
</Suspense>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
.layout {
|
||||
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||
min-height: 100dvh;
|
||||
padding: 80px 0 160px;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { notFound } from "next/navigation"
|
||||
|
||||
import { env } from "@/env/server"
|
||||
|
||||
import styles from "./layout.module.css"
|
||||
|
||||
// route groups needed as layouts have different bgc
|
||||
export default function ConfirmedBookingLayout({
|
||||
children,
|
||||
}: React.PropsWithChildren) {
|
||||
return <div className={styles.layout}>{children}</div>
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import LoadingSpinner from "@/components/LoadingSpinner"
|
||||
|
||||
export default function Loading() {
|
||||
return <LoadingSpinner fullPage />
|
||||
}
|
||||
@@ -1,7 +1,3 @@
|
||||
import { notFound } from "next/navigation"
|
||||
|
||||
import { env } from "@/env/server"
|
||||
|
||||
import styles from "./layout.module.css"
|
||||
|
||||
import { LangParams, LayoutArgs } from "@/types/params"
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
import { notFound } from "next/navigation"
|
||||
|
||||
import { env } from "@/env/server"
|
||||
|
||||
import styles from "./layout.module.css"
|
||||
|
||||
import { LangParams, LayoutArgs } from "@/types/params"
|
||||
@@ -12,9 +8,6 @@ export default function HotelReservationLayout({
|
||||
}: React.PropsWithChildren<LayoutArgs<LangParams>> & {
|
||||
sidePeek: React.ReactNode
|
||||
}) {
|
||||
if (!env.ENABLE_BOOKING_FLOW) {
|
||||
return notFound()
|
||||
}
|
||||
return (
|
||||
<div className={styles.layout}>
|
||||
{children}
|
||||
|
||||
@@ -17,6 +17,7 @@ import { setLang } from "@/i18n/serverContext"
|
||||
|
||||
import { fetchAvailableHotels, getFiltersFromHotels } from "../../utils"
|
||||
|
||||
import type { HotelData } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps"
|
||||
import type { SelectHotelSearchParams } from "@/types/components/hotelReservation/selectHotel/selectHotelSearchParams"
|
||||
import {
|
||||
TrackingChannelEnum,
|
||||
@@ -59,6 +60,10 @@ export default async function SelectHotelMapPage({
|
||||
children,
|
||||
})
|
||||
|
||||
const validHotels = hotels.filter(
|
||||
(hotel): hotel is HotelData => hotel !== null
|
||||
)
|
||||
|
||||
const arrivalDate = new Date(searchParams.fromDate)
|
||||
const departureDate = new Date(searchParams.toDate)
|
||||
|
||||
@@ -85,15 +90,15 @@ export default async function SelectHotelMapPage({
|
||||
leadTime: differenceInCalendarDays(arrivalDate, new Date()),
|
||||
searchType: "destination",
|
||||
bookingTypeofDay: isWeekend(arrivalDate) ? "weekend" : "weekday",
|
||||
country: hotels?.[0].hotelData.address.country,
|
||||
region: hotels?.[0].hotelData.address.city,
|
||||
country: validHotels?.[0].hotelData.address.country,
|
||||
region: validHotels?.[0].hotelData.address.city,
|
||||
}
|
||||
|
||||
const hotelPins = getHotelPins(hotels)
|
||||
const filterList = getFiltersFromHotels(hotels)
|
||||
const hotelPins = getHotelPins(validHotels)
|
||||
const filterList = getFiltersFromHotels(validHotels)
|
||||
const cityCoordinates = await getCityCoordinates({
|
||||
city: city.name,
|
||||
hotel: { address: hotels[0].hotelData.address.streetAddress },
|
||||
hotel: { address: hotels?.[0]?.hotelData?.address.streetAddress },
|
||||
})
|
||||
|
||||
return (
|
||||
@@ -102,7 +107,7 @@ export default async function SelectHotelMapPage({
|
||||
apiKey={googleMapsApiKey}
|
||||
hotelPins={hotelPins}
|
||||
mapId={googleMapId}
|
||||
hotels={hotels}
|
||||
hotels={validHotels}
|
||||
filterList={filterList}
|
||||
cityCoordinates={cityCoordinates}
|
||||
/>
|
||||
|
||||
@@ -36,6 +36,7 @@ import { setLang } from "@/i18n/serverContext"
|
||||
|
||||
import styles from "./page.module.css"
|
||||
|
||||
import type { HotelData } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps"
|
||||
import type { SelectHotelSearchParams } from "@/types/components/hotelReservation/selectHotel/selectHotelSearchParams"
|
||||
import {
|
||||
TrackingChannelEnum,
|
||||
@@ -69,6 +70,14 @@ export default async function SelectHotelPage({
|
||||
const selectHotelParams = new URLSearchParams(searchParams)
|
||||
const selectHotelParamsObject =
|
||||
getHotelReservationQueryParams(selectHotelParams)
|
||||
|
||||
if (
|
||||
!selectHotelParamsObject.room ||
|
||||
selectHotelParamsObject.room.length === 0
|
||||
) {
|
||||
return notFound()
|
||||
}
|
||||
|
||||
const adults = selectHotelParamsObject.room[0].adults // TODO: Handle multiple rooms
|
||||
const child = selectHotelParamsObject.room[0].child // TODO: Handle multiple rooms
|
||||
const children = child ? generateChildrenString(child) : undefined
|
||||
@@ -84,7 +93,11 @@ export default async function SelectHotelPage({
|
||||
const arrivalDate = new Date(searchParams.fromDate)
|
||||
const departureDate = new Date(searchParams.toDate)
|
||||
|
||||
const filterList = getFiltersFromHotels(hotels)
|
||||
const validHotels = hotels.filter(
|
||||
(hotel): hotel is HotelData => hotel !== null
|
||||
)
|
||||
|
||||
const filterList = getFiltersFromHotels(validHotels)
|
||||
const breadcrumbs = [
|
||||
{
|
||||
title: intl.formatMessage({ id: "Home" }),
|
||||
@@ -132,8 +145,8 @@ export default async function SelectHotelPage({
|
||||
leadTime: differenceInCalendarDays(arrivalDate, new Date()),
|
||||
searchType: "destination",
|
||||
bookingTypeofDay: isWeekend(arrivalDate) ? "weekend" : "weekday",
|
||||
country: hotels?.[0].hotelData.address.country,
|
||||
region: hotels?.[0].hotelData.address.city,
|
||||
country: validHotels?.[0].hotelData.address.country,
|
||||
region: validHotels?.[0].hotelData.address.city,
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -206,7 +219,7 @@ export default async function SelectHotelPage({
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
<HotelCardListing hotelData={hotels} />
|
||||
<HotelCardListing hotelData={validHotels} />
|
||||
</div>
|
||||
<TrackingSDK
|
||||
pageData={pageTrackingData}
|
||||
|
||||
@@ -4,7 +4,10 @@ import { serverClient } from "@/lib/trpc/server"
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
|
||||
import type { AvailabilityInput } from "@/types/components/hotelReservation/selectHotel/availabilityInput"
|
||||
import type { HotelData } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps"
|
||||
import type {
|
||||
HotelData,
|
||||
NullableHotelData,
|
||||
} from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps"
|
||||
import type {
|
||||
CategorizedFilters,
|
||||
Filter,
|
||||
@@ -30,10 +33,10 @@ const hotelFacilitiesFilterNames = [
|
||||
|
||||
export async function fetchAvailableHotels(
|
||||
input: AvailabilityInput
|
||||
): Promise<HotelData[]> {
|
||||
): Promise<NullableHotelData[]> {
|
||||
const availableHotels = await serverClient().hotel.availability.hotels(input)
|
||||
|
||||
if (!availableHotels) throw new Error()
|
||||
if (!availableHotels) return []
|
||||
|
||||
const language = getLang()
|
||||
|
||||
@@ -43,7 +46,7 @@ export async function fetchAvailableHotels(
|
||||
language,
|
||||
})
|
||||
|
||||
if (!hotelData) throw new Error()
|
||||
if (!hotelData) return { hotelData: null, price: hotel.productType }
|
||||
|
||||
return {
|
||||
hotelData: hotelData.data.attributes,
|
||||
@@ -55,7 +58,13 @@ export async function fetchAvailableHotels(
|
||||
}
|
||||
|
||||
export function getFiltersFromHotels(hotels: HotelData[]): CategorizedFilters {
|
||||
const filters = hotels.flatMap((hotel) => hotel.hotelData.detailedFacilities)
|
||||
if (hotels.length === 0)
|
||||
return { facilityFilters: [], surroundingsFilters: [] }
|
||||
|
||||
const filters = hotels.flatMap((hotel) => {
|
||||
if (!hotel.hotelData) return []
|
||||
return hotel.hotelData.detailedFacilities
|
||||
})
|
||||
|
||||
const uniqueFilterIds = [...new Set(filters.map((filter) => filter.id))]
|
||||
const filterList: Filter[] = uniqueFilterIds
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
}
|
||||
|
||||
.content {
|
||||
margin: var(--Spacing-x3) var(--Spacing-x2) 0;
|
||||
width: var(--max-width-page);
|
||||
margin: var(--Spacing-x3) auto 0;
|
||||
}
|
||||
|
||||
.summary {
|
||||
@@ -16,14 +17,17 @@
|
||||
|
||||
@media screen and (min-width: 1367px) {
|
||||
.container {
|
||||
width: var(--max-width-page);
|
||||
|
||||
grid-template-columns: 1fr 340px;
|
||||
grid-template-rows: auto 1fr;
|
||||
margin: var(--Spacing-x5) auto 0;
|
||||
/* simulates padding on viewport smaller than --max-width-navigation */
|
||||
width: min(
|
||||
calc(100dvw - (var(--Spacing-x2) * 2)),
|
||||
var(--max-width-navigation)
|
||||
);
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
margin: var(--Spacing-x3) 0 0;
|
||||
}
|
||||
|
||||
.summary {
|
||||
|
||||
@@ -19,7 +19,8 @@ import HistoryStateManager from "@/components/HotelReservation/EnterDetails/Hist
|
||||
import Payment from "@/components/HotelReservation/EnterDetails/Payment"
|
||||
import SectionAccordion from "@/components/HotelReservation/EnterDetails/SectionAccordion"
|
||||
import SelectedRoom from "@/components/HotelReservation/EnterDetails/SelectedRoom"
|
||||
import Summary from "@/components/HotelReservation/EnterDetails/Summary"
|
||||
import DesktopSummary from "@/components/HotelReservation/EnterDetails/Summary/Desktop"
|
||||
import MobileSummary from "@/components/HotelReservation/EnterDetails/Summary/Mobile"
|
||||
import {
|
||||
generateChildrenString,
|
||||
getQueryParamsForEnterDetails,
|
||||
@@ -182,6 +183,13 @@ export default async function StepPage({
|
||||
//lowestRoomPrice:
|
||||
}
|
||||
|
||||
const summary = {
|
||||
cancellationText: roomAvailability.cancellationText,
|
||||
isMember: !!user,
|
||||
rateDetails: roomAvailability.rateDetails,
|
||||
roomType: roomAvailability.selectedRoom.roomType,
|
||||
}
|
||||
|
||||
return (
|
||||
<EnterDetailsProvider
|
||||
bedTypes={roomAvailability.bedTypes}
|
||||
@@ -262,16 +270,8 @@ export default async function StepPage({
|
||||
</section>
|
||||
</div>
|
||||
<aside className={styles.summary}>
|
||||
<Summary
|
||||
adults={adults}
|
||||
fromDate={fromDate}
|
||||
hotelId={hotelId}
|
||||
kids={children}
|
||||
packageCodes={packageCodes}
|
||||
rateCode={rateCode}
|
||||
roomTypeCode={roomTypeCode}
|
||||
toDate={toDate}
|
||||
/>
|
||||
<MobileSummary {...summary} />
|
||||
<DesktopSummary {...summary} />
|
||||
</aside>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
12
app/[lang]/(live)/(public)/hotelreservation/layout.tsx
Normal file
12
app/[lang]/(live)/(public)/hotelreservation/layout.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import { notFound } from "next/navigation"
|
||||
|
||||
import { env } from "@/env/server"
|
||||
|
||||
export default function HotelReservationLayout({
|
||||
children,
|
||||
}: React.PropsWithChildren) {
|
||||
if (!env.ENABLE_BOOKING_FLOW) {
|
||||
return notFound()
|
||||
}
|
||||
return <>{children}</>
|
||||
}
|
||||
Reference in New Issue
Block a user