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 { 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) => {
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -227,9 +227,10 @@ export default function FilterAndSortModal({
|
||||
defaultMessage: "See results ({ count })",
|
||||
},
|
||||
{
|
||||
count: filteredCount
|
||||
? filteredCount
|
||||
: unfilteredResultCount,
|
||||
count:
|
||||
selectedFilters.length > 0
|
||||
? filteredCount
|
||||
: unfilteredResultCount,
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
|
||||
@@ -104,7 +104,7 @@ export async function AlternativeHotelsMapPage({
|
||||
isRedemptionAvailable: isRedemptionAvailability,
|
||||
})
|
||||
|
||||
const filterList = getFiltersFromHotels(hotels)
|
||||
const filterList = getFiltersFromHotels(hotels, isBookingCodeRateAvailable)
|
||||
|
||||
return (
|
||||
<MapContainer>
|
||||
|
||||
@@ -105,7 +105,7 @@ export async function SelectHotelMapPage({
|
||||
isRedemptionAvailable: isRedemptionAvailability,
|
||||
})
|
||||
|
||||
const filterList = getFiltersFromHotels(hotels)
|
||||
const filterList = getFiltersFromHotels(hotels, isBookingCodeRateAvailable)
|
||||
|
||||
const suspenseKey = stringify(searchParams)
|
||||
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
Reference in New Issue
Block a user