Merged in feat/SW-1232-filter-improvements-select-hot (pull request #2168)
feat: SW-1232 Implemented disabling of filters and show hotel count * feat: SW-1232 Implemented disabling of filters and show hotel count * feat: SW-1232 Optimised code Approved-by: Niclas Edenvin
This commit is contained in:
@@ -7,32 +7,9 @@ import type {
|
||||
AlternativeHotelsAvailabilityInput,
|
||||
AvailabilityInput,
|
||||
} from "@/types/components/hotelReservation/selectHotel/availabilityInput"
|
||||
import type {
|
||||
HotelData,
|
||||
NullableHotelData,
|
||||
} from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps"
|
||||
import type { CategorizedFilters } from "@/types/components/hotelReservation/selectHotel/hotelFilters"
|
||||
import type { DetailedFacility } from "@/types/hotel"
|
||||
import type { NullableHotelData } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps"
|
||||
import type { HotelsAvailabilityItem } from "@/types/trpc/routers/hotel/availability"
|
||||
|
||||
const hotelSurroundingsFilterNames = [
|
||||
"Hotel surroundings",
|
||||
"Hotel omgivelser",
|
||||
"Hotelumgebung",
|
||||
"Hotellia lähellä",
|
||||
"Hotellomgivelser",
|
||||
"Omgivningar",
|
||||
]
|
||||
|
||||
const hotelFacilitiesFilterNames = [
|
||||
"Hotel facilities",
|
||||
"Hotellfaciliteter",
|
||||
"Hotelfaciliteter",
|
||||
"Hotel faciliteter",
|
||||
"Hotel-Infos",
|
||||
"Hotellin palvelut",
|
||||
]
|
||||
|
||||
export async function fetchAvailableHotels(
|
||||
input: AvailabilityInput
|
||||
): Promise<NullableHotelData[]> {
|
||||
@@ -98,38 +75,3 @@ async function enhanceHotels(hotels: {
|
||||
|
||||
return await Promise.all(hotelFetchers)
|
||||
}
|
||||
|
||||
export function getFiltersFromHotels(hotels: HotelData[]): CategorizedFilters {
|
||||
if (hotels.length === 0)
|
||||
return { facilityFilters: [], surroundingsFilters: [] }
|
||||
|
||||
const filters = hotels.flatMap((hotel) => {
|
||||
if (!hotel.hotelData) return []
|
||||
return hotel.hotelData.detailedFacilities
|
||||
})
|
||||
|
||||
const uniqueFilterIds = [...new Set(filters.map((filter) => filter.id))]
|
||||
const filterList: DetailedFacility[] = uniqueFilterIds
|
||||
.map((filterId) => filters.find((filter) => filter.id === filterId))
|
||||
.filter((filter): filter is DetailedFacility => filter !== undefined)
|
||||
.sort((a, b) => b.sortOrder - a.sortOrder)
|
||||
|
||||
return filterList.reduce<CategorizedFilters>(
|
||||
(acc, filter) => {
|
||||
if (filter.filter && hotelSurroundingsFilterNames.includes(filter.filter))
|
||||
return {
|
||||
facilityFilters: acc.facilityFilters,
|
||||
surroundingsFilters: [...acc.surroundingsFilters, filter],
|
||||
}
|
||||
|
||||
if (filter.filter && hotelFacilitiesFilterNames.includes(filter.filter))
|
||||
return {
|
||||
facilityFilters: [...acc.facilityFilters, filter],
|
||||
surroundingsFilters: acc.surroundingsFilters,
|
||||
}
|
||||
|
||||
return acc
|
||||
},
|
||||
{ facilityFilters: [], surroundingsFilters: [] }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -33,3 +33,12 @@
|
||||
forced-color-adjust: none;
|
||||
background: var(--UI-Input-Controls-Surface-Normal);
|
||||
}
|
||||
|
||||
.container[data-disabled] {
|
||||
color: var(--Text-Interactive-Disabled);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.container[data-disabled] .checkbox {
|
||||
border-color: var(--Text-Interactive-Disabled);
|
||||
background: var(--Surface-Primary-Disabled);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ export default function FilterCheckbox({
|
||||
isSelected,
|
||||
name,
|
||||
id,
|
||||
isDisabled,
|
||||
onChange,
|
||||
}: FilterCheckboxProps) {
|
||||
return (
|
||||
@@ -20,6 +21,7 @@ export default function FilterCheckbox({
|
||||
className={styles.container}
|
||||
isSelected={isSelected}
|
||||
onChange={() => onChange(id)}
|
||||
isDisabled={isDisabled}
|
||||
>
|
||||
{({ isSelected }) => (
|
||||
<>
|
||||
|
||||
@@ -14,7 +14,10 @@ import FilterCheckbox from "./FilterCheckbox"
|
||||
|
||||
import styles from "./hotelFilter.module.css"
|
||||
|
||||
import type { HotelFiltersProps } from "@/types/components/hotelReservation/selectHotel/hotelFilters"
|
||||
import type {
|
||||
HotelFilter,
|
||||
HotelFiltersProps,
|
||||
} from "@/types/components/hotelReservation/selectHotel/hotelFilters"
|
||||
|
||||
export default function HotelFilter({ className, filters }: HotelFiltersProps) {
|
||||
const intl = useIntl()
|
||||
@@ -49,6 +52,47 @@ export default function HotelFilter({ className, filters }: HotelFiltersProps) {
|
||||
return null
|
||||
}
|
||||
|
||||
let filteredHotelIds: string[] | undefined
|
||||
if (activeFilters.length) {
|
||||
const allFilters = [
|
||||
...filters.facilityFilters,
|
||||
...filters.surroundingsFilters,
|
||||
]
|
||||
filteredHotelIds = allFilters
|
||||
.filter((f) => activeFilters.includes(f.id.toString()))
|
||||
.map((f) => f.hotelIds)
|
||||
.reduce((accumlatedHotelIds, currentHotelIds) =>
|
||||
accumlatedHotelIds?.filter((hotelId) =>
|
||||
currentHotelIds?.includes(hotelId)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
function filterOutput(filters: HotelFilter[]) {
|
||||
return filters.map((filter) => {
|
||||
const isDisabled = filteredHotelIds?.length
|
||||
? !filter.hotelIds?.some((hotelId) =>
|
||||
filteredHotelIds.includes(hotelId)
|
||||
)
|
||||
: false
|
||||
return (
|
||||
<li key={`li-${filter.id}`} className={styles.filter}>
|
||||
<FilterCheckbox
|
||||
name={filter.name}
|
||||
id={filter.id.toString()}
|
||||
onChange={() => toggleFilter(filter.id.toString())}
|
||||
isSelected={activeFilters.some((f) => f === filter.id.toString())}
|
||||
isDisabled={isDisabled}
|
||||
/>
|
||||
{!isDisabled ? (
|
||||
// eslint-disable-next-line formatjs/no-literal-string-in-jsx
|
||||
<span>{`(${filteredHotelIds?.filter((id) => filter.hotelIds.includes(id))?.length ?? filter.hotelIds.length})`}</span>
|
||||
) : null}
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<aside className={`${styles.container} ${className}`}>
|
||||
<form>
|
||||
@@ -63,20 +107,7 @@ export default function HotelFilter({ className, filters }: HotelFiltersProps) {
|
||||
defaultMessage: "Hotel facilities",
|
||||
})}
|
||||
</Subtitle>
|
||||
<ul>
|
||||
{filters.facilityFilters.map((filter) => (
|
||||
<li key={`li-${filter.id}`} className={styles.filter}>
|
||||
<FilterCheckbox
|
||||
name={filter.name}
|
||||
id={filter.id.toString()}
|
||||
onChange={() => toggleFilter(filter.id.toString())}
|
||||
isSelected={
|
||||
!!activeFilters.find((f) => f === filter.id.toString())
|
||||
}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<ul>{filterOutput(filters.facilityFilters)}</ul>
|
||||
</div>
|
||||
|
||||
<div className={styles.facilities}>
|
||||
@@ -85,20 +116,7 @@ export default function HotelFilter({ className, filters }: HotelFiltersProps) {
|
||||
defaultMessage: "Hotel surroundings",
|
||||
})}
|
||||
</Subtitle>
|
||||
<ul>
|
||||
{filters.surroundingsFilters.map((filter) => (
|
||||
<li key={`li-${filter.id}`} className={styles.filter}>
|
||||
<FilterCheckbox
|
||||
name={filter.name}
|
||||
id={filter.id.toString()}
|
||||
onChange={() => toggleFilter(filter.id.toString())}
|
||||
isSelected={
|
||||
!!activeFilters.find((f) => f === filter.id.toString())
|
||||
}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<ul>{filterOutput(filters.surroundingsFilters)}</ul>
|
||||
</div>
|
||||
</form>
|
||||
</aside>
|
||||
|
||||
@@ -9,13 +9,16 @@ import type {
|
||||
AlternativeHotelsAvailabilityInput,
|
||||
AvailabilityInput,
|
||||
} from "@/types/components/hotelReservation/selectHotel/availabilityInput"
|
||||
import type { CategorizedFilters } from "@/types/components/hotelReservation/selectHotel/hotelFilters"
|
||||
import type {
|
||||
CategorizedFilters,
|
||||
HotelFilter,
|
||||
} from "@/types/components/hotelReservation/selectHotel/hotelFilters"
|
||||
import { AvailabilityEnum } from "@/types/components/hotelReservation/selectHotel/selectHotel"
|
||||
import type {
|
||||
AlternativeHotelsSearchParams,
|
||||
SelectHotelSearchParams,
|
||||
} from "@/types/components/hotelReservation/selectHotel/selectHotelSearchParams"
|
||||
import type { AdditionalData, DetailedFacility, Hotel } from "@/types/hotel"
|
||||
import type { AdditionalData, Hotel } from "@/types/hotel"
|
||||
import type { HotelsAvailabilityItem } from "@/types/trpc/routers/hotel/availability"
|
||||
import type {
|
||||
HotelLocation,
|
||||
@@ -253,12 +256,31 @@ export function getFiltersFromHotels(
|
||||
return defaultFilters
|
||||
}
|
||||
|
||||
const filters = hotels.flatMap(({ hotel }) => hotel.detailedFacilities)
|
||||
const filters = hotels.flatMap(({ hotel }) =>
|
||||
hotel.detailedFacilities.map(
|
||||
(facility) =>
|
||||
<HotelFilter>{
|
||||
...facility,
|
||||
hotelId: hotel.operaId,
|
||||
hotelIds: [hotel.operaId],
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
const uniqueFilterIds = [...new Set(filters.map((filter) => filter.id))]
|
||||
const filterList: DetailedFacility[] = uniqueFilterIds
|
||||
.map((filterId) => filters.find((filter) => filter.id === filterId))
|
||||
.filter((filter): filter is DetailedFacility => filter !== undefined)
|
||||
const filterList: HotelFilter[] = uniqueFilterIds
|
||||
.map((filterId) => {
|
||||
const filter = filters.find((f) => f.id === filterId)
|
||||
|
||||
// List and include all hotel Ids having same filter / amenity
|
||||
if (filter) {
|
||||
filter.hotelIds = filters
|
||||
.filter((f) => f.id === filterId)
|
||||
.map((f) => f.hotelId)
|
||||
}
|
||||
return filter
|
||||
})
|
||||
.filter((filter): filter is HotelFilter => filter !== undefined)
|
||||
.sort((a, b) => b.sortOrder - a.sortOrder)
|
||||
|
||||
return filterList.reduce<CategorizedFilters>((filters, filter) => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export type FilterCheckboxProps = {
|
||||
name: string
|
||||
id: string
|
||||
isDisabled?: boolean
|
||||
isSelected: boolean
|
||||
onChange: (filterId: string) => void
|
||||
}
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
import type { Hotel } from "@/types/hotel"
|
||||
|
||||
export type HotelFilter = Hotel["detailedFacilities"][number] & {
|
||||
hotelId: string
|
||||
hotelIds: string[]
|
||||
}
|
||||
|
||||
export type CategorizedFilters = {
|
||||
facilityFilters: Hotel["detailedFacilities"]
|
||||
surroundingsFilters: Hotel["detailedFacilities"]
|
||||
facilityFilters: HotelFilter[]
|
||||
surroundingsFilters: HotelFilter[]
|
||||
}
|
||||
export type HotelFiltersProps = {
|
||||
filters: CategorizedFilters
|
||||
|
||||
Reference in New Issue
Block a user