fix(SW-2933): Making the hotels/city listing render correctly with active filter on page load
Approved-by: Christian Andolf Approved-by: Matilda Landström
This commit is contained in:
@@ -11,7 +11,7 @@ import type { PageArgs } from "@/types/params"
|
|||||||
export { generateMetadata } from "@/utils/generateMetadata"
|
export { generateMetadata } from "@/utils/generateMetadata"
|
||||||
|
|
||||||
export default async function DestinationCityPagePage(
|
export default async function DestinationCityPagePage(
|
||||||
props: PageArgs<{}, { view?: "map" }>
|
props: PageArgs<{}, { view?: "map"; filterFromUrl?: string }>
|
||||||
) {
|
) {
|
||||||
const searchParams = await props.searchParams
|
const searchParams = await props.searchParams
|
||||||
if (env.NEW_SITE_LIVE_STATUS === "NOT_LIVE") {
|
if (env.NEW_SITE_LIVE_STATUS === "NOT_LIVE") {
|
||||||
@@ -20,7 +20,10 @@ export default async function DestinationCityPagePage(
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={<DestinationCityPageSkeleton />}>
|
<Suspense fallback={<DestinationCityPageSkeleton />}>
|
||||||
<DestinationCityPage isMapView={searchParams.view === "map"} />
|
<DestinationCityPage
|
||||||
|
isMapView={searchParams.view === "map"}
|
||||||
|
filterFromUrl={searchParams.filterFromUrl}
|
||||||
|
/>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ import type { LangParams, PageArgs } from "@/types/params"
|
|||||||
|
|
||||||
export { generateMetadata } from "@/utils/generateMetadata"
|
export { generateMetadata } from "@/utils/generateMetadata"
|
||||||
|
|
||||||
export default function DestinationCountryPagePage({}: PageArgs<
|
export default async function DestinationCountryPagePage(
|
||||||
LangParams,
|
props: PageArgs<LangParams, { view?: "map"; filterFromUrl?: string }>
|
||||||
{ view?: "map" }
|
) {
|
||||||
>) {
|
const searchParams = await props.searchParams
|
||||||
if (env.NEW_SITE_LIVE_STATUS === "NOT_LIVE") {
|
if (env.NEW_SITE_LIVE_STATUS === "NOT_LIVE") {
|
||||||
return notFound()
|
return notFound()
|
||||||
}
|
}
|
||||||
@@ -22,6 +22,7 @@ export default function DestinationCountryPagePage({}: PageArgs<
|
|||||||
<Suspense fallback={<DestinationCountryPageSkeleton />}>
|
<Suspense fallback={<DestinationCountryPageSkeleton />}>
|
||||||
<DestinationCountryPage
|
<DestinationCountryPage
|
||||||
// isMapView={searchParams.view === "map"} // Disabled until further notice
|
// isMapView={searchParams.view === "map"} // Disabled until further notice
|
||||||
|
filterFromUrl={searchParams.filterFromUrl}
|
||||||
isMapView={false}
|
isMapView={false}
|
||||||
/>
|
/>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
|
|||||||
@@ -1,57 +0,0 @@
|
|||||||
import {
|
|
||||||
getDestinationCityPagesByCountry,
|
|
||||||
getHotelsByCountry,
|
|
||||||
} from "@/lib/trpc/memoizedRequests"
|
|
||||||
|
|
||||||
import { getIntl } from "@/i18n"
|
|
||||||
import DestinationDataProvider from "@/providers/DestinationDataProvider"
|
|
||||||
|
|
||||||
import type { SortItem } from "@/types/components/destinationFilterAndSort"
|
|
||||||
import type { Country } from "@/types/enums/country"
|
|
||||||
import { SortOption } from "@/types/enums/destinationFilterAndSort"
|
|
||||||
|
|
||||||
interface CityDataContainerProps extends React.PropsWithChildren {
|
|
||||||
country: Country
|
|
||||||
}
|
|
||||||
|
|
||||||
export function preload(country: Country) {
|
|
||||||
void getHotelsByCountry(country)
|
|
||||||
void getDestinationCityPagesByCountry(country)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async function CityDataContainer({
|
|
||||||
country,
|
|
||||||
children,
|
|
||||||
}: CityDataContainerProps) {
|
|
||||||
const intl = await getIntl()
|
|
||||||
const [hotels, cities] = await Promise.all([
|
|
||||||
getHotelsByCountry(country),
|
|
||||||
getDestinationCityPagesByCountry(country),
|
|
||||||
])
|
|
||||||
|
|
||||||
const sortItems: SortItem[] = [
|
|
||||||
{
|
|
||||||
label: intl.formatMessage({
|
|
||||||
defaultMessage: "Recommended",
|
|
||||||
}),
|
|
||||||
value: SortOption.Recommended,
|
|
||||||
isDefault: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: intl.formatMessage({
|
|
||||||
defaultMessage: "Name",
|
|
||||||
}),
|
|
||||||
value: SortOption.Name,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DestinationDataProvider
|
|
||||||
allHotels={hotels}
|
|
||||||
allCities={cities}
|
|
||||||
sortItems={sortItems}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</DestinationDataProvider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -2,32 +2,44 @@ import { notFound } from "next/navigation"
|
|||||||
import { Suspense } from "react"
|
import { Suspense } from "react"
|
||||||
|
|
||||||
import { env } from "@/env/server"
|
import { env } from "@/env/server"
|
||||||
import { getDestinationCityPage } from "@/lib/trpc/memoizedRequests"
|
import {
|
||||||
|
getDestinationCityPage,
|
||||||
|
getHotelsByCityIdentifier,
|
||||||
|
} from "@/lib/trpc/memoizedRequests"
|
||||||
|
import { getFiltersFromHotels } from "@/stores/destination-data/helper"
|
||||||
|
|
||||||
import Breadcrumbs from "@/components/Breadcrumbs"
|
import Breadcrumbs from "@/components/Breadcrumbs"
|
||||||
import BreadcrumbsSkeleton from "@/components/TempDesignSystem/Breadcrumbs/BreadcrumbsSkeleton"
|
import BreadcrumbsSkeleton from "@/components/TempDesignSystem/Breadcrumbs/BreadcrumbsSkeleton"
|
||||||
|
import { getIntl } from "@/i18n"
|
||||||
|
import DestinationDataProvider from "@/providers/DestinationDataProvider"
|
||||||
|
|
||||||
import Blocks from "../Blocks"
|
import Blocks from "../Blocks"
|
||||||
import ExperienceList from "../ExperienceList"
|
import ExperienceList from "../ExperienceList"
|
||||||
import HotelDataContainer, { preload } from "../HotelDataContainer"
|
|
||||||
import HotelListing from "../HotelListing"
|
import HotelListing from "../HotelListing"
|
||||||
import SidebarContentWrapper from "../SidebarContentWrapper"
|
import SidebarContentWrapper from "../SidebarContentWrapper"
|
||||||
import DestinationPageSidePeek from "../Sidepeek"
|
import DestinationPageSidePeek from "../Sidepeek"
|
||||||
import StaticMap from "../StaticMap"
|
import StaticMap from "../StaticMap"
|
||||||
import TopImages from "../TopImages"
|
import TopImages from "../TopImages"
|
||||||
import DestinationTracking from "../Tracking"
|
import DestinationTracking from "../Tracking"
|
||||||
|
import { getHeadingText } from "../utils"
|
||||||
import CityMap from "./CityMap"
|
import CityMap from "./CityMap"
|
||||||
import DestinationCityPageSkeleton from "./DestinationCityPageSkeleton"
|
import DestinationCityPageSkeleton from "./DestinationCityPageSkeleton"
|
||||||
|
|
||||||
import styles from "./destinationCityPage.module.css"
|
import styles from "./destinationCityPage.module.css"
|
||||||
|
|
||||||
|
import type { SortItem } from "@/types/components/destinationFilterAndSort"
|
||||||
|
import { SortOption } from "@/types/enums/destinationFilterAndSort"
|
||||||
|
|
||||||
interface DestinationCityPageProps {
|
interface DestinationCityPageProps {
|
||||||
isMapView: boolean
|
isMapView: boolean
|
||||||
|
filterFromUrl?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function DestinationCityPage({
|
export default async function DestinationCityPage({
|
||||||
isMapView,
|
isMapView,
|
||||||
|
filterFromUrl,
|
||||||
}: DestinationCityPageProps) {
|
}: DestinationCityPageProps) {
|
||||||
|
const intl = await getIntl()
|
||||||
const pageData = await getDestinationCityPage()
|
const pageData = await getDestinationCityPage()
|
||||||
|
|
||||||
if (!pageData) {
|
if (!pageData) {
|
||||||
@@ -46,12 +58,39 @@ export default async function DestinationCityPage({
|
|||||||
destination_settings,
|
destination_settings,
|
||||||
} = destinationCityPage
|
} = destinationCityPage
|
||||||
|
|
||||||
preload(cityIdentifier)
|
const allHotels = await getHotelsByCityIdentifier(cityIdentifier)
|
||||||
|
const allFilters = getFiltersFromHotels(allHotels)
|
||||||
|
const sortItems: SortItem[] = [
|
||||||
|
{
|
||||||
|
label: intl.formatMessage({
|
||||||
|
defaultMessage: "Distance to city center",
|
||||||
|
}),
|
||||||
|
value: SortOption.Distance,
|
||||||
|
isDefault: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: intl.formatMessage({
|
||||||
|
defaultMessage: "Name",
|
||||||
|
}),
|
||||||
|
value: SortOption.Name,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: intl.formatMessage({
|
||||||
|
defaultMessage: "TripAdvisor rating",
|
||||||
|
}),
|
||||||
|
value: SortOption.TripAdvisorRating,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Suspense fallback={<DestinationCityPageSkeleton />}>
|
<Suspense fallback={<DestinationCityPageSkeleton />}>
|
||||||
<HotelDataContainer cityIdentifier={cityIdentifier}>
|
<DestinationDataProvider
|
||||||
|
allHotels={allHotels}
|
||||||
|
allFilters={allFilters}
|
||||||
|
filterFromUrl={filterFromUrl}
|
||||||
|
sortItems={sortItems}
|
||||||
|
>
|
||||||
{isMapView ? (
|
{isMapView ? (
|
||||||
<CityMap
|
<CityMap
|
||||||
mapId={env.GOOGLE_DYNAMIC_MAP_ID}
|
mapId={env.GOOGLE_DYNAMIC_MAP_ID}
|
||||||
@@ -74,15 +113,24 @@ export default async function DestinationCityPage({
|
|||||||
{blocks && <Blocks blocks={blocks} />}
|
{blocks && <Blocks blocks={blocks} />}
|
||||||
</main>
|
</main>
|
||||||
<aside className={styles.sidebar}>
|
<aside className={styles.sidebar}>
|
||||||
<SidebarContentWrapper location={city.name} preamble={preamble}>
|
<SidebarContentWrapper
|
||||||
|
hasActiveFilter={!!filterFromUrl}
|
||||||
|
preamble={preamble}
|
||||||
|
heading={getHeadingText(
|
||||||
|
intl,
|
||||||
|
city.name,
|
||||||
|
allFilters,
|
||||||
|
filterFromUrl
|
||||||
|
)}
|
||||||
|
>
|
||||||
<ExperienceList experiences={experiences} />
|
<ExperienceList experiences={experiences} />
|
||||||
{has_sidepeek && sidepeek_content && (
|
{has_sidepeek && sidepeek_content ? (
|
||||||
<DestinationPageSidePeek
|
<DestinationPageSidePeek
|
||||||
buttonText={sidepeek_button_text}
|
buttonText={sidepeek_button_text}
|
||||||
sidePeekContent={sidepeek_content}
|
sidePeekContent={sidepeek_content}
|
||||||
location={city.name}
|
location={city.name}
|
||||||
/>
|
/>
|
||||||
)}
|
) : null}
|
||||||
|
|
||||||
{destination_settings.city && (
|
{destination_settings.city && (
|
||||||
<StaticMap
|
<StaticMap
|
||||||
@@ -94,7 +142,7 @@ export default async function DestinationCityPage({
|
|||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</HotelDataContainer>
|
</DestinationDataProvider>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
<DestinationTracking pageData={tracking} />
|
<DestinationTracking pageData={tracking} />
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -2,31 +2,44 @@ import { notFound } from "next/navigation"
|
|||||||
import { Suspense } from "react"
|
import { Suspense } from "react"
|
||||||
|
|
||||||
import { env } from "@/env/server"
|
import { env } from "@/env/server"
|
||||||
import { getDestinationCountryPage } from "@/lib/trpc/memoizedRequests"
|
import {
|
||||||
|
getDestinationCityPagesByCountry,
|
||||||
|
getDestinationCountryPage,
|
||||||
|
getHotelsByCountry,
|
||||||
|
} from "@/lib/trpc/memoizedRequests"
|
||||||
|
import { getFiltersFromHotels } from "@/stores/destination-data/helper"
|
||||||
|
|
||||||
import Breadcrumbs from "@/components/Breadcrumbs"
|
import Breadcrumbs from "@/components/Breadcrumbs"
|
||||||
import BreadcrumbsSkeleton from "@/components/TempDesignSystem/Breadcrumbs/BreadcrumbsSkeleton"
|
import BreadcrumbsSkeleton from "@/components/TempDesignSystem/Breadcrumbs/BreadcrumbsSkeleton"
|
||||||
|
import { getIntl } from "@/i18n"
|
||||||
|
import DestinationDataProvider from "@/providers/DestinationDataProvider"
|
||||||
|
|
||||||
import Blocks from "../Blocks"
|
import Blocks from "../Blocks"
|
||||||
import CityDataContainer, { preload } from "../CityDataContainer"
|
|
||||||
import CityListing from "../CityListing"
|
import CityListing from "../CityListing"
|
||||||
import ExperienceList from "../ExperienceList"
|
import ExperienceList from "../ExperienceList"
|
||||||
import SidebarContentWrapper from "../SidebarContentWrapper"
|
import SidebarContentWrapper from "../SidebarContentWrapper"
|
||||||
import DestinationPageSidePeek from "../Sidepeek"
|
import DestinationPageSidePeek from "../Sidepeek"
|
||||||
import TopImages from "../TopImages"
|
import TopImages from "../TopImages"
|
||||||
import DestinationTracking from "../Tracking"
|
import DestinationTracking from "../Tracking"
|
||||||
|
import { getHeadingText } from "../utils"
|
||||||
import CountryMap from "./CountryMap"
|
import CountryMap from "./CountryMap"
|
||||||
import DestinationCountryPageSkeleton from "./DestinationCountryPageSkeleton"
|
import DestinationCountryPageSkeleton from "./DestinationCountryPageSkeleton"
|
||||||
|
|
||||||
import styles from "./destinationCountryPage.module.css"
|
import styles from "./destinationCountryPage.module.css"
|
||||||
|
|
||||||
|
import type { SortItem } from "@/types/components/destinationFilterAndSort"
|
||||||
|
import { SortOption } from "@/types/enums/destinationFilterAndSort"
|
||||||
|
|
||||||
interface DestinationCountryPageProps {
|
interface DestinationCountryPageProps {
|
||||||
isMapView: boolean
|
isMapView: boolean
|
||||||
|
filterFromUrl?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function DestinationCountryPage({
|
export default async function DestinationCountryPage({
|
||||||
isMapView,
|
isMapView,
|
||||||
|
filterFromUrl,
|
||||||
}: DestinationCountryPageProps) {
|
}: DestinationCountryPageProps) {
|
||||||
|
const intl = await getIntl()
|
||||||
const pageData = await getDestinationCountryPage()
|
const pageData = await getDestinationCountryPage()
|
||||||
|
|
||||||
if (!pageData) {
|
if (!pageData) {
|
||||||
@@ -45,12 +58,38 @@ export default async function DestinationCountryPage({
|
|||||||
destination_settings,
|
destination_settings,
|
||||||
} = destinationCountryPage
|
} = destinationCountryPage
|
||||||
|
|
||||||
preload(destination_settings.country)
|
const [allHotels, allCities] = await Promise.all([
|
||||||
|
getHotelsByCountry(destination_settings.country),
|
||||||
|
getDestinationCityPagesByCountry(destination_settings.country),
|
||||||
|
])
|
||||||
|
const allFilters = getFiltersFromHotels(allHotels)
|
||||||
|
|
||||||
|
const sortItems: SortItem[] = [
|
||||||
|
{
|
||||||
|
label: intl.formatMessage({
|
||||||
|
defaultMessage: "Recommended",
|
||||||
|
}),
|
||||||
|
value: SortOption.Recommended,
|
||||||
|
isDefault: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: intl.formatMessage({
|
||||||
|
defaultMessage: "Name",
|
||||||
|
}),
|
||||||
|
value: SortOption.Name,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Suspense fallback={<DestinationCountryPageSkeleton />}>
|
<Suspense fallback={<DestinationCountryPageSkeleton />}>
|
||||||
<CityDataContainer country={destination_settings.country}>
|
<DestinationDataProvider
|
||||||
|
allHotels={allHotels}
|
||||||
|
allCities={allCities}
|
||||||
|
allFilters={allFilters}
|
||||||
|
filterFromUrl={filterFromUrl}
|
||||||
|
sortItems={sortItems}
|
||||||
|
>
|
||||||
{isMapView ? (
|
{isMapView ? (
|
||||||
<CountryMap
|
<CountryMap
|
||||||
mapId={env.GOOGLE_DYNAMIC_MAP_ID}
|
mapId={env.GOOGLE_DYNAMIC_MAP_ID}
|
||||||
@@ -77,22 +116,28 @@ export default async function DestinationCountryPage({
|
|||||||
</main>
|
</main>
|
||||||
<aside className={styles.sidebar}>
|
<aside className={styles.sidebar}>
|
||||||
<SidebarContentWrapper
|
<SidebarContentWrapper
|
||||||
location={translatedCountry}
|
hasActiveFilter={!!filterFromUrl}
|
||||||
preamble={preamble}
|
preamble={preamble}
|
||||||
|
heading={getHeadingText(
|
||||||
|
intl,
|
||||||
|
translatedCountry,
|
||||||
|
allFilters,
|
||||||
|
filterFromUrl
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<ExperienceList experiences={experiences} />
|
<ExperienceList experiences={experiences} />
|
||||||
{has_sidepeek && sidepeek_content && (
|
{has_sidepeek && sidepeek_content ? (
|
||||||
<DestinationPageSidePeek
|
<DestinationPageSidePeek
|
||||||
buttonText={sidepeek_button_text}
|
buttonText={sidepeek_button_text}
|
||||||
sidePeekContent={sidepeek_content}
|
sidePeekContent={sidepeek_content}
|
||||||
location={translatedCountry}
|
location={translatedCountry}
|
||||||
/>
|
/>
|
||||||
)}
|
) : null}
|
||||||
</SidebarContentWrapper>
|
</SidebarContentWrapper>
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</CityDataContainer>
|
</DestinationDataProvider>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
<DestinationTracking pageData={tracking} />
|
<DestinationTracking pageData={tracking} />
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -14,6 +14,11 @@ interface ExperienceListProps {
|
|||||||
|
|
||||||
export default function ExperienceList({ experiences }: ExperienceListProps) {
|
export default function ExperienceList({ experiences }: ExperienceListProps) {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
|
|
||||||
|
if (!experiences.length) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
const experienceList = mapExperiencesToListData(experiences, intl)
|
const experienceList = mapExperiencesToListData(experiences, intl)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
import { getHotelsByCityIdentifier } from "@/lib/trpc/memoizedRequests"
|
|
||||||
|
|
||||||
import { getIntl } from "@/i18n"
|
|
||||||
import DestinationDataProvider from "@/providers/DestinationDataProvider"
|
|
||||||
|
|
||||||
import type { SortItem } from "@/types/components/destinationFilterAndSort"
|
|
||||||
import { SortOption } from "@/types/enums/destinationFilterAndSort"
|
|
||||||
|
|
||||||
interface HotelDataContainerProps extends React.PropsWithChildren {
|
|
||||||
cityIdentifier: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export function preload(cityIdentifier: string) {
|
|
||||||
void getHotelsByCityIdentifier(cityIdentifier)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async function HotelDataContainer({
|
|
||||||
cityIdentifier,
|
|
||||||
children,
|
|
||||||
}: HotelDataContainerProps) {
|
|
||||||
const intl = await getIntl()
|
|
||||||
const hotels = await getHotelsByCityIdentifier(cityIdentifier)
|
|
||||||
|
|
||||||
const sortItems: SortItem[] = [
|
|
||||||
{
|
|
||||||
label: intl.formatMessage({
|
|
||||||
defaultMessage: "Distance to city center",
|
|
||||||
}),
|
|
||||||
value: SortOption.Distance,
|
|
||||||
isDefault: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: intl.formatMessage({
|
|
||||||
defaultMessage: "Name",
|
|
||||||
}),
|
|
||||||
value: SortOption.Name,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: intl.formatMessage({
|
|
||||||
defaultMessage: "TripAdvisor rating",
|
|
||||||
}),
|
|
||||||
value: SortOption.TripAdvisorRating,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DestinationDataProvider allHotels={hotels} sortItems={sortItems}>
|
|
||||||
{children}
|
|
||||||
</DestinationDataProvider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,52 +1,43 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useRef } from "react"
|
import { useRef } from "react"
|
||||||
import { useIntl } from "react-intl"
|
|
||||||
|
|
||||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
|
|
||||||
import { useDestinationDataStore } from "@/stores/destination-data"
|
|
||||||
import { StickyElementNameEnum } from "@/stores/sticky-position"
|
import { StickyElementNameEnum } from "@/stores/sticky-position"
|
||||||
|
|
||||||
import useStickyPosition from "@/hooks/useStickyPosition"
|
import useStickyPosition from "@/hooks/useStickyPosition"
|
||||||
|
|
||||||
import { getHeadingText } from "../utils"
|
|
||||||
|
|
||||||
import styles from "./sidebarContentWrapper.module.css"
|
import styles from "./sidebarContentWrapper.module.css"
|
||||||
|
|
||||||
interface SidebarContentWrapperProps extends React.PropsWithChildren {
|
interface SidebarContentWrapperProps extends React.PropsWithChildren {
|
||||||
location: string
|
|
||||||
preamble: string
|
preamble: string
|
||||||
|
hasActiveFilter: boolean
|
||||||
|
heading: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SidebarContentWrapper({
|
export default function SidebarContentWrapper({
|
||||||
location,
|
|
||||||
preamble,
|
preamble,
|
||||||
|
hasActiveFilter,
|
||||||
|
heading,
|
||||||
children,
|
children,
|
||||||
}: SidebarContentWrapperProps) {
|
}: SidebarContentWrapperProps) {
|
||||||
const intl = useIntl()
|
|
||||||
const sidebarRef = useRef<HTMLDivElement>(null)
|
const sidebarRef = useRef<HTMLDivElement>(null)
|
||||||
useStickyPosition({
|
useStickyPosition({
|
||||||
ref: sidebarRef,
|
ref: sidebarRef,
|
||||||
name: StickyElementNameEnum.DESTINATION_SIDEBAR,
|
name: StickyElementNameEnum.DESTINATION_SIDEBAR,
|
||||||
})
|
})
|
||||||
const { activeFilters, allFilters } = useDestinationDataStore((state) => ({
|
|
||||||
activeFilters: state.activeFilters,
|
|
||||||
allFilters: state.allFilters,
|
|
||||||
}))
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={sidebarRef} className={styles.sidebarContent}>
|
<div ref={sidebarRef} className={styles.sidebarContent}>
|
||||||
<Typography variant="Title/md">
|
<Typography variant="Title/md">
|
||||||
<h1 className={styles.heading}>
|
<h1 className={styles.heading}>{heading}</h1>
|
||||||
{getHeadingText(intl, location, allFilters, activeFilters[0])}
|
|
||||||
</h1>
|
|
||||||
</Typography>
|
</Typography>
|
||||||
{!activeFilters.length && (
|
{!hasActiveFilter ? (
|
||||||
<Typography variant="Body/Paragraph/mdRegular">
|
<Typography variant="Body/Paragraph/mdRegular">
|
||||||
<p className={styles.text}>{preamble}</p>
|
<p className={styles.text}>{preamble}</p>
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
) : null}
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ import type { DestinationDataProviderProps } from "@/types/providers/destination
|
|||||||
export default function DestinationDataProvider({
|
export default function DestinationDataProvider({
|
||||||
allCities = [],
|
allCities = [],
|
||||||
allHotels,
|
allHotels,
|
||||||
|
allFilters,
|
||||||
|
filterFromUrl,
|
||||||
sortItems,
|
sortItems,
|
||||||
children,
|
children,
|
||||||
}: DestinationDataProviderProps) {
|
}: DestinationDataProviderProps) {
|
||||||
@@ -25,6 +27,8 @@ export default function DestinationDataProvider({
|
|||||||
storeRef.current = createDestinationDataStore({
|
storeRef.current = createDestinationDataStore({
|
||||||
allCities,
|
allCities,
|
||||||
allHotels,
|
allHotels,
|
||||||
|
allFilters,
|
||||||
|
filterFromUrl,
|
||||||
pathname,
|
pathname,
|
||||||
sortItems,
|
sortItems,
|
||||||
searchParams,
|
searchParams,
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import {
|
|||||||
getBasePathNameWithoutFilters,
|
getBasePathNameWithoutFilters,
|
||||||
getFilteredCities,
|
getFilteredCities,
|
||||||
getFilteredHotels,
|
getFilteredHotels,
|
||||||
getFiltersFromHotels,
|
|
||||||
getSortedCities,
|
getSortedCities,
|
||||||
getSortedHotels,
|
getSortedHotels,
|
||||||
isValidSortOption,
|
isValidSortOption,
|
||||||
@@ -27,18 +26,19 @@ import type {
|
|||||||
export function createDestinationDataStore({
|
export function createDestinationDataStore({
|
||||||
allCities,
|
allCities,
|
||||||
allHotels,
|
allHotels,
|
||||||
|
allFilters,
|
||||||
|
filterFromUrl,
|
||||||
pathname,
|
pathname,
|
||||||
sortItems,
|
sortItems,
|
||||||
searchParams,
|
searchParams,
|
||||||
}: InitialState) {
|
}: InitialState) {
|
||||||
const defaultSort =
|
const defaultSort =
|
||||||
sortItems.find((s) => s.isDefault)?.value ?? sortItems[0].value
|
sortItems.find((s) => s.isDefault)?.value ?? sortItems[0].value
|
||||||
const allFilters = getFiltersFromHotels(allHotels)
|
|
||||||
const allFilterSlugs = Object.values(allFilters).flatMap((filter: Filter[]) =>
|
const allFilterSlugs = Object.values(allFilters).flatMap((filter: Filter[]) =>
|
||||||
filter.map((f) => f.slug)
|
filter.map((f) => f.slug)
|
||||||
)
|
)
|
||||||
|
|
||||||
const activeFilters: string[] = []
|
const activeFilters: string[] = filterFromUrl ? [filterFromUrl] : []
|
||||||
const basePathnameWithoutFilters = getBasePathNameWithoutFilters(
|
const basePathnameWithoutFilters = getBasePathNameWithoutFilters(
|
||||||
pathname,
|
pathname,
|
||||||
allFilterSlugs
|
allFilterSlugs
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
import type { DestinationPagesHotelData } from "@/types/hotel"
|
import type { DestinationPagesHotelData } from "@/types/hotel"
|
||||||
import type { SortItem } from "../components/destinationFilterAndSort"
|
import type {
|
||||||
|
CategorizedFilters,
|
||||||
|
SortItem,
|
||||||
|
} from "../components/destinationFilterAndSort"
|
||||||
import type { DestinationCityListItem } from "../trpc/routers/contentstack/destinationCityPage"
|
import type { DestinationCityListItem } from "../trpc/routers/contentstack/destinationCityPage"
|
||||||
|
|
||||||
export interface DestinationDataProviderProps extends React.PropsWithChildren {
|
export interface DestinationDataProviderProps extends React.PropsWithChildren {
|
||||||
allHotels: DestinationPagesHotelData[]
|
allHotels: DestinationPagesHotelData[]
|
||||||
allCities?: DestinationCityListItem[]
|
allCities?: DestinationCityListItem[]
|
||||||
|
allFilters: CategorizedFilters
|
||||||
|
filterFromUrl?: string
|
||||||
sortItems: SortItem[]
|
sortItems: SortItem[]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,11 @@ export interface DestinationDataState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface InitialState
|
export interface InitialState
|
||||||
extends Pick<DestinationDataState, "allHotels" | "allCities" | "sortItems"> {
|
extends Pick<
|
||||||
|
DestinationDataState,
|
||||||
|
"allHotels" | "allCities" | "sortItems" | "allFilters"
|
||||||
|
> {
|
||||||
pathname: string
|
pathname: string
|
||||||
|
filterFromUrl?: string
|
||||||
searchParams: ReadonlyURLSearchParams
|
searchParams: ReadonlyURLSearchParams
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user