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

@@ -609,3 +609,108 @@ fragment SpecificAccordion_CampaignPageRefs on CampaignPageBlocksAccordionBlockA
}
}
}
fragment Accordion_DestinationFilterBlocks on DestinationFilterBlocksAccordion {
__typename
accordion {
title
accordions {
__typename
...GlobalAccordion_DestinationFilterBlocks
...SpecificAccordion_DestinationFilterBlocks
}
}
}
fragment GlobalAccordion_DestinationFilterBlocks on DestinationFilterBlocksAccordionBlockAccordionsGlobalAccordion {
__typename
global_accordion {
global_accordionConnection {
edges {
node {
...AccordionBlock
}
}
}
}
}
fragment SpecificAccordion_DestinationFilterBlocks on DestinationFilterBlocksAccordionBlockAccordionsSpecificAccordion {
__typename
specific_accordion {
questions {
question
answer {
json
embedded_itemsConnection {
edges {
node {
__typename
...SysAsset
...AccountPageLink
...CampaignOverviewPageLink
...CampaignPageLink
...CollectionPageLink
...ContentPageLink
...DestinationCityPageLink
...DestinationCountryPageLink
...DestinationOverviewPageLink
...HotelPageLink
...LoyaltyPageLink
...StartPageLink
}
}
}
}
}
}
}
fragment Accordion_DestinationFilterBlocksRefs on DestinationFilterBlocksAccordion {
accordion {
accordions {
__typename
...GlobalAccordion_DestinationFilterBlocksRefs
...SpecificAccordion_DestinationFilterBlocksRefs
}
}
}
fragment GlobalAccordion_DestinationFilterBlocksRefs on DestinationFilterBlocksAccordionBlockAccordionsGlobalAccordion {
global_accordion {
global_accordionConnection {
edges {
node {
...AccordionBlockRefs
}
}
}
}
}
fragment SpecificAccordion_DestinationFilterBlocksRefs on DestinationFilterBlocksAccordionBlockAccordionsSpecificAccordion {
specific_accordion {
questions {
answer {
embedded_itemsConnection {
edges {
node {
__typename
...AccountPageRef
...CampaignOverviewPageRef
...CampaignPageRef
...CollectionPageRef
...ContentPageRef
...DestinationCityPageRef
...DestinationCountryPageRef
...DestinationOverviewPageRef
...HotelPageRef
...LoyaltyPageRef
...StartPageRef
}
}
}
}
}
}
}

View File

@@ -233,3 +233,54 @@ fragment Content_DestinationCountryPageRefs on DestinationCountryPageBlocksConte
}
}
}
fragment Content_DestinationFilterBlocks on DestinationFilterBlocksContent {
content {
content {
embedded_itemsConnection {
edges {
node {
__typename
...AccountPageLink
...CampaignOverviewPageLink
...CampaignPageLink
...CollectionPageLink
...ContentPageLink
...DestinationCityPageLink
...DestinationCountryPageLink
...DestinationOverviewPageLink
...HotelPageLink
...LoyaltyPageLink
...StartPageLink
}
}
}
json
}
}
}
fragment Content_DestinationFilterBlocksRefs on DestinationFilterBlocksContent {
content {
content {
embedded_itemsConnection {
edges {
node {
__typename
...AccountPageRef
...CampaignOverviewPageRef
...CampaignPageRef
...CollectionPageRef
...ContentPageRef
...DestinationCityPageRef
...DestinationCountryPageRef
...DestinationOverviewPageRef
...HotelPageRef
...LoyaltyPageRef
...StartPageRef
}
}
}
}
}
}

View File

@@ -0,0 +1,38 @@
#import "./System.graphql"
#import "./HotelFilter.graphql"
#import "./Blocks/Accordion.graphql"
#import "./Blocks/Content.graphql"
fragment DestinationFilter on DestinationFilter {
heading
preamble
blocks {
__typename
...Accordion_DestinationFilterBlocks
...Content_DestinationFilterBlocks
}
filterConnection {
edges {
node {
...HotelFilter
}
}
}
}
fragment DestinationFilterRef on DestinationFilter {
blocks {
__typename
...Accordion_DestinationFilterBlocksRefs
...Content_DestinationFilterBlocksRefs
}
filterConnection {
edges {
node {
...HotelFilterRef
}
}
}
}

View File

