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:
Bianca Widstam
2025-09-26 08:03:25 +00:00
parent b72f4c71e3
commit 7f3fd0c7a6
11 changed files with 63 additions and 63 deletions

View File

@@ -3,7 +3,6 @@
import { useCallback, useEffect, useRef } from "react"
import { useIntl } from "react-intl"
import { useHotelFilterStore } from "../../stores/hotel-filters"
import { useHotelsMapStore } from "../../stores/hotels-map"
import ListingHotelCardDialog from "../ListingHotelCardDialog"
import { getHotelPins } from "./utils"
@@ -14,12 +13,10 @@ import type { HotelResponse } from "../SelectHotel/helpers"
interface HotelCardDialogListingProps {
hotels: HotelResponse[]
unfilteredHotelCount: number
}
export default function HotelCardDialogListing({
hotels,
unfilteredHotelCount,
}: HotelCardDialogListingProps) {
const intl = useIntl()
const isRedemption = hotels?.find(
@@ -37,7 +34,6 @@ export default function HotelCardDialogListing({
const isScrollingRef = useRef<boolean>(false)
const debounceTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
const { activeHotel, activate, deactivate } = useHotelsMapStore()
const setResultCount = useHotelFilterStore((state) => state.setResultCount)
const handleIntersection = useCallback(
(entries: IntersectionObserverEntry[]) => {
@@ -125,10 +121,6 @@ export default function HotelCardDialogListing({
}
}, [dialogRef, activeHotel, deactivate])
useEffect(() => {
setResultCount(hotels.length, unfilteredHotelCount)
}, [hotels, setResultCount, unfilteredHotelCount])
return (
<div className={styles.hotelCardDialogListing} ref={dialogRef}>
{hotelsPinData?.map((data) => {

View File

@@ -38,14 +38,12 @@ export enum HotelCardListingTypeEnum {
type HotelCardListingProps = {
hotelData: HotelResponse[]
unfilteredHotelCount: number
type?: HotelCardListingTypeEnum
isAlternative?: boolean
}
export default function HotelCardListing({
hotelData,
unfilteredHotelCount,
type = HotelCardListingTypeEnum.PageListing,
isAlternative,
}: HotelCardListingProps) {
@@ -82,6 +80,10 @@ export default function HotelCardListing({
isBookingCodeRateAvailable &&
activeCodeFilter === BookingCodeFilterEnum.Discounted
const unfilteredHotelCount = showOnlyBookingCodeRates
? hotelData.filter((hotel) => hotel.availability.bookingCode).length
: hotelData.length
const hotels = useMemo(() => {
const sortedHotels = getSortedHotels({
hotels: hotelData,
@@ -124,8 +126,10 @@ export default function HotelCardListing({
}, [activeHotel, type])
useEffect(() => {
setResultCount(hotels.length, unfilteredHotelCount)
}, [hotels, setResultCount, unfilteredHotelCount])
if (type === HotelCardListingTypeEnum.PageListing) {
setResultCount(hotels.length, unfilteredHotelCount)
}
}, [hotels, setResultCount, type, unfilteredHotelCount])
function isHotelActiveInMapView(hotelName: string): boolean {
return (

View File

@@ -227,9 +227,10 @@ export default function FilterAndSortModal({
defaultMessage: "See results ({ count })",
},
{
count: filteredCount
? filteredCount
: unfilteredResultCount,
count:
selectedFilters.length > 0
? filteredCount
: unfilteredResultCount,
}
)}
</p>

View File

@@ -43,25 +43,29 @@ export default function FilterContent({
const [filteredHotelIds, setFilteredHotelIds] = useState<string[]>([])
useEffect(() => {
if (activeFilters.length) {
const allFilters = [
...filters.facilityFilters,
...filters.surroundingsFilters,
]
setFilteredHotelIds(
allFilters
const allFilters = [
...filters.facilityFilters,
...filters.surroundingsFilters,
]
const selectedFilters = activeFilters.length
? allFilters
.filter((f) => activeFilters.includes(f.id.toString()))
.map((f) => f.hotelIds)
.reduce((accumulatedHotelIds, currentHotelIds) =>
accumulatedHotelIds.filter((hotelId) =>
currentHotelIds.includes(hotelId)
)
.map((f) =>
showOnlyBookingCodeRates ? f.bookingCodeFilteredIds : f.hotelIds
)
)
} else {
setFilteredHotelIds([])
}
}, [filters, activeFilters, setFilteredHotelIds])
: []
const filteredIds = selectedFilters.reduce(
(accumulatedHotelIds, currentHotelIds) =>
accumulatedHotelIds.filter((hotelId) =>
currentHotelIds.includes(hotelId)
),
selectedFilters[0] || []
)
setFilteredHotelIds(filteredIds)
}, [filters, activeFilters, setFilteredHotelIds, showOnlyBookingCodeRates])
useEffect(() => {
onFilteredCountChange(filteredHotelIds.length)
@@ -74,7 +78,7 @@ export default function FilterContent({
function filterOutput(filters: HotelFilter[]) {
return filters.map((filter) => {
const relevantIds = showOnlyBookingCodeRates
? filter.filteredIds
? filter.bookingCodeFilteredIds
: filter.hotelIds
const isSelected = activeFilters.some((f) => f === filter.id.toString())

View File

@@ -14,29 +14,21 @@ import type { HotelResponse } from "../../helpers"
interface HotelListingProps {
hotels: HotelResponse[]
unfilteredHotelCount: number
}
export default function HotelListing({
hotels,
unfilteredHotelCount,
}: HotelListingProps) {
export default function HotelListing({ hotels }: HotelListingProps) {
const { activeHotel } = useHotelsMapStore()
const isMobile = useMediaQuery("(max-width: 899px)")
return isMobile ? (
<div className={styles.hotelListingMobile} data-open={!!activeHotel}>
<HotelCardDialogListing
hotels={hotels}
unfilteredHotelCount={unfilteredHotelCount}
/>
<HotelCardDialogListing hotels={hotels} />
</div>
) : (
<div className={styles.hotelListing}>
<HotelCardListing
hotelData={hotels}
type={HotelCardListingTypeEnum.MapListing}
unfilteredHotelCount={unfilteredHotelCount}
/>
</div>
)

View File

@@ -1,7 +1,7 @@
"use client"
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 { useMediaQuery } from "usehooks-ts"
@@ -75,6 +75,8 @@ export function SelectHotelMapContent({
const listingContainerRef = useRef<HTMLDivElement | null>(null)
const activeFilters = useHotelFilterStore((state) => state.activeFilters)
const setResultCount = useHotelFilterStore((state) => state.setResultCount)
const hotelMapStore = useHotelsMapStore()
const { showBackToTop, scrollToTop } = useScrollToTop({
@@ -193,7 +195,13 @@ export function SelectHotelMapContent({
const showBookingCodeFilter =
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 (
<div className={styles.container}>
@@ -233,10 +241,7 @@ export function SelectHotelMapContent({
<RoomCardSkeleton />
</div>
) : (
<HotelListing
hotels={visibleHotels}
unfilteredHotelCount={unfilteredHotelCount}
/>
<HotelListing hotels={visibleHotels} />
)}
{showBackToTop && (
<BackToTopButton

View File

@@ -292,7 +292,8 @@ const hotelFacilitiesFilterNames = [
]
export function getFiltersFromHotels(
hotels: HotelResponse[]
hotels: HotelResponse[],
showBookingCodeFilter: boolean
): CategorizedHotelFilters {
const defaultFilters = { facilityFilters: [], surroundingsFilters: [] }
if (!hotels.length) {
@@ -306,7 +307,10 @@ export function getFiltersFromHotels(
...facility,
hotelId: 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)
filter.hotelIds = matchingFilters.map((f) => f.hotelId)
filter.filteredIds = [
...new Set(matchingFilters.flatMap((f) => f.filteredIds ?? [])),
filter.bookingCodeFilteredIds = [
...new Set(
matchingFilters.flatMap((f) => f.bookingCodeFilteredIds ?? [])
),
]
}
return filter

View File

@@ -57,10 +57,10 @@ export async function SelectHotel({
hotel.availability.productType?.voucher
)
const filterList = getFiltersFromHotels(hotels)
const showBookingCodeFilter = isBookingCodeRateAvailable && !isSpecialRate
const filterList = getFiltersFromHotels(hotels, showBookingCodeFilter)
return (
<>
<header className={styles.header}>
@@ -127,11 +127,7 @@ export async function SelectHotel({
bookingCode={bookingCode}
isBookingCodeRateNotAvailable={!isBookingCodeRateAvailable}
/>
<HotelCardListing
hotelData={hotels}
isAlternative={isAlternative}
unfilteredHotelCount={hotels.length}
/>
<HotelCardListing hotelData={hotels} isAlternative={isAlternative} />
</div>
</main>
</>

View File

@@ -104,7 +104,7 @@ export async function AlternativeHotelsMapPage({
isRedemptionAvailable: isRedemptionAvailability,
})
const filterList = getFiltersFromHotels(hotels)
const filterList = getFiltersFromHotels(hotels, isBookingCodeRateAvailable)
return (
<MapContainer>

View File

@@ -105,7 +105,7 @@ export async function SelectHotelMapPage({
isRedemptionAvailable: isRedemptionAvailability,
})
const filterList = getFiltersFromHotels(hotels)
const filterList = getFiltersFromHotels(hotels, isBookingCodeRateAvailable)
const suspenseKey = stringify(searchParams)

View File

@@ -5,7 +5,7 @@ export type NextSearchParams = { [key: string]: string | string[] | undefined }
export type HotelFilter = Hotel["detailedFacilities"][number] & {
hotelId: string
hotelIds: string[]
filteredIds: string[]
bookingCodeFilteredIds: string[]
}
export type CategorizedHotelFilters = {