feat(BOOK-56): Added content related to destination filters

Approved-by: Chuma Mcphoy (We Ahead)
This commit is contained in:
Erik Tiekstra
2025-09-25 08:10:30 +00:00
parent 9032789fd0
commit 7714761c77
21 changed files with 379 additions and 172 deletions

View File

@@ -10,15 +10,17 @@ import AccordionSection from "@/components/Blocks/Accordion"
import type { BlocksProps } from "@/types/components/blocks"
export default function Blocks({ blocks }: BlocksProps) {
const { activeFilters } = useDestinationDataStore((state) => ({
activeFilters: state.activeFilters,
const { activeSeoFilter } = useDestinationDataStore((state) => ({
activeSeoFilter: state.activeSeoFilter,
}))
if (activeFilters.length) {
const activeBlocks = activeSeoFilter?.blocks ? activeSeoFilter.blocks : blocks
if (!activeBlocks.length) {
return null
}
return blocks.map((block, idx) => {
return activeBlocks.map((block, idx) => {
switch (block.typename) {
case BlocksEnums.block.Accordion:
return (

View File

@@ -8,7 +8,7 @@ import { Typography } from "@scandic-hotels/design-system/Typography"
import { useDestinationDataStore } from "@/stores/destination-data"
import CityMapContainer from "../../Map/CityMapContainer"
import { getCityHeadingText } from "../../utils"
import { getHeadingText } from "../../utils"
import { BackToCities } from "./BackToCitiesLink"
import HotelList from "./HotelList"
@@ -32,11 +32,10 @@ export default function CityMap({
defaultLocation,
}: CityMapProps) {
const intl = useIntl()
const { activeHotels, allFilters, filterFromUrl } = useDestinationDataStore(
const { activeHotels, activeSeoFilter } = useDestinationDataStore(
(state) => ({
activeHotels: state.activeHotels,
allFilters: state.allFilters,
filterFromUrl: state.filterFromUrl,
activeSeoFilter: state.activeSeoFilter,
})
)
const [fromCountryPage, setIsFromCountryPage] = useState(false)
@@ -58,7 +57,7 @@ export default function CityMap({
{fromCountryPage ? <BackToCities /> : null}
<Typography variant="Title/sm">
<h1 className={styles.title}>
{getCityHeadingText(intl, city.name, allFilters, filterFromUrl)}
{getHeadingText(intl, city.name, "city", activeSeoFilter)}
</h1>
</Typography>
</span>

View File

@@ -117,12 +117,12 @@ export default async function DestinationCityPage({
</div>
<main className={styles.mainContent}>
<HotelListing />
{blocks && <Blocks blocks={blocks} />}
<Blocks blocks={blocks || []} />
<SeoFilters seoFilters={seo_filters} location={city.name} />
</main>
<aside className={styles.sidebar}>
<SidebarContentWrapper
preamble={preamble}
defaultPreamble={preamble}
location={city.name}
pageType="city"
>

View File

@@ -7,7 +7,7 @@ import { Typography } from "@scandic-hotels/design-system/Typography"
import { useDestinationDataStore } from "@/stores/destination-data"
import CountryMapContainer from "../../Map/CountryMapContainer"
import { getCountryHeadingText } from "../../utils"
import { getHeadingText } from "../../utils"
import CityList from "./CityList"
import styles from "./countryMap.module.css"
@@ -28,11 +28,10 @@ export default function CountryMap({
defaultLocation,
}: CountryMapProps) {
const intl = useIntl()
const { activeCities, allFilters, filterFromUrl } = useDestinationDataStore(
const { activeCities, activeSeoFilter } = useDestinationDataStore(
(state) => ({
activeCities: state.activeCities,
allFilters: state.allFilters,
filterFromUrl: state.filterFromUrl,
activeSeoFilter: state.activeSeoFilter,
})
)
@@ -45,7 +44,7 @@ export default function CountryMap({
>
<Typography variant="Title/sm">
<h1 className={styles.title}>
{getCountryHeadingText(intl, country, allFilters, filterFromUrl)}
{getHeadingText(intl, country, "country", activeSeoFilter)}
</h1>
</Typography>
<CityList />

View File

@@ -133,7 +133,7 @@ export default async function DestinationCountryPage({
</div>
<main className={styles.mainContent}>
<CityListing />
{blocks && <Blocks blocks={blocks} />}
<Blocks blocks={blocks || []} />
<SeoFilters
seoFilters={seo_filters}
location={translatedCountry}
@@ -141,7 +141,7 @@ export default async function DestinationCountryPage({
</main>
<aside className={styles.sidebar}>
<SidebarContentWrapper
preamble={preamble}
defaultPreamble={preamble}
location={translatedCountry}
pageType="country"
>

View File

@@ -10,13 +10,10 @@ import { useDestinationDataStore } from "@/stores/destination-data"
import styles from "./seoFilters.module.css"
import type { HotelFilter } from "@scandic-hotels/trpc/types/hotel"
import type { DestinationFilters } from "@scandic-hotels/trpc/types/destinationsData"
interface SeoFiltersProps {
seoFilters: {
facilityFilters: HotelFilter[]
surroundingsFilters: HotelFilter[]
} | null
seoFilters: DestinationFilters
location: string
}
@@ -25,11 +22,6 @@ export function SeoFilters({ seoFilters, location }: SeoFiltersProps) {
const { basePath } = useDestinationDataStore((state) => ({
basePath: state.basePathnameWithoutFilters,
}))
if (!seoFilters) {
return null
}
const { facilityFilters, surroundingsFilters } = seoFilters
if (!facilityFilters.length && !surroundingsFilters.length) {
@@ -48,7 +40,7 @@ export function SeoFilters({ seoFilters, location }: SeoFiltersProps) {
showAsSubtitle
>
<ul className={styles.filterList}>
{facilityFilters.map((filter) => (
{facilityFilters.map(({ filter }) => (
<li key={filter.id}>
<Link
href={`${basePath}/${filter.slug}`}
@@ -71,7 +63,7 @@ export function SeoFilters({ seoFilters, location }: SeoFiltersProps) {
showAsSubtitle
>
<ul className={styles.filterList}>
{surroundingsFilters.map((filter) => (
{surroundingsFilters.map(({ filter }) => (
<li key={filter.id}>
<Link
href={`${basePath}/${filter.slug}`}

View File

@@ -9,36 +9,37 @@ import { Typography } from "@scandic-hotels/design-system/Typography"
import { useDestinationDataStore } from "@/stores/destination-data"
import { getCityHeadingText, getCountryHeadingText } from "../utils"
import {
getHeadingText,
getPreambleText,
} from "@/components/ContentType/DestinationPage/utils"
import styles from "./sidebarContentWrapper.module.css"
interface SidebarContentWrapperProps extends React.PropsWithChildren {
preamble: string
defaultPreamble: string
location: string
pageType: "country" | "city"
}
export default function SidebarContentWrapper({
preamble,
defaultPreamble,
location,
pageType,
children,
}: SidebarContentWrapperProps) {
const intl = useIntl()
const sidebarRef = useRef<HTMLDivElement>(null)
const { allFilters, filterFromUrl } = useDestinationDataStore((state) => ({
allFilters: state.allFilters,
filterFromUrl: state.filterFromUrl,
const { activeSeoFilter } = useDestinationDataStore((state) => ({
activeSeoFilter: state.activeSeoFilter,
}))
useStickyPosition({
ref: sidebarRef,
name: StickyElementNameEnum.DESTINATION_SIDEBAR,
})
const heading =
pageType === "country"
? getCountryHeadingText(intl, location, allFilters, filterFromUrl)
: getCityHeadingText(intl, location, allFilters, filterFromUrl)
const heading = getHeadingText(intl, location, pageType, activeSeoFilter)
const preamble = getPreambleText(defaultPreamble, activeSeoFilter)
return (
<div ref={sidebarRef} className={styles.sidebarContent}>
@@ -46,11 +47,9 @@ export default function SidebarContentWrapper({
<Typography variant="Title/md">
<h1 className={styles.heading}>{heading}</h1>
</Typography>
{!filterFromUrl ? (
<Typography variant="Body/Paragraph/mdRegular">
<p>{preamble}</p>
</Typography>
) : null}
<Typography variant="Body/Paragraph/mdRegular">
<p>{preamble}</p>
</Typography>
</div>
{children}
</div>

View File

@@ -1,81 +1,41 @@
import type {
CategorizedHotelFilters,
HotelFilter,
} from "@scandic-hotels/trpc/types/hotel"
import type { DestinationFilter } from "@scandic-hotels/trpc/types/destinationsData"
import type { IntlShape } from "react-intl"
export function getCityHeadingText(
export function getHeadingText(
intl: IntlShape,
location: string,
allFilters: CategorizedHotelFilters,
filterFromUrl: HotelFilter | null
pageType: "country" | "city",
activeSeoFilter: DestinationFilter | null
) {
if (filterFromUrl) {
const facilityFilter = allFilters.facilityFilters.find(
(f) => f.id === filterFromUrl.id
)
const surroudingsFilter = allFilters.surroundingsFilters.find(
(f) => f.id === filterFromUrl.id
)
const defaultHeading =
pageType === "country"
? intl.formatMessage(
{
defaultMessage: "Destinations in {location}",
},
{ location }
)
: intl.formatMessage(
{
defaultMessage: "Hotels in {location}",
},
{ location }
)
if (facilityFilter) {
return intl.formatMessage(
{
defaultMessage: "Hotels with {filter} in {location}",
},
{ location, filter: facilityFilter.name }
)
} else if (surroudingsFilter) {
return intl.formatMessage(
{
defaultMessage: "Hotels near {filter} in {location}",
},
{ location, filter: surroudingsFilter.name }
)
}
if (activeSeoFilter?.heading) {
return activeSeoFilter.heading
}
return intl.formatMessage(
{
defaultMessage: "Hotels in {location}",
},
{ location }
)
return defaultHeading
}
export function getCountryHeadingText(
intl: IntlShape,
location: string,
allFilters: CategorizedHotelFilters,
filterFromUrl: HotelFilter | null
export function getPreambleText(
defaultPreamble: string,
activeSeoFilter: DestinationFilter | null
) {
if (filterFromUrl) {
const facilityFilter = allFilters.facilityFilters.find(
(f) => f.id === filterFromUrl.id
)
const surroudingsFilter = allFilters.surroundingsFilters.find(
(f) => f.id === filterFromUrl.id
)
if (facilityFilter) {
return intl.formatMessage(
{
defaultMessage: "Destinations with {filter} in {location}",
},
{ location, filter: facilityFilter.name }
)
} else if (surroudingsFilter) {
return intl.formatMessage(
{
defaultMessage: "Destinations near {filter} in {location}",
},
{ location, filter: surroudingsFilter.name }
)
}
if (activeSeoFilter) {
return activeSeoFilter.preamble || null
}
return intl.formatMessage(
{
defaultMessage: "Destinations in {location}",
},
{ location }
)
return defaultPreamble
}