Merged in fix/SW-1491-SW-1500-link-in-hotel-card-to-map (pull request #1707)
fix(SW-1491-SW-1500): address on hotel card should go to map, remove link on maplisting view * fix(SW-1491-SW-1500): address on hotel card should go to map, remove link on maplisting view * fix(SW-1491-SW-1500): fix comment * fix(SW-1491-SW-1500): add underscore Approved-by: Niclas Edenvin
This commit is contained in:
@@ -54,13 +54,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.address {
|
.address {
|
||||||
display: none;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.addressMobile {
|
|
||||||
display: block;
|
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
|
color: var(--Text-Tertiary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.facilities {
|
.facilities {
|
||||||
@@ -149,14 +144,6 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pageListing .addressMobile {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pageListing .address {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pageListing .prices {
|
.pageListing .prices {
|
||||||
width: 260px;
|
width: 260px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useParams } from "next/dist/client/components/navigation"
|
import { useParams } from "next/dist/client/components/navigation"
|
||||||
|
import { useRouter, useSearchParams } from "next/navigation"
|
||||||
import { memo, useCallback } from "react"
|
import { memo, useCallback } from "react"
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import { HotelLogo, MaterialIcon } from "@scandic-hotels/design-system/Icons"
|
import { HotelLogo, MaterialIcon } from "@scandic-hotels/design-system/Icons"
|
||||||
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
|
|
||||||
import { selectRate } from "@/constants/routes/hotelReservation"
|
import { selectHotelMap, selectRate } from "@/constants/routes/hotelReservation"
|
||||||
import { useHotelsMapStore } from "@/stores/hotels-map"
|
import { useHotelsMapStore } from "@/stores/hotels-map"
|
||||||
|
|
||||||
import { FacilityToIcon } from "@/components/ContentType/HotelPage/data"
|
import { FacilityToIcon } from "@/components/ContentType/HotelPage/data"
|
||||||
@@ -46,6 +48,8 @@ function HotelCard({
|
|||||||
bookingCode = "",
|
bookingCode = "",
|
||||||
}: HotelCardProps) {
|
}: HotelCardProps) {
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
|
const searchParams = useSearchParams()
|
||||||
|
|
||||||
const lang = params.lang as Lang
|
const lang = params.lang as Lang
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const { setActiveHotelPin, setActiveHotelCard } = useHotelsMapStore()
|
const { setActiveHotelPin, setActiveHotelCard } = useHotelsMapStore()
|
||||||
@@ -60,12 +64,19 @@ function HotelCard({
|
|||||||
}, [setActiveHotelPin, setActiveHotelCard])
|
}, [setActiveHotelPin, setActiveHotelCard])
|
||||||
|
|
||||||
const amenities = hotel.detailedFacilities.slice(0, 5)
|
const amenities = hotel.detailedFacilities.slice(0, 5)
|
||||||
|
const router = useRouter()
|
||||||
const classNames = hotelCardVariants({
|
const classNames = hotelCardVariants({
|
||||||
type,
|
type,
|
||||||
state,
|
state,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const handleAddressClick = (event: React.MouseEvent) => {
|
||||||
|
event.preventDefault()
|
||||||
|
setActiveHotelPin(hotel.name)
|
||||||
|
setActiveHotelCard(hotel.name)
|
||||||
|
router.push(`${selectHotelMap(lang)}?${searchParams.toString()}`)
|
||||||
|
}
|
||||||
|
|
||||||
const addressStr = `${hotel.address.streetAddress}, ${hotel.address.city}`
|
const addressStr = `${hotel.address.streetAddress}, ${hotel.address.city}`
|
||||||
const galleryImages = mapApiImagesToGalleryImages(hotel.galleryImages || [])
|
const galleryImages = mapApiImagesToGalleryImages(hotel.galleryImages || [])
|
||||||
const fullPrice =
|
const fullPrice =
|
||||||
@@ -101,26 +112,29 @@ function HotelCard({
|
|||||||
</Subtitle>
|
</Subtitle>
|
||||||
<div className={styles.addressContainer}>
|
<div className={styles.addressContainer}>
|
||||||
<address className={styles.address}>
|
<address className={styles.address}>
|
||||||
<Caption color="uiTextPlaceholder">{addressStr}</Caption>
|
{type == HotelCardListingTypeEnum.MapListing ? (
|
||||||
</address>
|
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||||
<address className={styles.addressMobile}>
|
<p>{addressStr}</p>
|
||||||
<Caption color="burgundy" type="underline" asChild>
|
</Typography>
|
||||||
|
) : (
|
||||||
<Link
|
<Link
|
||||||
href={`https://www.google.com/maps/dir/?api=1&destination=${hotel.location.latitude},${hotel.location.longitude}`}
|
|
||||||
target="_blank"
|
|
||||||
aria-label={intl.formatMessage({
|
|
||||||
id: "Driving directions",
|
|
||||||
})}
|
|
||||||
title={intl.formatMessage({
|
|
||||||
id: "Driving directions",
|
|
||||||
})}
|
|
||||||
color="burgundy"
|
|
||||||
size="small"
|
size="small"
|
||||||
|
color="burgundy"
|
||||||
|
variant="underscored"
|
||||||
|
onClick={handleAddressClick}
|
||||||
|
href={selectHotelMap(lang)}
|
||||||
|
keepSearchParams
|
||||||
|
aria-label={intl.formatMessage({
|
||||||
|
id: "See on map",
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
{addressStr}
|
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||||
|
<p>{addressStr}</p>
|
||||||
|
</Typography>
|
||||||
</Link>
|
</Link>
|
||||||
</Caption>
|
)}
|
||||||
</address>
|
</address>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<Divider variant="vertical" color="subtle" />
|
<Divider variant="vertical" color="subtle" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { useSearchParams } from "next/navigation"
|
import { useSearchParams } from "next/navigation"
|
||||||
import { useSession } from "next-auth/react"
|
import { useSession } from "next-auth/react"
|
||||||
import { useEffect, useMemo } from "react"
|
import { useEffect, useMemo, useRef } from "react"
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import { useBookingCodeFilterStore } from "@/stores/bookingCode-filter"
|
import { useBookingCodeFilterStore } from "@/stores/bookingCode-filter"
|
||||||
@@ -39,6 +39,7 @@ export default function HotelCardListing({
|
|||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const { activeHotelCard } = useHotelsMapStore()
|
const { activeHotelCard } = useHotelsMapStore()
|
||||||
const { showBackToTop, scrollToTop } = useScrollToTop({ threshold: 490 })
|
const { showBackToTop, scrollToTop } = useScrollToTop({ threshold: 490 })
|
||||||
|
const activeCardRef = useRef<HTMLDivElement | null>(null)
|
||||||
|
|
||||||
const sortBy = searchParams.get("sort") ?? DEFAULT_SORT
|
const sortBy = searchParams.get("sort") ?? DEFAULT_SORT
|
||||||
|
|
||||||
@@ -99,25 +100,47 @@ export default function HotelCardListing({
|
|||||||
isSpecialRate,
|
isSpecialRate,
|
||||||
])
|
])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (activeCardRef.current && type === HotelCardListingTypeEnum.MapListing) {
|
||||||
|
activeCardRef.current.scrollIntoView({
|
||||||
|
behavior: "smooth",
|
||||||
|
block: "nearest",
|
||||||
|
inline: "center",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [activeHotelCard, type])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setResultCount(hotels.length)
|
setResultCount(hotels.length)
|
||||||
}, [hotels, setResultCount])
|
}, [hotels, setResultCount])
|
||||||
|
|
||||||
|
function isHotelActiveInMapView(hotelName: string): boolean {
|
||||||
|
return (
|
||||||
|
hotelName === activeHotelCard &&
|
||||||
|
type === HotelCardListingTypeEnum.MapListing
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={styles.hotelCards}>
|
<section className={styles.hotelCards}>
|
||||||
{hotels?.length
|
{hotels?.length
|
||||||
? hotels.map((hotel) => (
|
? hotels.map((hotel) => (
|
||||||
<div
|
<div
|
||||||
key={hotel.hotel.operaId}
|
key={hotel.hotel.operaId}
|
||||||
|
ref={
|
||||||
|
isHotelActiveInMapView(hotel.hotel.name) ? activeCardRef : null
|
||||||
|
}
|
||||||
data-active={
|
data-active={
|
||||||
hotel.hotel.name === activeHotelCard ? "true" : "false"
|
isHotelActiveInMapView(hotel.hotel.name) ? "true" : "false"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<HotelCard
|
<HotelCard
|
||||||
hotelData={hotel}
|
hotelData={hotel}
|
||||||
isUserLoggedIn={isUserLoggedIn}
|
isUserLoggedIn={isUserLoggedIn}
|
||||||
state={
|
state={
|
||||||
hotel.hotel.name === activeHotelCard ? "active" : "default"
|
isHotelActiveInMapView(hotel.hotel.name)
|
||||||
|
? "active"
|
||||||
|
: "default"
|
||||||
}
|
}
|
||||||
type={type}
|
type={type}
|
||||||
bookingCode={bookingCode}
|
bookingCode={bookingCode}
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
.hotelListing {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hotelListingMobile {
|
.hotelListingMobile {
|
||||||
display: none;
|
display: none;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
@@ -16,16 +12,9 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
.hotelListing {
|
||||||
.hotelListing {
|
display: block;
|
||||||
display: block;
|
width: 100%;
|
||||||
width: 100%;
|
overflow-y: auto;
|
||||||
overflow-y: auto;
|
padding-top: var(--Spacing-x2);
|
||||||
padding-top: var(--Spacing-x2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hotelListingMobile,
|
|
||||||
.hotelListingMobile[data-open="true"] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
|
import { useMediaQuery } from "usehooks-ts"
|
||||||
|
|
||||||
import { useHotelsMapStore } from "@/stores/hotels-map"
|
import { useHotelsMapStore } from "@/stores/hotels-map"
|
||||||
|
|
||||||
import HotelCardDialogListing from "@/components/HotelReservation/HotelCardDialogListing"
|
import HotelCardDialogListing from "@/components/HotelReservation/HotelCardDialogListing"
|
||||||
@@ -12,17 +14,21 @@ import type { HotelListingProps } from "@/types/components/hotelReservation/sele
|
|||||||
|
|
||||||
export default function HotelListing({ hotels }: HotelListingProps) {
|
export default function HotelListing({ hotels }: HotelListingProps) {
|
||||||
const { activeHotelPin } = useHotelsMapStore()
|
const { activeHotelPin } = useHotelsMapStore()
|
||||||
|
const isMobile = useMediaQuery("(max-width: 767px)")
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={styles.hotelListing}>
|
{isMobile ? (
|
||||||
<HotelCardListing
|
<div className={styles.hotelListingMobile} data-open={!!activeHotelPin}>
|
||||||
hotelData={hotels}
|
<HotelCardDialogListing hotels={hotels} />
|
||||||
type={HotelCardListingTypeEnum.MapListing}
|
</div>
|
||||||
/>
|
) : (
|
||||||
</div>
|
<div className={styles.hotelListing}>
|
||||||
<div className={styles.hotelListingMobile} data-open={!!activeHotelPin}>
|
<HotelCardListing
|
||||||
<HotelCardDialogListing hotels={hotels} />
|
hotelData={hotels}
|
||||||
</div>
|
type={HotelCardListingTypeEnum.MapListing}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { useMap } from "@vis.gl/react-google-maps"
|
import { useMap } from "@vis.gl/react-google-maps"
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
|
import { useCallback, useMemo, useRef, useState } from "react"
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
import { useMediaQuery } from "usehooks-ts"
|
import { useMediaQuery } from "usehooks-ts"
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ export default function SelectHotelContent({
|
|||||||
const listingContainerRef = useRef<HTMLDivElement | null>(null)
|
const listingContainerRef = useRef<HTMLDivElement | null>(null)
|
||||||
|
|
||||||
const activeFilters = useHotelFilterStore((state) => state.activeFilters)
|
const activeFilters = useHotelFilterStore((state) => state.activeFilters)
|
||||||
const { activeHotelCard, activeHotelPin } = useHotelsMapStore()
|
const { activeHotelPin } = useHotelsMapStore()
|
||||||
|
|
||||||
const { showBackToTop, scrollToTop } = useScrollToTop({
|
const { showBackToTop, scrollToTop } = useScrollToTop({
|
||||||
threshold: 490,
|
threshold: 490,
|
||||||
@@ -63,23 +63,22 @@ export default function SelectHotelContent({
|
|||||||
(state) => state.activeCodeFilter
|
(state) => state.activeCodeFilter
|
||||||
)
|
)
|
||||||
|
|
||||||
const coordinates = useMemo(
|
const coordinates = useMemo(() => {
|
||||||
() =>
|
if (activeHotelPin) {
|
||||||
isAboveMobile
|
const hotel = hotels.find((hotel) => hotel.hotel.name === activeHotelPin)
|
||||||
? cityCoordinates
|
|
||||||
: { ...cityCoordinates, lat: cityCoordinates.lat - 0.006 },
|
|
||||||
[isAboveMobile, cityCoordinates]
|
|
||||||
)
|
|
||||||
|
|
||||||
useEffect(() => {
|
if (hotel && hotel.hotel.location) {
|
||||||
if (listingContainerRef.current) {
|
return {
|
||||||
const activeElement =
|
lat: hotel.hotel.location.latitude,
|
||||||
listingContainerRef.current.querySelector(`[data-active="true"]`)
|
lng: hotel.hotel.location.longitude,
|
||||||
if (activeElement) {
|
}
|
||||||
activeElement.scrollIntoView({ behavior: "smooth", block: "nearest" })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [activeHotelCard, activeHotelPin])
|
|
||||||
|
return isAboveMobile
|
||||||
|
? cityCoordinates
|
||||||
|
: { ...cityCoordinates, lat: cityCoordinates.lat - 0.006 }
|
||||||
|
}, [activeHotelPin, hotels, isAboveMobile, cityCoordinates])
|
||||||
|
|
||||||
const filteredHotelPins = useMemo(() => {
|
const filteredHotelPins = useMemo(() => {
|
||||||
const updatedHotelsList = bookingCode
|
const updatedHotelsList = bookingCode
|
||||||
|
|||||||
Reference in New Issue
Block a user