feat(SW-344): Hotel list in mobile

This commit is contained in:
Pontus Dreij
2024-11-12 09:19:11 +01:00
parent 169751c5a6
commit b73421dbde
10 changed files with 146 additions and 47 deletions

View File

@@ -3,6 +3,7 @@ import { notFound } from "next/navigation"
import { env } from "@/env/server" import { env } from "@/env/server"
import { getLocations } from "@/lib/trpc/memoizedRequests" import { getLocations } from "@/lib/trpc/memoizedRequests"
import { getHotelPins } from "@/components/HotelReservation/HotelCardDialogListing/utils"
import SelectHotelMap from "@/components/HotelReservation/SelectHotel/SelectHotelMap" import SelectHotelMap from "@/components/HotelReservation/SelectHotel/SelectHotelMap"
import { import {
generateChildrenString, generateChildrenString,
@@ -11,11 +12,7 @@ import {
import { MapModal } from "@/components/MapModal" import { MapModal } from "@/components/MapModal"
import { setLang } from "@/i18n/serverContext" import { setLang } from "@/i18n/serverContext"
import { import { fetchAvailableHotels, getCentralCoordinates } from "../../utils"
fetchAvailableHotels,
getCentralCoordinates,
getHotelPins,
} from "../../utils"
import type { SelectHotelSearchParams } from "@/types/components/hotelReservation/selectHotel/selectHotelSearchParams" import type { SelectHotelSearchParams } from "@/types/components/hotelReservation/selectHotel/selectHotelSearchParams"
import type { LangParams, PageArgs } from "@/types/params" import type { LangParams, PageArgs } from "@/types/params"

View File

@@ -88,25 +88,6 @@ export function getFiltersFromHotels(hotels: HotelData[]): CategorizedFilters {
) )
} }
export function getHotelPins(hotels: HotelData[]): HotelPin[] {
return hotels.map((hotel) => ({
coordinates: {
lat: hotel.hotelData.location.latitude,
lng: hotel.hotelData.location.longitude,
},
name: hotel.hotelData.name,
publicPrice: hotel.price?.regularAmount ?? null,
memberPrice: hotel.price?.memberAmount ?? null,
currency: hotel.price?.currency || null,
images: [
hotel.hotelData.hotelContent.images,
...(hotel.hotelData.gallery?.heroImages ?? []),
],
amenities: hotel.hotelData.detailedFacilities.slice(0, 3),
ratings: hotel.hotelData.ratings?.tripAdvisor.rating ?? null,
}))
}
export function getCentralCoordinates(hotels: HotelPin[]) { export function getCentralCoordinates(hotels: HotelPin[]) {
const centralCoordinates = hotels.reduce( const centralCoordinates = hotels.reduce(
(acc, pin) => { (acc, pin) => {

View File

@@ -17,13 +17,13 @@ import styles from "./hotelCardDialog.module.css"
import type { HotelCardDialogProps } from "@/types/components/hotelReservation/selectHotel/map" import type { HotelCardDialogProps } from "@/types/components/hotelReservation/selectHotel/map"
export default function HotelCardDialog({ export default function HotelCardDialog({
pin, data,
isOpen, isOpen,
handleClose, handleClose,
}: HotelCardDialogProps) { }: HotelCardDialogProps) {
const intl = useIntl() const intl = useIntl()
if (!pin) { if (!data) {
return null return null
} }
@@ -35,7 +35,7 @@ export default function HotelCardDialog({
amenities, amenities,
images, images,
ratings, ratings,
} = pin } = data
const firstImage = images[0]?.imageSizes?.small const firstImage = images[0]?.imageSizes?.small
const altText = images[0]?.metaData?.altText const altText = images[0]?.metaData?.altText

View File

@@ -0,0 +1,47 @@
"use client"
import { useEffect, useRef } from "react"
import HotelCardDialog from "../HotelCardDialog"
import { getHotelPins } from "./utils"
import type { HotelData } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps"
export default function HotelCardDialogListing({
hotels,
activeCard,
}: {
hotels: HotelData[]
activeCard: string | null | undefined
}) {
const hotelsPinData = getHotelPins(hotels)
const activeCardRef = useRef<HTMLDivElement | null>(null)
useEffect(() => {
if (activeCardRef.current) {
activeCardRef.current.scrollIntoView({
behavior: "smooth",
block: "nearest",
inline: "center",
})
}
}, [activeCard])
return (
<>
{hotelsPinData?.length &&
hotelsPinData.map((data) => {
const isActive = data.name === activeCard
return (
<div key={data.name} ref={isActive ? activeCardRef : null}>
<HotelCardDialog
data={data}
isOpen={!!activeCard}
handleClose={() => {}}
/>
</div>
)
})}
</>
)
}

View File

@@ -0,0 +1,21 @@
import type { HotelData } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps"
import type { HotelPin } from "@/types/components/hotelReservation/selectHotel/map"
export function getHotelPins(hotels: HotelData[]): HotelPin[] {
return hotels.map((hotel) => ({
coordinates: {
lat: hotel.hotelData.location.latitude,
lng: hotel.hotelData.location.longitude,
},
name: hotel.hotelData.name,
publicPrice: hotel.price?.regularAmount ?? null,
memberPrice: hotel.price?.memberAmount ?? null,
currency: hotel.price?.currency || null,
images: [
hotel.hotelData.hotelContent.images,
...(hotel.hotelData.gallery?.heroImages ?? []),
],
amenities: hotel.hotelData.detailedFacilities.slice(0, 3),
ratings: hotel.hotelData.ratings?.tripAdvisor.rating ?? null,
}))
}

View File

@@ -2,6 +2,37 @@
display: none; display: none;
} }
.hotelListingMobile {
display: none;
align-items: flex-end;
overflow-x: auto;
position: absolute;
bottom: 0px;
left: 0;
right: 0;
z-index: 10;
height: 350px;
gap: var(--Spacing-x1);
}
.hotelListingMobile[data-open="true"] {
display: flex;
}
.hotelListingMobile dialog {
position: relative;
padding: 0;
margin: 0;
}
.hotelListingMobile > div:first-child {
margin-left: 16px;
}
.hotelListingMobile > div:last-child {
margin-right: 16px;
}
@media (min-width: 768px) { @media (min-width: 768px) {
.hotelListing { .hotelListing {
display: block; display: block;
@@ -9,4 +40,9 @@
overflow-y: auto; overflow-y: auto;
padding-top: var(--Spacing-x2); padding-top: var(--Spacing-x2);
} }
.hotelListingMobile,
.hotelListingMobile[data-open="true"] {
display: none;
}
} }

View File

@@ -1,5 +1,6 @@
"use client" "use client"
import HotelCardDialogListing from "@/components/HotelReservation/HotelCardDialogListing"
import HotelCardListing from "@/components/HotelReservation/HotelCardListing" import HotelCardListing from "@/components/HotelReservation/HotelCardListing"
import styles from "./hotelListing.module.css" import styles from "./hotelListing.module.css"
@@ -13,13 +14,18 @@ export default function HotelListing({
onHotelCardHover, onHotelCardHover,
}: HotelListingProps) { }: HotelListingProps) {
return ( return (
<div className={styles.hotelListing}> <>
<HotelCardListing <div className={styles.hotelListing}>
hotelData={hotels} <HotelCardListing
type={HotelCardListingTypeEnum.MapListing} hotelData={hotels}
activeCard={activeHotelPin} type={HotelCardListingTypeEnum.MapListing}
onHotelCardHover={onHotelCardHover} activeCard={activeHotelPin}
/> onHotelCardHover={onHotelCardHover}
</div> />
</div>
<div className={styles.hotelListingMobile} data-open={!!activeHotelPin}>
<HotelCardDialogListing hotels={hotels} activeCard={activeHotelPin} />
</div>
</>
) )
} }

View File

@@ -8,6 +8,10 @@
min-width: 109px !important; /* Overwriting the default width of the @vis.gl/react-google-maps AdvancedMarker */ min-width: 109px !important; /* Overwriting the default width of the @vis.gl/react-google-maps AdvancedMarker */
} }
.dialogContainer {
display: none;
}
.pin { .pin {
position: absolute; position: absolute;
top: 0; top: 0;
@@ -67,3 +71,9 @@
.card.active { .card.active {
display: block; display: block;
} }
@media (min-width: 768px) {
.dialogContainer {
display: block;
}
}

View File

@@ -50,17 +50,18 @@ export default function HotelListingMapContent({
) )
} }
> >
<HotelCardDialog <div className={styles.dialogContainer}>
isOpen={isActiveOrHovered} <HotelCardDialog
handleClose={(event: { stopPropagation: () => void }) => { isOpen={isActiveOrHovered}
event.stopPropagation() handleClose={(event: { stopPropagation: () => void }) => {
if (activeHotelPin === pin.name) { event.stopPropagation()
toggleActiveHotelPin(null) if (activeHotelPin === pin.name) {
} toggleActiveHotelPin(null)
}} }
pin={pin} }}
/> data={pin}
/>
</div>
<span <span
className={`${styles.pin} ${isActiveOrHovered ? styles.active : ""}`} className={`${styles.pin} ${isActiveOrHovered ? styles.active : ""}`}
> >

View File

@@ -50,6 +50,6 @@ export interface HotelListingMapContentProps {
export interface HotelCardDialogProps { export interface HotelCardDialogProps {
isOpen: boolean isOpen: boolean
pin: HotelPin data: HotelPin
handleClose: (event: { stopPropagation: () => void }) => void handleClose: (event: { stopPropagation: () => void }) => void
} }