Files
web/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/index.tsx
Niclas Edenvin 8d1312c0fa Merged in fix/sw-2444-view-all-on-mobile (pull request #1834)
fix(sw-2444): view all ancillaries mobile

* fix(sw-2444): view all ancillaries mobile

On mobile the view all button to show all ancillaries shouldn't be
shown. It's already a carousel showing all available ancillaries.


Approved-by: Bianca Widstam
Approved-by: Matilda Landström
2025-04-22 09:07:24 +00:00

231 lines
6.7 KiB
TypeScript

"use client"
import { useMemo } from "react"
import { useIntl } from "react-intl"
import { Carousel } from "@/components/Carousel"
import Title from "@/components/TempDesignSystem/Text/Title"
import { AddAncillaryProvider } from "@/providers/AddAncillaryProvider"
import AddAncillaryFlowModal from "./AddAncillaryFlow/AddAncillaryFlowModal"
import AncillaryFlowModalWrapper from "./AddAncillaryFlow/AncillaryFlowModalWrapper"
import WrappedAncillaryCard from "./AddAncillaryFlow/WrappedAncillaryCard"
import { AddedAncillaries } from "./AddedAncillaries"
import ViewAllAncillaries from "./ViewAllAncillaries"
import styles from "./ancillaries.module.css"
import type {
Ancillaries,
AncillariesProps,
Ancillary,
SelectedAncillary,
} from "@/types/components/myPages/myStay/ancillaries"
import { BreakfastPackageEnum } from "@/types/enums/breakfast"
import type { User } from "@/types/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,
categoryName: string
): Ancillaries {
if (!breakfast) return ancillaries
const category = ancillaries.find((a) => a.categoryName === categoryName)
if (category) {
const newCategory = {
...category,
ancillaryContent: [breakfast, ...category.ancillaryContent],
}
return ancillaries.map((ancillary) =>
ancillary.categoryName === categoryName ? newCategory : ancillary
)
}
return [{ categoryName, ancillaryContent: [breakfast] }, ...ancillaries]
}
export function Ancillaries({
ancillaries,
booking,
packages,
user,
savedCreditCards,
refId,
}: AncillariesProps) {
const intl = useIntl()
/**
* 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 = useMemo(() => {
// This is the logic deciding if breakfast should be addable or not
if (
booking.rateDefinition.breakfastIncluded ||
booking.packages.some((p) =>
Object.values(BreakfastPackageEnum).includes(
p.code as unknown as BreakfastPackageEnum
)
)
) {
return undefined
}
const breakfastPackage = packages?.find(
(p) => p.code === BreakfastPackageEnum.ANCILLARY_REGULAR_BREAKFAST
)
const breakfastAncillary: SelectedAncillary | undefined = breakfastPackage
? {
description: intl.formatMessage({
defaultMessage: "Buffet",
}),
id: breakfastPackage.code,
title: intl.formatMessage({
defaultMessage: "Breakfast",
}),
price: {
currency: breakfastPackage.localPrice.currency,
total: breakfastPackage.localPrice.totalPrice,
},
// TODO: Change this to the correct URL, whatever that is
imageUrl:
"https://images-test.scandichotels.com/publishedmedia/hcf9hchiad7zrvlkc2pt/Breakfast_-_Scandic_Sweden_-_Free_to_use.jpg",
requiresDeliveryTime: false,
loyaltyCode: undefined,
points: undefined,
hotelId: Number(booking.hotelId),
categoryName: "Food",
}
: undefined
return breakfastAncillary
}, [
booking.packages,
booking.rateDefinition.breakfastIncluded,
intl,
packages,
booking.hotelId,
])
const allAncillaries = useMemo(() => {
if (!ancillaries?.length) {
return []
}
const withBreakfastPopular = addBreakfastPackage(
ancillaries,
breakfastAncillary,
"Popular"
)
const withBreakfastFood = addBreakfastPackage(
withBreakfastPopular,
breakfastAncillary,
"Food"
)
const filtered = filterPoints(withBreakfastFood, user)
return filtered
}, [ancillaries, breakfastAncillary, user])
if (!ancillaries?.length) {
return null
}
const uniqueAncillaries = generateUniqueAncillaries(allAncillaries)
return (
<AddAncillaryProvider booking={booking} ancillaries={allAncillaries}>
<div className={styles.container}>
{uniqueAncillaries.length > 0 && booking.canModifyAncillaries && (
<>
<div className={styles.title}>
<Title as="h5">
{intl.formatMessage({
defaultMessage: "Upgrade your stay",
})}
</Title>
<div className={styles.viewAllLink}>
<ViewAllAncillaries />
</div>
</div>
<div className={styles.ancillaries}>
{uniqueAncillaries.slice(0, 4).map((ancillary) => (
<WrappedAncillaryCard
ancillary={ancillary}
key={ancillary.id}
/>
))}
</div>
<div className={styles.mobileAncillaries}>
<Carousel>
<Carousel.Content>
{uniqueAncillaries.map((ancillary) => {
return (
<Carousel.Item key={ancillary.id}>
<WrappedAncillaryCard ancillary={ancillary} />
</Carousel.Item>
)
})}
</Carousel.Content>
<Carousel.Previous />
<Carousel.Next />
<Carousel.Dots />
</Carousel>
</div>
</>
)}
<AddedAncillaries booking={booking} ancillaries={uniqueAncillaries} />
<AncillaryFlowModalWrapper>
<AddAncillaryFlowModal
user={user}
booking={booking}
packages={packages}
refId={refId}
savedCreditCards={savedCreditCards}
/>
</AncillaryFlowModalWrapper>
</div>
</AddAncillaryProvider>
)
}