Merged in fix/BOOK-130-booking-code-filtering (pull request #2868)
fix(BOOK-130): update booking code filtering on map view and filter and sort modal * fix(BOOK-130): update booking code filtering on map view and filter and sort modal * fix(BOOK-130): change name of filteredIds * fix(BOOK-130): add initial value reduce Approved-by: Joakim Jäderberg Approved-by: Anton Gunnarsson
This commit is contained in:
@@ -3,7 +3,6 @@
|
|||||||
import { useCallback, useEffect, useRef } from "react"
|
import { useCallback, useEffect, useRef } from "react"
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import { useHotelFilterStore } from "../../stores/hotel-filters"
|
|
||||||
import { useHotelsMapStore } from "../../stores/hotels-map"
|
import { useHotelsMapStore } from "../../stores/hotels-map"
|
||||||
import ListingHotelCardDialog from "../ListingHotelCardDialog"
|
import ListingHotelCardDialog from "../ListingHotelCardDialog"
|
||||||
import { getHotelPins } from "./utils"
|
import { getHotelPins } from "./utils"
|
||||||
@@ -14,12 +13,10 @@ import type { HotelResponse } from "../SelectHotel/helpers"
|
|||||||
|
|
||||||
interface HotelCardDialogListingProps {
|
interface HotelCardDialogListingProps {
|
||||||
hotels: HotelResponse[]
|
hotels: HotelResponse[]
|
||||||
unfilteredHotelCount: number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function HotelCardDialogListing({
|
export default function HotelCardDialogListing({
|
||||||
hotels,
|
hotels,
|
||||||
unfilteredHotelCount,
|
|
||||||
}: HotelCardDialogListingProps) {
|
}: HotelCardDialogListingProps) {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const isRedemption = hotels?.find(
|
const isRedemption = hotels?.find(
|
||||||
@@ -37,7 +34,6 @@ export default function HotelCardDialogListing({
|
|||||||
const isScrollingRef = useRef<boolean>(false)
|
const isScrollingRef = useRef<boolean>(false)
|
||||||
const debounceTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
const debounceTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
||||||
const { activeHotel, activate, deactivate } = useHotelsMapStore()
|
const { activeHotel, activate, deactivate } = useHotelsMapStore()
|
||||||
const setResultCount = useHotelFilterStore((state) => state.setResultCount)
|
|
||||||
|
|
||||||
const handleIntersection = useCallback(
|
const handleIntersection = useCallback(
|
||||||
(entries: IntersectionObserverEntry[]) => {
|
(entries: IntersectionObserverEntry[]) => {
|
||||||
@@ -125,10 +121,6 @@ export default function HotelCardDialogListing({
|
|||||||
}
|
}
|
||||||
}, [dialogRef, activeHotel, deactivate])
|
}, [dialogRef, activeHotel, deactivate])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setResultCount(hotels.length, unfilteredHotelCount)
|
|
||||||
}, [hotels, setResultCount, unfilteredHotelCount])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.hotelCardDialogListing} ref={dialogRef}>
|
<div className={styles.hotelCardDialogListing} ref={dialogRef}>
|
||||||
{hotelsPinData?.map((data) => {
|
{hotelsPinData?.map((data) => {
|
||||||
|
|||||||
@@ -38,14 +38,12 @@ export enum HotelCardListingTypeEnum {
|
|||||||
|
|
||||||
type HotelCardListingProps = {
|
type HotelCardListingProps = {
|
||||||
hotelData: HotelResponse[]
|
hotelData: HotelResponse[]
|
||||||
unfilteredHotelCount: number
|
|
||||||
type?: HotelCardListingTypeEnum
|
type?: HotelCardListingTypeEnum
|
||||||
isAlternative?: boolean
|
isAlternative?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function HotelCardListing({
|
export default function HotelCardListing({
|
||||||
hotelData,
|
hotelData,
|
||||||
unfilteredHotelCount,
|
|
||||||
type = HotelCardListingTypeEnum.PageListing,
|
type = HotelCardListingTypeEnum.PageListing,
|
||||||
isAlternative,
|
isAlternative,
|
||||||
}: HotelCardListingProps) {
|
}: HotelCardListingProps) {
|
||||||
@@ -82,6 +80,10 @@ export default function HotelCardListing({
|
|||||||
isBookingCodeRateAvailable &&
|
isBookingCodeRateAvailable &&
|
||||||
activeCodeFilter === BookingCodeFilterEnum.Discounted
|
activeCodeFilter === BookingCodeFilterEnum.Discounted
|
||||||
|
|
||||||
|
const unfilteredHotelCount = showOnlyBookingCodeRates
|
||||||
|
? hotelData.filter((hotel) => hotel.availability.bookingCode).length
|
||||||
|
: hotelData.length
|
||||||
|
|
||||||
const hotels = useMemo(() => {
|
const hotels = useMemo(() => {
|
||||||
const sortedHotels = getSortedHotels({
|
const sortedHotels = getSortedHotels({
|
||||||
hotels: hotelData,
|
hotels: hotelData,
|
||||||
@@ -124,8 +126,10 @@ export default function HotelCardListing({
|
|||||||
}, [activeHotel, type])
|
}, [activeHotel, type])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setResultCount(hotels.length, unfilteredHotelCount)
|
if (type === HotelCardListingTypeEnum.PageListing) {
|
||||||
}, [hotels, setResultCount, unfilteredHotelCount])
|
setResultCount(hotels.length, unfilteredHotelCount)
|
||||||
|
}
|
||||||
|
}, [hotels, setResultCount, type, unfilteredHotelCount])
|
||||||
|
|
||||||
function isHotelActiveInMapView(hotelName: string): boolean {
|
function isHotelActiveInMapView(hotelName: string): boolean {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -227,9 +227,10 @@ export default function FilterAndSortModal({
|
|||||||
defaultMessage: "See results ({ count })",
|
defaultMessage: "See results ({ count })",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
count: filteredCount
|
count:
|
||||||
? filteredCount
|
selectedFilters.length > 0
|
||||||
: unfilteredResultCount,
|
? filteredCount
|
||||||
|
: unfilteredResultCount,
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -43,25 +43,29 @@ export default function FilterContent({
|
|||||||
const [filteredHotelIds, setFilteredHotelIds] = useState<string[]>([])
|
const [filteredHotelIds, setFilteredHotelIds] = useState<string[]>([])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (activeFilters.length) {
|
const allFilters = [
|
||||||
const allFilters = [
|
...filters.facilityFilters,
|
||||||
...filters.facilityFilters,
|
...filters.surroundingsFilters,
|
||||||
...filters.surroundingsFilters,
|
]
|
||||||
]
|
|
||||||
setFilteredHotelIds(
|
const selectedFilters = activeFilters.length
|
||||||
allFilters
|
? allFilters
|
||||||
.filter((f) => activeFilters.includes(f.id.toString()))
|
.filter((f) => activeFilters.includes(f.id.toString()))
|
||||||
.map((f) => f.hotelIds)
|
.map((f) =>
|
||||||
.reduce((accumulatedHotelIds, currentHotelIds) =>
|
showOnlyBookingCodeRates ? f.bookingCodeFilteredIds : f.hotelIds
|
||||||
accumulatedHotelIds.filter((hotelId) =>
|
|
||||||
currentHotelIds.includes(hotelId)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
: []
|
||||||
} else {
|
|
||||||
setFilteredHotelIds([])
|
const filteredIds = selectedFilters.reduce(
|
||||||
}
|
(accumulatedHotelIds, currentHotelIds) =>
|
||||||
}, [filters, activeFilters, setFilteredHotelIds])
|
accumulatedHotelIds.filter((hotelId) =>
|
||||||
|
currentHotelIds.includes(hotelId)
|
||||||
|
),
|
||||||
|
selectedFilters[0] || []
|
||||||
|
)
|
||||||
|
|
||||||
|
setFilteredHotelIds(filteredIds)
|
||||||
|
}, [filters, activeFilters, setFilteredHotelIds, showOnlyBookingCodeRates])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onFilteredCountChange(filteredHotelIds.length)
|
onFilteredCountChange(filteredHotelIds.length)
|
||||||
@@ -74,7 +78,7 @@ export default function FilterContent({
|
|||||||
function filterOutput(filters: HotelFilter[]) {
|
function filterOutput(filters: HotelFilter[]) {
|
||||||
return filters.map((filter) => {
|
return filters.map((filter) => {
|
||||||
const relevantIds = showOnlyBookingCodeRates
|
const relevantIds = showOnlyBookingCodeRates
|
||||||
? filter.filteredIds
|
? filter.bookingCodeFilteredIds
|
||||||
: filter.hotelIds
|
: filter.hotelIds
|
||||||
|
|
||||||
const isSelected = activeFilters.some((f) => f === filter.id.toString())
|
const isSelected = activeFilters.some((f) => f === filter.id.toString())
|
||||||
|
|||||||
@@ -14,29 +14,21 @@ import type { HotelResponse } from "../../helpers"
|
|||||||
|
|
||||||
interface HotelListingProps {
|
interface HotelListingProps {
|
||||||
hotels: HotelResponse[]
|
hotels: HotelResponse[]
|
||||||
unfilteredHotelCount: number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function HotelListing({
|
export default function HotelListing({ hotels }: HotelListingProps) {
|
||||||
hotels,
|
|
||||||
unfilteredHotelCount,
|
|
||||||
}: HotelListingProps) {
|
|
||||||
const { activeHotel } = useHotelsMapStore()
|
const { activeHotel } = useHotelsMapStore()
|
||||||
const isMobile = useMediaQuery("(max-width: 899px)")
|
const isMobile = useMediaQuery("(max-width: 899px)")
|
||||||
|
|
||||||
return isMobile ? (
|
return isMobile ? (
|
||||||
<div className={styles.hotelListingMobile} data-open={!!activeHotel}>
|
<div className={styles.hotelListingMobile} data-open={!!activeHotel}>
|
||||||
<HotelCardDialogListing
|
<HotelCardDialogListing hotels={hotels} />
|
||||||
hotels={hotels}
|
|
||||||
unfilteredHotelCount={unfilteredHotelCount}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className={styles.hotelListing}>
|
<div className={styles.hotelListing}>
|
||||||
<HotelCardListing
|
<HotelCardListing
|
||||||
hotelData={hotels}
|
hotelData={hotels}
|
||||||
type={HotelCardListingTypeEnum.MapListing}
|
type={HotelCardListingTypeEnum.MapListing}
|
||||||
unfilteredHotelCount={unfilteredHotelCount}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useMap } from "@vis.gl/react-google-maps"
|
import { useMap } from "@vis.gl/react-google-maps"
|
||||||
import { useCallback, useMemo, useRef, useState } from "react"
|
import { useCallback, useEffect, 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"
|
||||||
|
|
||||||
@@ -75,6 +75,8 @@ export function SelectHotelMapContent({
|
|||||||
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 setResultCount = useHotelFilterStore((state) => state.setResultCount)
|
||||||
|
|
||||||
const hotelMapStore = useHotelsMapStore()
|
const hotelMapStore = useHotelsMapStore()
|
||||||
|
|
||||||
const { showBackToTop, scrollToTop } = useScrollToTop({
|
const { showBackToTop, scrollToTop } = useScrollToTop({
|
||||||
@@ -193,7 +195,13 @@ export function SelectHotelMapContent({
|
|||||||
const showBookingCodeFilter =
|
const showBookingCodeFilter =
|
||||||
bookingCode && isBookingCodeRateAvailable && !isSpecialRate
|
bookingCode && isBookingCodeRateAvailable && !isSpecialRate
|
||||||
|
|
||||||
const unfilteredHotelCount = hotelPins.length
|
const unfilteredHotelCount = showOnlyBookingCodeRates
|
||||||
|
? hotelPins.filter((hotel) => hotel.bookingCode).length
|
||||||
|
: hotelPins.length
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setResultCount(hotels.length, unfilteredHotelCount)
|
||||||
|
}, [hotels, setResultCount, unfilteredHotelCount])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
@@ -233,10 +241,7 @@ export function SelectHotelMapContent({
|
|||||||
<RoomCardSkeleton />
|
<RoomCardSkeleton />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<HotelListing
|
<HotelListing hotels={visibleHotels} />
|
||||||
hotels={visibleHotels}
|
|
||||||
unfilteredHotelCount={unfilteredHotelCount}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
{showBackToTop && (
|
{showBackToTop && (
|
||||||
<BackToTopButton
|
<BackToTopButton
|
||||||
|
|||||||
@@ -292,7 +292,8 @@ const hotelFacilitiesFilterNames = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
export function getFiltersFromHotels(
|
export function getFiltersFromHotels(
|
||||||
hotels: HotelResponse[]
|
hotels: HotelResponse[],
|
||||||
|
showBookingCodeFilter: boolean
|
||||||
): CategorizedHotelFilters {
|
): CategorizedHotelFilters {
|
||||||
const defaultFilters = { facilityFilters: [], surroundingsFilters: [] }
|
const defaultFilters = { facilityFilters: [], surroundingsFilters: [] }
|
||||||
if (!hotels.length) {
|
if (!hotels.length) {
|
||||||
@@ -306,7 +307,10 @@ export function getFiltersFromHotels(
|
|||||||
...facility,
|
...facility,
|
||||||
hotelId: hotel.operaId,
|
hotelId: hotel.operaId,
|
||||||
hotelIds: [hotel.operaId],
|
hotelIds: [hotel.operaId],
|
||||||
filteredIds: availability.bookingCode ? [hotel.operaId] : [],
|
bookingCodeFilteredIds:
|
||||||
|
availability.bookingCode || !showBookingCodeFilter
|
||||||
|
? [hotel.operaId]
|
||||||
|
: [],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -321,8 +325,10 @@ export function getFiltersFromHotels(
|
|||||||
const matchingFilters = filters.filter((f) => f.id === filterId)
|
const matchingFilters = filters.filter((f) => f.id === filterId)
|
||||||
|
|
||||||
filter.hotelIds = matchingFilters.map((f) => f.hotelId)
|
filter.hotelIds = matchingFilters.map((f) => f.hotelId)
|
||||||
filter.filteredIds = [
|
filter.bookingCodeFilteredIds = [
|
||||||
...new Set(matchingFilters.flatMap((f) => f.filteredIds ?? [])),
|
...new Set(
|
||||||
|
matchingFilters.flatMap((f) => f.bookingCodeFilteredIds ?? [])
|
||||||
|
),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
return filter
|
return filter
|
||||||
|
|||||||
@@ -57,10 +57,10 @@ export async function SelectHotel({
|
|||||||
hotel.availability.productType?.voucher
|
hotel.availability.productType?.voucher
|
||||||
)
|
)
|
||||||
|
|
||||||
const filterList = getFiltersFromHotels(hotels)
|
|
||||||
|
|
||||||
const showBookingCodeFilter = isBookingCodeRateAvailable && !isSpecialRate
|
const showBookingCodeFilter = isBookingCodeRateAvailable && !isSpecialRate
|
||||||
|
|
||||||
|
const filterList = getFiltersFromHotels(hotels, showBookingCodeFilter)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<header className={styles.header}>
|
<header className={styles.header}>
|
||||||
@@ -127,11 +127,7 @@ export async function SelectHotel({
|
|||||||
bookingCode={bookingCode}
|
bookingCode={bookingCode}
|
||||||
isBookingCodeRateNotAvailable={!isBookingCodeRateAvailable}
|
isBookingCodeRateNotAvailable={!isBookingCodeRateAvailable}
|
||||||
/>
|
/>
|
||||||
<HotelCardListing
|
<HotelCardListing hotelData={hotels} isAlternative={isAlternative} />
|
||||||
hotelData={hotels}
|
|
||||||
isAlternative={isAlternative}
|
|
||||||
unfilteredHotelCount={hotels.length}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ export async function AlternativeHotelsMapPage({
|
|||||||
isRedemptionAvailable: isRedemptionAvailability,
|
isRedemptionAvailable: isRedemptionAvailability,
|
||||||
})
|
})
|
||||||
|
|
||||||
const filterList = getFiltersFromHotels(hotels)
|
const filterList = getFiltersFromHotels(hotels, isBookingCodeRateAvailable)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MapContainer>
|
<MapContainer>
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ export async function SelectHotelMapPage({
|
|||||||
isRedemptionAvailable: isRedemptionAvailability,
|
isRedemptionAvailable: isRedemptionAvailability,
|
||||||
})
|
})
|
||||||
|
|
||||||
const filterList = getFiltersFromHotels(hotels)
|
const filterList = getFiltersFromHotels(hotels, isBookingCodeRateAvailable)
|
||||||
|
|
||||||
const suspenseKey = stringify(searchParams)
|
const suspenseKey = stringify(searchParams)
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export type NextSearchParams = { [key: string]: string | string[] | undefined }
|
|||||||
export type HotelFilter = Hotel["detailedFacilities"][number] & {
|
export type HotelFilter = Hotel["detailedFacilities"][number] & {
|
||||||
hotelId: string
|
hotelId: string
|
||||||
hotelIds: string[]
|
hotelIds: string[]
|
||||||
filteredIds: string[]
|
bookingCodeFilteredIds: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CategorizedHotelFilters = {
|
export type CategorizedHotelFilters = {
|
||||||
|
|||||||
Reference in New Issue
Block a user