feat(BOOK-463): Fetching hotel filters from CMS and using these inside the destination pages and select hotel page
* feat(BOOK-463): Fetching hotel filters from CMS and using these inside the destination pages * fix(BOOK-698): fetch hotel filters from CMS on select hotel page Approved-by: Bianca Widstam
This commit is contained in:
@@ -4,12 +4,13 @@ import {
|
||||
type HotelSortItem,
|
||||
HotelSortOption,
|
||||
} from "@scandic-hotels/trpc/types/hotel"
|
||||
import { getFiltersFromHotels } from "@scandic-hotels/trpc/utils/getFiltersFromHotels"
|
||||
|
||||
import { getHotelsByCSFilter } from "@/lib/trpc/memoizedRequests"
|
||||
import {
|
||||
getHotelFilters,
|
||||
getHotelsByCSFilter,
|
||||
} from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import { getIntl } from "@/i18n"
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
import HotelListingDataProvider from "@/providers/HotelListingDataProvider"
|
||||
|
||||
import CampaignHotelListingSkeleton from "./CampaignHotelListingSkeleton"
|
||||
@@ -35,14 +36,13 @@ export default async function CampaignHotelListing({
|
||||
isMainBlock = false,
|
||||
}: CampaignHotelListingProps) {
|
||||
const intl = await getIntl()
|
||||
const lang = await getLang()
|
||||
const hotels = await getHotelsByCSFilter({ hotelsToInclude: hotelIds })
|
||||
|
||||
if (!hotels.length) {
|
||||
return null
|
||||
}
|
||||
|
||||
const allFilters = getFiltersFromHotels(hotels, lang)
|
||||
const allFilters = await getHotelFilters()
|
||||
const sortItems: HotelSortItem[] = [
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
||||
@@ -5,11 +5,11 @@ import {
|
||||
type HotelSortItem,
|
||||
HotelSortOption,
|
||||
} from "@scandic-hotels/trpc/types/hotel"
|
||||
import { getFiltersFromHotels } from "@scandic-hotels/trpc/utils/getFiltersFromHotels"
|
||||
|
||||
import { env } from "@/env/server"
|
||||
import {
|
||||
getDestinationCityPage,
|
||||
getHotelFilters,
|
||||
getHotelsByCityIdentifier,
|
||||
} from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
@@ -17,7 +17,6 @@ import Breadcrumbs from "@/components/Breadcrumbs"
|
||||
import { SeoFilters } from "@/components/ContentType/DestinationPage/SeoFilters"
|
||||
import BreadcrumbsSkeleton from "@/components/TempDesignSystem/Breadcrumbs/BreadcrumbsSkeleton"
|
||||
import { getIntl } from "@/i18n"
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
import DestinationDataProvider from "@/providers/DestinationDataProvider"
|
||||
import { getPathname } from "@/utils/getPathname"
|
||||
|
||||
@@ -45,7 +44,6 @@ export default async function DestinationCityPage({
|
||||
filterFromUrl,
|
||||
}: DestinationCityPageProps) {
|
||||
const intl = await getIntl()
|
||||
const lang = await getLang()
|
||||
const pathname = await getPathname()
|
||||
const pageData = await getDestinationCityPage()
|
||||
|
||||
@@ -70,7 +68,7 @@ export default async function DestinationCityPage({
|
||||
const activeSeoFilter = getActiveSeoFilter(seo_filters, filterFromUrl)
|
||||
|
||||
const allHotels = await getHotelsByCityIdentifier(cityIdentifier)
|
||||
const hotelFilters = getFiltersFromHotels(allHotels, lang)
|
||||
const allHotelFilters = await getHotelFilters()
|
||||
|
||||
const sortItems: HotelSortItem[] = [
|
||||
{
|
||||
@@ -102,7 +100,7 @@ export default async function DestinationCityPage({
|
||||
<Suspense fallback={<DestinationCityPageSkeleton />}>
|
||||
<DestinationDataProvider
|
||||
allHotels={allHotels}
|
||||
hotelFilters={hotelFilters}
|
||||
hotelFilters={allHotelFilters}
|
||||
seoFilters={seo_filters}
|
||||
sortItems={sortItems}
|
||||
pathname={pathname}
|
||||
|
||||
@@ -5,12 +5,12 @@ import {
|
||||
type HotelSortItem,
|
||||
HotelSortOption,
|
||||
} from "@scandic-hotels/trpc/types/hotel"
|
||||
import { getFiltersFromHotels } from "@scandic-hotels/trpc/utils/getFiltersFromHotels"
|
||||
|
||||
import { env } from "@/env/server"
|
||||
import {
|
||||
getDestinationCityPagesByCountry,
|
||||
getDestinationCountryPage,
|
||||
getHotelFilters,
|
||||
getHotelsByCountry,
|
||||
} from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
@@ -18,7 +18,6 @@ import Breadcrumbs from "@/components/Breadcrumbs"
|
||||
import { SeoFilters } from "@/components/ContentType/DestinationPage/SeoFilters"
|
||||
import BreadcrumbsSkeleton from "@/components/TempDesignSystem/Breadcrumbs/BreadcrumbsSkeleton"
|
||||
import { getIntl } from "@/i18n"
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
import DestinationDataProvider from "@/providers/DestinationDataProvider"
|
||||
import { getPathname } from "@/utils/getPathname"
|
||||
|
||||
@@ -46,7 +45,6 @@ export default async function DestinationCountryPage({
|
||||
filterFromUrl,
|
||||
}: DestinationCountryPageProps) {
|
||||
const intl = await getIntl()
|
||||
const lang = await getLang()
|
||||
const pathname = await getPathname()
|
||||
const pageData = await getDestinationCountryPage()
|
||||
|
||||
@@ -74,7 +72,7 @@ export default async function DestinationCountryPage({
|
||||
getHotelsByCountry(destination_settings.country),
|
||||
getDestinationCityPagesByCountry(destination_settings.country),
|
||||
])
|
||||
const hotelFilters = getFiltersFromHotels(allHotels, lang)
|
||||
const allHotelFilters = await getHotelFilters()
|
||||
|
||||
const sortItems: HotelSortItem[] = [
|
||||
{
|
||||
@@ -114,7 +112,7 @@ export default async function DestinationCountryPage({
|
||||
<DestinationDataProvider
|
||||
allHotels={allHotels}
|
||||
allCities={allCitiesWithCount}
|
||||
hotelFilters={hotelFilters}
|
||||
hotelFilters={allHotelFilters}
|
||||
seoFilters={seo_filters}
|
||||
sortItems={sortItems}
|
||||
pathname={pathname}
|
||||
|
||||
@@ -6,36 +6,38 @@
|
||||
cursor: pointer;
|
||||
border-radius: var(--Corner-radius-md);
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.checkboxWrapper:hover {
|
||||
background-color: var(--UI-Input-Controls-Surface-Hover);
|
||||
@media (hover: hover) {
|
||||
&:not([data-disabled]):hover {
|
||||
background-color: var(--UI-Input-Controls-Surface-Hover);
|
||||
}
|
||||
}
|
||||
|
||||
&[data-selected] .checkbox {
|
||||
border: none;
|
||||
background-color: var(--Surface-UI-Fill-Active);
|
||||
}
|
||||
|
||||
&[data-disabled] {
|
||||
cursor: not-allowed;
|
||||
|
||||
.checkbox {
|
||||
border-color: var(--UI-Input-Controls-Border-Disabled);
|
||||
background-color: var(--UI-Input-Controls-Surface-Disabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
min-width: 24px;
|
||||
background: var(--UI-Input-Controls-Surface-Normal);
|
||||
border: 1px solid var(--UI-Input-Controls-Border-Normal);
|
||||
border-radius: var(--Corner-radius-sm);
|
||||
transition: all 0.3s;
|
||||
border-radius: 4px;
|
||||
transition: all 200ms;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: var(--UI-Input-Controls-Surface-Normal);
|
||||
}
|
||||
|
||||
.checkboxWrapper[data-selected] .checkbox {
|
||||
border-color: var(--UI-Input-Controls-Fill-Selected);
|
||||
background-color: var(--UI-Input-Controls-Fill-Selected);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 767px) {
|
||||
.checkboxWrapper:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.checkboxWrapper[data-selected] {
|
||||
background-color: transparent;
|
||||
}
|
||||
forced-color-adjust: none;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ interface CheckboxProps {
|
||||
name: string
|
||||
value: string
|
||||
isSelected: boolean
|
||||
isDisabled?: boolean
|
||||
onChange: (filterId: string) => void
|
||||
}
|
||||
|
||||
@@ -18,6 +19,7 @@ export default function Checkbox({
|
||||
isSelected,
|
||||
name,
|
||||
value,
|
||||
isDisabled,
|
||||
onChange,
|
||||
}: CheckboxProps) {
|
||||
return (
|
||||
@@ -25,6 +27,7 @@ export default function Checkbox({
|
||||
className={styles.checkboxWrapper}
|
||||
isSelected={isSelected}
|
||||
onChange={() => onChange(value)}
|
||||
isDisabled={isDisabled}
|
||||
>
|
||||
{({ isSelected }) => (
|
||||
<>
|
||||
|
||||
@@ -10,13 +10,17 @@ import Checkbox from "./Checkbox"
|
||||
|
||||
import styles from "./filter.module.css"
|
||||
|
||||
import type { CategorizedHotelFilters } from "@scandic-hotels/trpc/types/hotel"
|
||||
import type {
|
||||
HotelFilter,
|
||||
HotelFilters,
|
||||
} from "@scandic-hotels/trpc/routers/hotels/filters/output"
|
||||
|
||||
interface FilterProps {
|
||||
filters: CategorizedHotelFilters
|
||||
filters: HotelFilters
|
||||
listType: "city" | "hotel"
|
||||
}
|
||||
|
||||
export default function Filter({ filters }: FilterProps) {
|
||||
export default function Filter({ filters, listType }: FilterProps) {
|
||||
const intl = useIntl()
|
||||
const { facilityFilters, surroundingsFilters } = filters
|
||||
const { pendingFilters, togglePendingFilter } = useDestinationDataStore(
|
||||
@@ -54,8 +58,9 @@ export default function Filter({ filters }: FilterProps) {
|
||||
{facilityFilters.map((filter) => (
|
||||
<li key={`filter-${filter.slug}`}>
|
||||
<Checkbox
|
||||
name={filter.name}
|
||||
name={getCheckboxLabelWithCount(filter, listType)}
|
||||
value={filter.slug}
|
||||
isDisabled={filter.hotelCount === 0}
|
||||
onChange={() => togglePendingFilter(filter)}
|
||||
isSelected={
|
||||
!!pendingFilters.find((pf) => pf.id === filter.id)
|
||||
@@ -79,8 +84,9 @@ export default function Filter({ filters }: FilterProps) {
|
||||
{surroundingsFilters.map((filter) => (
|
||||
<li key={`filter-${filter.slug}`}>
|
||||
<Checkbox
|
||||
name={filter.name}
|
||||
name={getCheckboxLabelWithCount(filter, listType)}
|
||||
value={filter.slug}
|
||||
isDisabled={filter.hotelCount === 0}
|
||||
onChange={() => togglePendingFilter(filter)}
|
||||
isSelected={
|
||||
!!pendingFilters.find((pf) => pf.id === filter.id)
|
||||
@@ -94,3 +100,17 @@ export default function Filter({ filters }: FilterProps) {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function getCheckboxLabelWithCount(
|
||||
filter: HotelFilter,
|
||||
listType: "city" | "hotel"
|
||||
) {
|
||||
if (listType === "city" && filter.cityCount) {
|
||||
return `${filter.name} (${filter.cityCount})`
|
||||
}
|
||||
if (listType === "hotel" && filter.hotelCount) {
|
||||
return `${filter.name} (${filter.hotelCount})`
|
||||
}
|
||||
|
||||
return filter.name
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ export default function DestinationFilterAndSort({
|
||||
const intl = useIntl()
|
||||
const router = useRouter()
|
||||
const {
|
||||
allFilters,
|
||||
filtersWithCount,
|
||||
sortItems,
|
||||
pendingFilters,
|
||||
pendingSort,
|
||||
@@ -47,7 +47,7 @@ export default function DestinationFilterAndSort({
|
||||
resetPendingValues,
|
||||
setIsLoading,
|
||||
} = useDestinationDataStore((state) => ({
|
||||
allFilters: state.allFilters,
|
||||
filtersWithCount: state.filtersWithCount,
|
||||
sortItems: state.sortItems,
|
||||
pendingFilters: state.pendingFilters,
|
||||
pendingSort: state.pendingSort,
|
||||
@@ -166,7 +166,7 @@ export default function DestinationFilterAndSort({
|
||||
<div className={styles.content}>
|
||||
<Sort sortItems={sortItems} />
|
||||
<Divider className={styles.divider} />
|
||||
<Filter filters={allFilters} />
|
||||
<Filter filters={filtersWithCount} listType={listType} />
|
||||
</div>
|
||||
{pendingCount === 0 && (
|
||||
<div className={styles.alertWrapper}>
|
||||
|
||||
@@ -6,36 +6,38 @@
|
||||
cursor: pointer;
|
||||
border-radius: var(--Corner-radius-md);
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.checkboxWrapper:hover {
|
||||
background-color: var(--UI-Input-Controls-Surface-Hover);
|
||||
@media (hover: hover) {
|
||||
&:not([data-disabled]):hover {
|
||||
background-color: var(--UI-Input-Controls-Surface-Hover);
|
||||
}
|
||||
}
|
||||
|
||||
&[data-selected] .checkbox {
|
||||
border: none;
|
||||
background-color: var(--Surface-UI-Fill-Active);
|
||||
}
|
||||
|
||||
&[data-disabled] {
|
||||
cursor: not-allowed;
|
||||
|
||||
.checkbox {
|
||||
border-color: var(--UI-Input-Controls-Border-Disabled);
|
||||
background-color: var(--UI-Input-Controls-Surface-Disabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
min-width: 24px;
|
||||
background: var(--UI-Input-Controls-Surface-Normal);
|
||||
border: 1px solid var(--UI-Input-Controls-Border-Normal);
|
||||
border-radius: var(--Corner-radius-sm);
|
||||
transition: all 0.3s;
|
||||
border-radius: 4px;
|
||||
transition: all 200ms;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: var(--UI-Input-Controls-Surface-Normal);
|
||||
}
|
||||
|
||||
.checkboxWrapper[data-selected] .checkbox {
|
||||
border-color: var(--UI-Input-Controls-Fill-Selected);
|
||||
background-color: var(--UI-Input-Controls-Fill-Selected);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 767px) {
|
||||
.checkboxWrapper:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.checkboxWrapper[data-selected] {
|
||||
background-color: transparent;
|
||||
}
|
||||
forced-color-adjust: none;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ interface CheckboxProps {
|
||||
name: string
|
||||
value: string
|
||||
isSelected: boolean
|
||||
isDisabled?: boolean
|
||||
onChange: (filterId: string) => void
|
||||
}
|
||||
|
||||
@@ -18,12 +19,14 @@ export default function Checkbox({
|
||||
isSelected,
|
||||
name,
|
||||
value,
|
||||
isDisabled,
|
||||
onChange,
|
||||
}: CheckboxProps) {
|
||||
return (
|
||||
<AriaCheckbox
|
||||
className={styles.checkboxWrapper}
|
||||
isSelected={isSelected}
|
||||
isDisabled={isDisabled}
|
||||
onChange={() => onChange(value)}
|
||||
>
|
||||
{({ isSelected }) => (
|
||||
|
||||
@@ -10,10 +10,10 @@ import Checkbox from "./Checkbox"
|
||||
|
||||
import styles from "./filter.module.css"
|
||||
|
||||
import type { CategorizedHotelFilters } from "@scandic-hotels/trpc/types/hotel"
|
||||
import type { HotelFilters } from "@scandic-hotels/trpc/routers/hotels/filters/output"
|
||||
|
||||
interface FilterProps {
|
||||
filters: CategorizedHotelFilters
|
||||
filters: HotelFilters
|
||||
}
|
||||
|
||||
export default function Filter({ filters }: FilterProps) {
|
||||
@@ -58,10 +58,17 @@ export default function Filter({ filters }: FilterProps) {
|
||||
{countryFilters.map((filter) => (
|
||||
<li key={`filter-${filter.slug}`}>
|
||||
<Checkbox
|
||||
name={filter.name}
|
||||
name={
|
||||
filter.hotelCount
|
||||
? `${filter.name} (${filter.hotelCount})`
|
||||
: filter.name
|
||||
}
|
||||
isDisabled={!filter.hotelCount}
|
||||
value={filter.slug}
|
||||
onChange={() => togglePendingFilter(filter.slug)}
|
||||
isSelected={!!pendingFilters.find((f) => f === filter.slug)}
|
||||
onChange={() => togglePendingFilter(filter)}
|
||||
isSelected={
|
||||
!!pendingFilters.find((pf) => pf.id === filter.id)
|
||||
}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
@@ -81,10 +88,17 @@ export default function Filter({ filters }: FilterProps) {
|
||||
{facilityFilters.map((filter) => (
|
||||
<li key={`filter-${filter.slug}`}>
|
||||
<Checkbox
|
||||
name={filter.name}
|
||||
name={
|
||||
filter.hotelCount
|
||||
? `${filter.name} (${filter.hotelCount})`
|
||||
: filter.name
|
||||
}
|
||||
isDisabled={!filter.hotelCount}
|
||||
value={filter.slug}
|
||||
onChange={() => togglePendingFilter(filter.slug)}
|
||||
isSelected={!!pendingFilters.find((f) => f === filter.slug)}
|
||||
onChange={() => togglePendingFilter(filter)}
|
||||
isSelected={
|
||||
!!pendingFilters.find((pf) => pf.id === filter.id)
|
||||
}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
@@ -104,10 +118,17 @@ export default function Filter({ filters }: FilterProps) {
|
||||
{surroundingsFilters.map((filter) => (
|
||||
<li key={`filter-${filter.slug}`}>
|
||||
<Checkbox
|
||||
name={filter.name}
|
||||
name={
|
||||
filter.hotelCount
|
||||
? `${filter.name} (${filter.hotelCount})`
|
||||
: filter.name
|
||||
}
|
||||
isDisabled={!filter.hotelCount}
|
||||
value={filter.slug}
|
||||
onChange={() => togglePendingFilter(filter.slug)}
|
||||
isSelected={!!pendingFilters.find((f) => f === filter.slug)}
|
||||
onChange={() => togglePendingFilter(filter)}
|
||||
isSelected={
|
||||
!!pendingFilters.find((pf) => pf.id === filter.id)
|
||||
}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
|
||||
@@ -28,7 +28,7 @@ export default function HotelFilterAndSort() {
|
||||
const intl = useIntl()
|
||||
const router = useRouter()
|
||||
const {
|
||||
filters,
|
||||
filtersWithCount,
|
||||
sortItems,
|
||||
pendingFilters,
|
||||
pendingSort,
|
||||
@@ -39,7 +39,7 @@ export default function HotelFilterAndSort() {
|
||||
resetPendingValues,
|
||||
setIsLoading,
|
||||
} = useHotelListingDataStore((state) => ({
|
||||
filters: state.allFilters,
|
||||
filtersWithCount: state.filtersWithCount,
|
||||
sortItems: state.sortItems,
|
||||
pendingFilters: state.pendingFilters,
|
||||
pendingSort: state.pendingSort,
|
||||
@@ -135,7 +135,7 @@ export default function HotelFilterAndSort() {
|
||||
<div className={styles.content}>
|
||||
<Sort sortItems={sortItems} />
|
||||
<Divider className={styles.divider} />
|
||||
<Filter filters={filters} />
|
||||
<Filter filters={filtersWithCount} />
|
||||
</div>
|
||||
{pendingCount === 0 && (
|
||||
<div className={styles.alertWrapper}>
|
||||
|
||||
Reference in New Issue
Block a user