Merged in fix/handle-single-ancillaries (pull request #3231)
Fix(STAY-128): Handle single ancillaries * fix: refactor ancillaries flow * fix: add logic to determine if an ancillary requires quantity * fix: breakout ancillary description to its own component * fix: cleanup * fix: cleanup Approved-by: Bianca Widstam Approved-by: Erik Tiekstra
This commit is contained in:
185
apps/scandic-web/hooks/useAncillaries.ts
Normal file
185
apps/scandic-web/hooks/useAncillaries.ts
Normal file
@@ -0,0 +1,185 @@
|
||||
import { use } from "react"
|
||||
import { type IntlShape, useIntl } from "react-intl"
|
||||
|
||||
import { BreakfastPackageEnum } from "@scandic-hotels/trpc/enums/breakfast"
|
||||
|
||||
import { useMyStayStore } from "@/stores/my-stay"
|
||||
|
||||
import type { User } from "@scandic-hotels/trpc/types/user"
|
||||
|
||||
import type {
|
||||
Ancillaries,
|
||||
Ancillary,
|
||||
Packages,
|
||||
SelectedAncillary,
|
||||
} from "@/types/components/myPages/myStay/ancillaries"
|
||||
|
||||
export function useAncillaries(
|
||||
ancillariesPromise: Promise<Ancillaries | null>,
|
||||
packages: Packages | null,
|
||||
user: User | null
|
||||
) {
|
||||
const intl = useIntl()
|
||||
const bookedRoom = useMyStayStore((state) => state.bookedRoom)
|
||||
|
||||
if (!bookedRoom || bookedRoom.isCancelled || !bookedRoom.showAncillaries) {
|
||||
return null
|
||||
}
|
||||
|
||||
const ancillaries = use(ancillariesPromise)
|
||||
|
||||
const alreadyHasBreakfast =
|
||||
bookedRoom.rateDefinition.breakfastIncluded || bookedRoom.breakfast
|
||||
|
||||
const breakfastPackageAdults = alreadyHasBreakfast
|
||||
? undefined
|
||||
: packages?.find(
|
||||
(p) => p.code === BreakfastPackageEnum.ANCILLARY_REGULAR_BREAKFAST
|
||||
)
|
||||
|
||||
/**
|
||||
* A constructed ancillary for breakfast
|
||||
*
|
||||
* This is a "fake" ancillary for breakfast, since breakfast isn't really an
|
||||
* ancillary in the system. This makes it play nicely with the add ancillary
|
||||
* flow. If the user shouldn't be able to add breakfast this will be `undefined`.
|
||||
*/
|
||||
const breakfastAncillary: SelectedAncillary | undefined =
|
||||
breakfastPackageAdults
|
||||
? {
|
||||
description: intl.formatMessage({
|
||||
id: "common.buffet",
|
||||
defaultMessage: "Buffet",
|
||||
}),
|
||||
id: breakfastPackageAdults.code,
|
||||
title: intl.formatMessage({
|
||||
id: "common.breakfast",
|
||||
defaultMessage: "Breakfast",
|
||||
}),
|
||||
price: {
|
||||
currency: breakfastPackageAdults.localPrice.currency,
|
||||
total: breakfastPackageAdults.localPrice.totalPrice,
|
||||
},
|
||||
imageUrl:
|
||||
"https://images.scandichotels.com/publishedmedia/inyre69evkpzgtygjnvp/Breakfast_-_Scandic_Sweden_-_Free_to_use.jpg",
|
||||
requiresDeliveryTime: false,
|
||||
loyaltyCode: undefined,
|
||||
points: undefined,
|
||||
hotelId: Number(bookedRoom.hotelId),
|
||||
internalCategoryName: "Food",
|
||||
translatedCategoryName: intl.formatMessage({
|
||||
id: "common.food",
|
||||
defaultMessage: "Food",
|
||||
}),
|
||||
requiresQuantity: false,
|
||||
}
|
||||
: undefined
|
||||
|
||||
const allAncillaries = mapAncillaries(
|
||||
intl,
|
||||
ancillaries,
|
||||
breakfastAncillary,
|
||||
user
|
||||
)
|
||||
|
||||
if (!allAncillaries.length) {
|
||||
return null
|
||||
}
|
||||
|
||||
const uniqueAncillaries = generateUniqueAncillaries(allAncillaries)
|
||||
|
||||
return { all: allAncillaries, unique: uniqueAncillaries }
|
||||
}
|
||||
|
||||
function mapAncillaries(
|
||||
intl: IntlShape,
|
||||
ancillaries: Ancillaries | null,
|
||||
breakfastAncillary: SelectedAncillary | undefined,
|
||||
user: User | null
|
||||
) {
|
||||
const withBreakfastPopular = addBreakfastPackage(
|
||||
ancillaries ?? [],
|
||||
breakfastAncillary,
|
||||
"Popular",
|
||||
intl.formatMessage({
|
||||
defaultMessage: "Popular",
|
||||
id: "myStay.ancillaries.popularCategory",
|
||||
})
|
||||
)
|
||||
const withBreakfastFood = addBreakfastPackage(
|
||||
withBreakfastPopular,
|
||||
breakfastAncillary,
|
||||
"Food",
|
||||
intl.formatMessage({
|
||||
id: "common.food",
|
||||
defaultMessage: "Food",
|
||||
})
|
||||
)
|
||||
|
||||
return filterPoints(withBreakfastFood, user)
|
||||
}
|
||||
|
||||
function filterPoints(ancillaries: Ancillaries, user: User | null) {
|
||||
return ancillaries.map((ancillary) => {
|
||||
return {
|
||||
...ancillary,
|
||||
ancillaryContent: ancillary.ancillaryContent.map(
|
||||
({ points, ...ancillary }) => ({
|
||||
...ancillary,
|
||||
points: user ? points : undefined,
|
||||
})
|
||||
),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function generateUniqueAncillaries(
|
||||
ancillaries: Ancillaries
|
||||
): Ancillary["ancillaryContent"] {
|
||||
const uniqueAncillaries = new Map(
|
||||
ancillaries.flatMap((a) => {
|
||||
return a.ancillaryContent.map((ancillary) => [ancillary.id, ancillary])
|
||||
})
|
||||
)
|
||||
return [...uniqueAncillaries.values()]
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the breakfast package to the ancillaries
|
||||
*
|
||||
* Returns the ancillaries array with the breakfast package added to the
|
||||
* specified category. If the category doesn't exist it's created.
|
||||
*/
|
||||
function addBreakfastPackage(
|
||||
ancillaries: Ancillaries,
|
||||
breakfast: SelectedAncillary | undefined,
|
||||
internalCategoryName: string,
|
||||
translatedCategoryName: string
|
||||
): Ancillaries {
|
||||
if (!breakfast) return ancillaries
|
||||
|
||||
const category = ancillaries.find(
|
||||
(a) => a.internalCategoryName === internalCategoryName
|
||||
)
|
||||
if (category) {
|
||||
const newCategory = {
|
||||
...category,
|
||||
ancillaryContent: [breakfast, ...category.ancillaryContent],
|
||||
}
|
||||
|
||||
return ancillaries.map((ancillary) =>
|
||||
ancillary.internalCategoryName === internalCategoryName
|
||||
? newCategory
|
||||
: ancillary
|
||||
)
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
internalCategoryName,
|
||||
translatedCategoryName,
|
||||
ancillaryContent: [breakfast],
|
||||
},
|
||||
...ancillaries,
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user