@@ -1,6 +1,6 @@
#import "../../Fragments/System.graphql"
#import "../../Fragments/HotelFilter.graphql"
#import "../../Fragments/DestinationFilter.graphql"
#import "../../Fragments/Blocks/Accordion.graphql"
#import "../../Fragments/Blocks/Content.graphql"
@@ -85,13 +85,7 @@ query GetDestinationCityPage($locale: String!, $uid: String!) {
...Content_DestinationCityPage
}
seo_filters {
filterConnection {
edges {
node {
...HotelFilter
}
}
}
...DestinationFilter
}
system {
...System
@@ -147,13 +141,7 @@ query GetDestinationCityPageRefs($locale: String!, $uid: String!) {
...Content_DestinationCityPageRefs
}
seo_filters {
filterConnection {
edges {
node {
...HotelFilterRef
}
}
}
...DestinationFilterRef
}
system {
...System

View File

@@ -1,6 +1,6 @@
#import "../../Fragments/System.graphql"
#import "../../Fragments/HotelFilter.graphql"
#import "../../Fragments/DestinationFilter.graphql"
#import "../../Fragments/Blocks/Accordion.graphql"
#import "../../Fragments/Blocks/Content.graphql"
@@ -80,13 +80,7 @@ query GetDestinationCountryPage($locale: String!, $uid: String!) {
...Content_DestinationCountryPage
}
seo_filters {
filterConnection {
edges {
node {
...HotelFilter
}
}
}
...DestinationFilter
}
system {
...System
@@ -129,13 +123,7 @@ query GetDestinationCountryPageRefs($locale: String!, $uid: String!) {
...Content_DestinationCountryPageRefs
}
seo_filters {
filterConnection {
edges {
node {
...HotelFilterRef
}
}
}
...DestinationFilterRef
}
system {
...System

View File

@@ -45,6 +45,8 @@ enum AccordionEnum {
DestinationCityPageBlocksAccordionBlockAccordionsSpecificAccordion = "DestinationCityPageBlocksAccordionBlockAccordionsSpecificAccordion",
DestinationCountryPageBlocksAccordionBlockAccordionsGlobalAccordion = "DestinationCountryPageBlocksAccordionBlockAccordionsGlobalAccordion",
DestinationCountryPageBlocksAccordionBlockAccordionsSpecificAccordion = "DestinationCountryPageBlocksAccordionBlockAccordionsSpecificAccordion",
DestinationFilterBlocksAccordionBlockAccordionsSpecificAccordion = "DestinationFilterBlocksAccordionBlockAccordionsSpecificAccordion",
DestinationFilterBlocksAccordionBlockAccordionsGlobalAccordion = "DestinationFilterBlocksAccordionBlockAccordionsGlobalAccordion",
}
export const accordionSchema = z.object({
@@ -89,6 +91,7 @@ export const accordionSchema = z.object({
case AccordionEnum.ContentPageBlocksAccordionBlockAccordionsGlobalAccordion:
case AccordionEnum.DestinationCityPageBlocksAccordionBlockAccordionsGlobalAccordion:
case AccordionEnum.DestinationCountryPageBlocksAccordionBlockAccordionsGlobalAccordion:
case AccordionEnum.DestinationFilterBlocksAccordionBlockAccordionsGlobalAccordion:
return (
acc.global_accordion?.global_accordionConnection.edges.flatMap(
({ node: accordionConnection }) => {
@@ -101,6 +104,7 @@ export const accordionSchema = z.object({
case AccordionEnum.ContentPageBlocksAccordionBlockAccordionsSpecificAccordion:
case AccordionEnum.DestinationCityPageBlocksAccordionBlockAccordionsSpecificAccordion:
case AccordionEnum.DestinationCountryPageBlocksAccordionBlockAccordionsSpecificAccordion:
case AccordionEnum.DestinationFilterBlocksAccordionBlockAccordionsSpecificAccordion:
return acc.specific_accordion?.questions || []
default:
return null
@@ -170,6 +174,7 @@ export const accordionRefsSchema = z.object({
case AccordionEnum.ContentPageBlocksAccordionBlockAccordionsGlobalAccordion:
case AccordionEnum.DestinationCityPageBlocksAccordionBlockAccordionsGlobalAccordion:
case AccordionEnum.DestinationCountryPageBlocksAccordionBlockAccordionsGlobalAccordion:
case AccordionEnum.DestinationFilterBlocksAccordionBlockAccordionsGlobalAccordion:
return (
accordion.global_accordion?.global_accordionConnection.edges.flatMap(
({ node: accordionConnection }) => {
@@ -184,6 +189,7 @@ export const accordionRefsSchema = z.object({
case AccordionEnum.CampaignPageBlocksAccordionBlockAccordionsSpecificAccordion:
case AccordionEnum.DestinationCityPageBlocksAccordionBlockAccordionsSpecificAccordion:
case AccordionEnum.DestinationCountryPageBlocksAccordionBlockAccordionsSpecificAccordion:
case AccordionEnum.DestinationFilterBlocksAccordionBlockAccordionsSpecificAccordion:
return (
accordion.specific_accordion?.questions.flatMap((question) =>
question.answer.embedded_itemsConnection.edges.flatMap(

View File

@@ -3,11 +3,39 @@ import { z } from "zod"
import { FacilityEnum } from "@scandic-hotels/common/constants/facilities"
import { isDefined } from "@scandic-hotels/common/utils/isDefined"
import { DestinationFilterBlocksEnum } from "../../../types/destinationsData"
import { discriminatedUnionArray } from "../../../utils/discriminatedUnion"
import { accordionSchema } from "./blocks/accordion"
import { contentSchema } from "./blocks/content"
import { systemSchema } from "./system"
export const destinationFilterBlockContent = z
.object({
__typename: z.literal(
DestinationFilterBlocksEnum.ContentStack.blocks.Content
),
})
.merge(contentSchema)
export const destinationFilterBlockAccordion = z
.object({
__typename: z.literal(
DestinationFilterBlocksEnum.ContentStack.blocks.Accordion
),
})
.merge(accordionSchema)
export const blocksSchema = z.discriminatedUnion("__typename", [
destinationFilterBlockAccordion,
destinationFilterBlockContent,
])
export const destinationFiltersSchema = z
.array(
z.object({
heading: z.string().nullish(),
preamble: z.string().nullish(),
blocks: discriminatedUnionArray(blocksSchema.options).nullish(),
filterConnection: z.object({
edges: z.array(
z.object({
@@ -50,27 +78,39 @@ export const destinationFiltersRefsSchema = z
export function transformDestinationFiltersResponse(
data: typeof destinationFiltersSchema._type
) {
const filters = data
?.map(({ filterConnection }) => filterConnection.edges[0]?.node)
const filterData = data
?.map(({ filterConnection, heading, preamble, blocks }) => {
const filter = filterConnection.edges[0]?.node
if (!filter) {
return null
}
return {
heading,
preamble,
blocks,
filter: {
id: filter.facility_id,
name: filter.title,
filterType: filter.category,
slug: filter.slug,
sortOrder: 0,
},
}
})
.filter(isDefined)
if (!data || !filters?.length) {
return null
if (!data || !filterData?.length) {
return {
facilityFilters: [],
surroundingsFilters: [],
}
}
const transformedFilters = filters.map((filter) => ({
id: filter.facility_id,
name: filter.title,
filterType: filter.category,
slug: filter.slug,
sortOrder: 0,
}))
const facilityFilters = transformedFilters.filter(
(f) => f.filterType === "facility"
const facilityFilters = filterData.filter(
(f) => f.filter.filterType === "facility"
)
const surroundingsFilters = transformedFilters.filter(
(f) => f.filterType === "surroundings"
const surroundingsFilters = filterData.filter(
(f) => f.filter.filterType === "surroundings"
)
return {

View File

@@ -1,4 +1,6 @@
import type { HotelFilter } from "./hotel"
import type { z } from "zod"
import type { transformedDestinationFiltersSchema } from "../routers/contentstack/schemas/destinationFilters"
export type City = {
id: string
@@ -17,7 +19,18 @@ export type DestinationCountry = {
export type DestinationsData = DestinationCountry[]
export type SEOFilters = {
facilityFilters: HotelFilter[]
surroundingsFilters: HotelFilter[]
export namespace DestinationFilterBlocksEnum {
export namespace ContentStack {
export const enum blocks {
Accordion = "DestinationFilterBlocksAccordion",
Content = "DestinationFilterBlocksContent",
}
}
}
export type DestinationFilters = z.output<
typeof transformedDestinationFiltersSchema
>
export type DestinationFilter =
DestinationFilters[keyof DestinationFilters][number]

View File

@@ -1,7 +1,7 @@
import type { FacilityEnum } from "@scandic-hotels/common/constants/facilities"
import type { Lang } from "@scandic-hotels/common/constants/language"
import type { SEOFilters } from "../types/destinationsData"
import type { DestinationFilters } from "../types/destinationsData"
import type {
CategorizedHotelFilters,
HotelFilter,
@@ -39,17 +39,19 @@ function sortFilters(filters: HotelFilter[]): HotelFilter[] {
// In case of duplicates, the SEO filter takes precedence.
function mergeAndDeduplicate(
hotelFilters: HotelFilter[],
seoFilters: HotelFilter[]
seoFilters:
| DestinationFilters["facilityFilters"]
| DestinationFilters["surroundingsFilters"]
): HotelFilter[] {
const map = new Map<FacilityEnum, HotelFilter>()
hotelFilters.forEach((filter) => map.set(filter.id, filter))
seoFilters.forEach((filter) => map.set(filter.id, filter))
seoFilters.forEach(({ filter }) => map.set(filter.id, filter))
return Array.from(map.values())
}
export function mergeHotelFiltersAndSeoFilters(
hotelFilters: CategorizedHotelFilters,
seoFilters: SEOFilters | null
seoFilters: DestinationFilters
): CategorizedHotelFilters {
if (!seoFilters) {
return hotelFilters