fix(SW-1111) Moved map to a page instead of intercepted route
This commit is contained in:
@@ -1,5 +0,0 @@
|
||||
import LoadingSpinner from "@/components/LoadingSpinner"
|
||||
|
||||
export default function LoadingModal() {
|
||||
return <LoadingSpinner />
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
import { notFound } from "next/navigation"
|
||||
|
||||
import { env } from "@/env/server"
|
||||
import { getCityCoordinates, getLocations } from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import { getHotelPins } from "@/components/HotelReservation/HotelCardDialogListing/utils"
|
||||
import SelectHotelMap from "@/components/HotelReservation/SelectHotel/SelectHotelMap"
|
||||
import {
|
||||
generateChildrenString,
|
||||
getHotelReservationQueryParams,
|
||||
} from "@/components/HotelReservation/SelectRate/RoomSelection/utils"
|
||||
import { MapModal } from "@/components/MapModal"
|
||||
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 type { LangParams, PageArgs } from "@/types/params"
|
||||
|
||||
export default async function SelectHotelMapPage({
|
||||
params,
|
||||
searchParams,
|
||||
}: PageArgs<LangParams, SelectHotelSearchParams>) {
|
||||
setLang(params.lang)
|
||||
const locations = await getLocations()
|
||||
|
||||
if (!locations || "error" in locations) {
|
||||
return null
|
||||
}
|
||||
const city = locations.data.find(
|
||||
(location) =>
|
||||
location.name.toLowerCase() === searchParams.city.toLowerCase()
|
||||
)
|
||||
if (!city) return notFound()
|
||||
|
||||
const googleMapId = env.GOOGLE_DYNAMIC_MAP_ID
|
||||
const googleMapsApiKey = env.GOOGLE_STATIC_MAP_KEY
|
||||
|
||||
const selectHotelParams = new URLSearchParams(searchParams)
|
||||
const selectHotelParamsObject =
|
||||
getHotelReservationQueryParams(selectHotelParams)
|
||||
const adults = selectHotelParamsObject.room[0].adults // TODO: Handle multiple rooms
|
||||
const children = selectHotelParamsObject.room[0].child
|
||||
? generateChildrenString(selectHotelParamsObject.room[0].child)
|
||||
: undefined // TODO: Handle multiple rooms
|
||||
|
||||
const hotels = await fetchAvailableHotels({
|
||||
cityId: city.id,
|
||||
roomStayStartDate: searchParams.fromDate,
|
||||
roomStayEndDate: searchParams.toDate,
|
||||
adults,
|
||||
children,
|
||||
})
|
||||
|
||||
const validHotels = hotels.filter(
|
||||
(hotel): hotel is HotelData => hotel !== null
|
||||
)
|
||||
|
||||
const hotelPins = getHotelPins(validHotels)
|
||||
const filterList = getFiltersFromHotels(validHotels)
|
||||
const cityCoordinates = await getCityCoordinates({
|
||||
city: city.name,
|
||||
hotel: { address: hotels?.[0]?.hotelData?.address.streetAddress },
|
||||
})
|
||||
|
||||
return (
|
||||
<MapModal>
|
||||
<SelectHotelMap
|
||||
apiKey={googleMapsApiKey}
|
||||
hotelPins={hotelPins}
|
||||
mapId={googleMapId}
|
||||
hotels={validHotels}
|
||||
filterList={filterList}
|
||||
cityCoordinates={cityCoordinates}
|
||||
/>
|
||||
</MapModal>
|
||||
)
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
export default function Default() {
|
||||
return null
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
export default function Page() {
|
||||
return null
|
||||
}
|
||||
@@ -2,4 +2,11 @@
|
||||
min-height: 100dvh;
|
||||
background-color: var(--Base-Background-Primary-Normal);
|
||||
position: relative;
|
||||
z-index: var(--header-z-index);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.layout {
|
||||
z-index: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,6 @@ import { LangParams, LayoutArgs } from "@/types/params"
|
||||
|
||||
export default function HotelReservationLayout({
|
||||
children,
|
||||
modal,
|
||||
}: React.PropsWithChildren<
|
||||
LayoutArgs<LangParams> & { modal: React.ReactNode }
|
||||
>) {
|
||||
return (
|
||||
<div className={styles.layout}>
|
||||
{children}
|
||||
{modal}
|
||||
</div>
|
||||
)
|
||||
}: React.PropsWithChildren<LayoutArgs<LangParams>>) {
|
||||
return <div className={styles.layout}>{children}</div>
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
"use client"
|
||||
|
||||
import { useEffect } from "react"
|
||||
|
||||
import styles from "../layout.module.css"
|
||||
|
||||
import { LangParams, LayoutArgs } from "@/types/params"
|
||||
|
||||
export default function HotelReservationLayout({
|
||||
children,
|
||||
}: React.PropsWithChildren<LayoutArgs<LangParams>>) {
|
||||
useEffect(() => {
|
||||
document.body.style.overflow = "hidden"
|
||||
|
||||
return () => {
|
||||
document.body.style.overflow = ""
|
||||
}
|
||||
}, [])
|
||||
return <div className={styles.layout}>{children}</div>
|
||||
}
|
||||
@@ -1 +1,79 @@
|
||||
export { default } from "../@modal/(.)map/page"
|
||||
import { notFound } from "next/navigation"
|
||||
|
||||
import { env } from "@/env/server"
|
||||
import { getCityCoordinates, getLocations } from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import { getHotelPins } from "@/components/HotelReservation/HotelCardDialogListing/utils"
|
||||
import SelectHotelMap from "@/components/HotelReservation/SelectHotel/SelectHotelMap"
|
||||
import {
|
||||
generateChildrenString,
|
||||
getHotelReservationQueryParams,
|
||||
} from "@/components/HotelReservation/SelectRate/RoomSelection/utils"
|
||||
import { MapContainer } from "@/components/MapContainer"
|
||||
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 type { LangParams, PageArgs } from "@/types/params"
|
||||
|
||||
export default async function SelectHotelMapPage({
|
||||
params,
|
||||
searchParams,
|
||||
}: PageArgs<LangParams, SelectHotelSearchParams>) {
|
||||
setLang(params.lang)
|
||||
const locations = await getLocations()
|
||||
|
||||
if (!locations || "error" in locations) {
|
||||
return null
|
||||
}
|
||||
const city = locations.data.find(
|
||||
(location) =>
|
||||
location.name.toLowerCase() === searchParams.city.toLowerCase()
|
||||
)
|
||||
if (!city) return notFound()
|
||||
|
||||
const googleMapId = env.GOOGLE_DYNAMIC_MAP_ID
|
||||
const googleMapsApiKey = env.GOOGLE_STATIC_MAP_KEY
|
||||
|
||||
const selectHotelParams = new URLSearchParams(searchParams)
|
||||
const selectHotelParamsObject =
|
||||
getHotelReservationQueryParams(selectHotelParams)
|
||||
const adults = selectHotelParamsObject.room[0].adults // TODO: Handle multiple rooms
|
||||
const children = selectHotelParamsObject.room[0].child
|
||||
? generateChildrenString(selectHotelParamsObject.room[0].child)
|
||||
: undefined // TODO: Handle multiple rooms
|
||||
|
||||
const hotels = await fetchAvailableHotels({
|
||||
cityId: city.id,
|
||||
roomStayStartDate: searchParams.fromDate,
|
||||
roomStayEndDate: searchParams.toDate,
|
||||
adults,
|
||||
children,
|
||||
})
|
||||
|
||||
const validHotels = hotels.filter(
|
||||
(hotel): hotel is HotelData => hotel !== null
|
||||
)
|
||||
|
||||
const hotelPins = getHotelPins(validHotels)
|
||||
const filterList = getFiltersFromHotels(validHotels)
|
||||
const cityCoordinates = await getCityCoordinates({
|
||||
city: city.name,
|
||||
hotel: { address: hotels?.[0]?.hotelData?.address.streetAddress },
|
||||
})
|
||||
|
||||
return (
|
||||
<MapContainer>
|
||||
<SelectHotelMap
|
||||
apiKey={googleMapsApiKey}
|
||||
hotelPins={hotelPins}
|
||||
mapId={googleMapId}
|
||||
hotels={validHotels}
|
||||
filterList={filterList}
|
||||
cityCoordinates={cityCoordinates}
|
||||
/>
|
||||
</MapContainer>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import "@scandic-hotels/design-system/style.css"
|
||||
|
||||
import Script from "next/script"
|
||||
|
||||
import { env } from "@/env/server"
|
||||
import TrpcProvider from "@/lib/trpc/Provider"
|
||||
|
||||
import TokenRefresher from "@/components/Auth/TokenRefresher"
|
||||
|
||||
@@ -23,7 +23,7 @@ export default function List({
|
||||
getItemProps={getItemProps}
|
||||
highlightedIndex={highlightedIndex}
|
||||
index={initialIndex + index}
|
||||
key={location.id}
|
||||
key={location.id + index}
|
||||
location={location}
|
||||
/>
|
||||
))}
|
||||
|
||||
@@ -26,6 +26,7 @@ import type { SearchProps } from "@/types/components/search"
|
||||
import type { Location } from "@/types/trpc/routers/hotel/locations"
|
||||
|
||||
const name = "search"
|
||||
|
||||
export default function Search({ locations }: SearchProps) {
|
||||
const { register, setValue, trigger, unregister } =
|
||||
useFormContext<BookingWidgetSchema>()
|
||||
@@ -166,7 +167,6 @@ export default function Search({ locations }: SearchProps) {
|
||||
onInputValueChange={(inputValue) => dispatchInputValue(inputValue)}
|
||||
>
|
||||
{({
|
||||
closeMenu,
|
||||
getInputProps,
|
||||
getItemProps,
|
||||
getLabelProps,
|
||||
@@ -207,9 +207,6 @@ export default function Search({ locations }: SearchProps) {
|
||||
id: "Destinations & hotels",
|
||||
}),
|
||||
...register(name, {
|
||||
onBlur: function () {
|
||||
closeMenu()
|
||||
},
|
||||
onChange: handleOnChange,
|
||||
}),
|
||||
type: "search",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client"
|
||||
import { APIProvider } from "@vis.gl/react-google-maps"
|
||||
import { useRouter, useSearchParams } from "next/navigation"
|
||||
import { useSearchParams } from "next/navigation"
|
||||
import { useEffect, useRef, useState } from "react"
|
||||
import { useIntl } from "react-intl"
|
||||
import { useMediaQuery } from "usehooks-ts"
|
||||
@@ -11,6 +11,7 @@ import { CloseIcon, CloseLargeIcon } from "@/components/Icons"
|
||||
import InteractiveMap from "@/components/Maps/InteractiveMap"
|
||||
import { BackToTopButton } from "@/components/TempDesignSystem/BackToTopButton"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import useLang from "@/hooks/useLang"
|
||||
|
||||
import FilterAndSortModal from "../FilterAndSortModal"
|
||||
@@ -29,7 +30,7 @@ export default function SelectHotelMap({
|
||||
cityCoordinates,
|
||||
}: SelectHotelMapProps) {
|
||||
const searchParams = useSearchParams()
|
||||
const router = useRouter()
|
||||
|
||||
const lang = useLang()
|
||||
const intl = useIntl()
|
||||
const isAboveMobile = useMediaQuery("(min-width: 768px)")
|
||||
@@ -82,25 +83,18 @@ export default function SelectHotelMap({
|
||||
hotelListingElement?.scrollTo({ top: 0, behavior: "smooth" })
|
||||
}
|
||||
|
||||
function handlePageRedirect() {
|
||||
const newUrl = `${selectHotel(lang)}?${searchParams.toString()}`
|
||||
if (window.history.length > 1) {
|
||||
router.back()
|
||||
} else {
|
||||
router.push(newUrl)
|
||||
}
|
||||
}
|
||||
|
||||
const closeButton = (
|
||||
<Button
|
||||
intent="inverted"
|
||||
size="small"
|
||||
theme="base"
|
||||
className={styles.closeButton}
|
||||
onClick={handlePageRedirect}
|
||||
asChild
|
||||
>
|
||||
<CloseIcon color="burgundy" />
|
||||
{intl.formatMessage({ id: "Close the map" })}
|
||||
<Link href={selectHotel(lang)} keepSearchParams>
|
||||
<CloseIcon color="burgundy" />
|
||||
{intl.formatMessage({ id: "Close the map" })}
|
||||
</Link>
|
||||
</Button>
|
||||
)
|
||||
return (
|
||||
@@ -113,10 +107,12 @@ export default function SelectHotelMap({
|
||||
size="small"
|
||||
variant="icon"
|
||||
wrapping
|
||||
onClick={handlePageRedirect}
|
||||
className={styles.filterContainerCloseButton}
|
||||
asChild
|
||||
>
|
||||
<CloseLargeIcon />
|
||||
<Link href={selectHotel(lang)} keepSearchParams>
|
||||
<CloseLargeIcon />
|
||||
</Link>
|
||||
</Button>
|
||||
<FilterAndSortModal filters={filterList} />
|
||||
</div>
|
||||
@@ -126,7 +122,7 @@ export default function SelectHotelMap({
|
||||
setActiveHotelPin={setActiveHotelPin}
|
||||
/>
|
||||
{showBackToTop && (
|
||||
<BackToTopButton position="right" onClick={scrollToTop} />
|
||||
<BackToTopButton position="left" onClick={scrollToTop} />
|
||||
)}
|
||||
</div>
|
||||
<InteractiveMap
|
||||
|
||||
@@ -14,10 +14,6 @@
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 10;
|
||||
background-color: var(--Base-Surface-Secondary-light-Normal);
|
||||
padding: 0 var(--Spacing-x2);
|
||||
height: 44px;
|
||||
|
||||
@@ -1,29 +1,18 @@
|
||||
"use client"
|
||||
|
||||
import { useRouter } from "next/navigation"
|
||||
import { useCallback, useEffect, useRef, useState } from "react"
|
||||
import { Dialog, Modal } from "react-aria-components"
|
||||
|
||||
import { debounce } from "@/utils/debounce"
|
||||
|
||||
import styles from "./mapModal.module.css"
|
||||
|
||||
export function MapModal({ children }: { children: React.ReactNode }) {
|
||||
const router = useRouter()
|
||||
export function MapContainer({ children }: { children: React.ReactNode }) {
|
||||
const [mapHeight, setMapHeight] = useState("0px")
|
||||
const [mapTop, setMapTop] = useState("0px")
|
||||
const [isOpen, setIsOpen] = useState(true)
|
||||
const [scrollHeightWhenOpened, setScrollHeightWhenOpened] = useState(0)
|
||||
|
||||
const rootDiv = useRef<HTMLDivElement | null>(null)
|
||||
|
||||
const handleOnOpenChange = (open: boolean) => {
|
||||
setIsOpen(open)
|
||||
if (!open) {
|
||||
router.back()
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the height of the map based on the viewport height from the start-point (below the header and booking widget)
|
||||
const handleMapHeight = useCallback(() => {
|
||||
const topPosition = rootDiv.current?.getBoundingClientRect().top ?? 0
|
||||
@@ -66,19 +55,17 @@ export function MapModal({ children }: { children: React.ReactNode }) {
|
||||
|
||||
return (
|
||||
<div className={styles.wrapper} ref={rootDiv}>
|
||||
<Modal isOpen={isOpen} onOpenChange={handleOnOpenChange}>
|
||||
<Dialog
|
||||
style={
|
||||
{
|
||||
"--hotel-map-height": mapHeight,
|
||||
"--hotel-map-top": mapTop,
|
||||
} as React.CSSProperties
|
||||
}
|
||||
className={styles.dynamicMap}
|
||||
>
|
||||
{children}
|
||||
</Dialog>
|
||||
</Modal>
|
||||
<div
|
||||
style={
|
||||
{
|
||||
"--hotel-map-height": mapHeight,
|
||||
"--hotel-map-top": mapTop,
|
||||
} as React.CSSProperties
|
||||
}
|
||||
className={styles.dynamicMap}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
.dynamicMap {
|
||||
--hotel-map-height: 100dvh;
|
||||
--hotel-map-top: 0px;
|
||||
position: absolute;
|
||||
position: fixed;
|
||||
top: var(--hotel-map-top);
|
||||
left: 0;
|
||||
height: var(--hotel-map-height);
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
AdvancedMarker,
|
||||
AdvancedMarkerAnchorPoint,
|
||||
} from "@vis.gl/react-google-maps"
|
||||
import { memo, useCallback, useState } from "react"
|
||||
import { useCallback, useState } from "react"
|
||||
|
||||
import HotelCardDialog from "@/components/HotelReservation/HotelCardDialog"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
@@ -84,6 +84,7 @@ function HotelListingMapContent({
|
||||
color={isActiveOrHovered ? "burgundy" : "white"}
|
||||
/>
|
||||
</span>
|
||||
|
||||
<Body
|
||||
asChild
|
||||
color={isActiveOrHovered ? "white" : "baseTextHighContrast"}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
.sitewideAlert {
|
||||
width: 100%;
|
||||
z-index: var(--header-z-index);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.alarm {
|
||||
|
||||
Reference in New Issue
Block a user