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, packages: Packages | null, user: User | null, alreadyAcquiredAncillaryCodes: string[] ) { 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, unitName: intl.formatMessage({ id: "common.person", defaultMessage: "person", }), } : undefined const allAncillaries = mapAncillaries( intl, ancillaries, breakfastAncillary, user ) if (!allAncillaries.length) { return null } const allUniqueAncillaries = generateUniqueAncillaries(allAncillaries) const availableByCategory = alreadyAcquiredAncillaryCodes.length ? filterOutAlreadyAcquiredAncillaries( allAncillaries, alreadyAcquiredAncillaryCodes ) : allAncillaries const availableUniqueAncillaries = generateUniqueAncillaries(availableByCategory) return { availableByCategory, allUnique: allUniqueAncillaries, availableUnique: availableUniqueAncillaries, } } 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, }) ), } }) } 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, ] } function filterOutAlreadyAcquiredAncillaries( ancillaries: Ancillaries, alreadyAcquiredAncillaryCodes: string[] ): Ancillaries { return ancillaries.map((cat) => ({ ...cat, ancillaryContent: cat.ancillaryContent.filter((ancillary) => ancillary.requiresQuantity ? true : !alreadyAcquiredAncillaryCodes.includes( ancillary.loyaltyCode || ancillary.id ) ), })) }