Merged in fix/STAY-131-hide-ancillaries (pull request #3299)
fix: fix logic for identifying single use ancillaries * fix: fix logic for identifying single use ancillaries * fix: filter out empty categories of ancillaries Approved-by: Erik Tiekstra
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
import { ChipButton } from "@scandic-hotels/design-system/ChipButton"
|
||||
|
||||
import { useAddAncillaryStore } from "@/stores/my-stay/add-ancillary-flow"
|
||||
|
||||
@@ -30,22 +30,20 @@ export default function SelectAncillaryStep({
|
||||
<div className={styles.container}>
|
||||
<div className={styles.tabs}>
|
||||
{categories.map((categoryName) => (
|
||||
<button
|
||||
<ChipButton
|
||||
onPress={() => selectCategory(categoryName)}
|
||||
key={categoryName}
|
||||
className={`${styles.chip} ${categoryName === selectedCategory ? styles.selected : ""}`}
|
||||
onClick={() => selectCategory(categoryName)}
|
||||
selected={categoryName === selectedCategory}
|
||||
variant="FilterRounded"
|
||||
size="Large"
|
||||
>
|
||||
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||
<p>
|
||||
{categoryName
|
||||
? categoryName
|
||||
: intl.formatMessage({
|
||||
id: "common.other",
|
||||
defaultMessage: "Other",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
</button>
|
||||
</ChipButton>
|
||||
))}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ import { AddAncillaryProvider } from "@/providers/AddAncillaryProvider"
|
||||
|
||||
import AddAncillaryFlowModal from "./AddAncillaryFlow/AddAncillaryFlowModal"
|
||||
import AncillaryFlowModalWrapper from "./AddAncillaryFlow/AncillaryFlowModalWrapper"
|
||||
import AllAncillariesModal from "./AllAncillariesModal/input"
|
||||
import { AddedAncillaries } from "./AddedAncillaries"
|
||||
import AllAncillariesModal from "./AllAncillariesModal"
|
||||
import WrappedAncillaryCard from "./Card"
|
||||
|
||||
import styles from "./ancillaries.module.css"
|
||||
@@ -28,16 +28,25 @@ export function Ancillaries({
|
||||
const intl = useIntl()
|
||||
const bookedRoom = useMyStayStore((state) => state.bookedRoom)
|
||||
|
||||
const ancillaries = useAncillaries(ancillariesPromise, packages, user)
|
||||
const ancillaries = useAncillaries(
|
||||
ancillariesPromise,
|
||||
packages,
|
||||
user,
|
||||
bookedRoom.ancillaries.map((a) => a.code)
|
||||
)
|
||||
|
||||
if (!ancillaries || !bookedRoom) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<AddAncillaryProvider booking={bookedRoom} ancillaries={ancillaries.all}>
|
||||
<AddAncillaryProvider
|
||||
booking={bookedRoom}
|
||||
ancillaries={ancillaries.availableByCategory}
|
||||
>
|
||||
<div className={styles.container}>
|
||||
{ancillaries.unique.length > 0 && bookedRoom.canModifyAncillaries && (
|
||||
{ancillaries.availableUnique.length > 0 &&
|
||||
bookedRoom.canModifyAncillaries && (
|
||||
<>
|
||||
<div className={styles.title}>
|
||||
<Typography variant="Title/Subtitle/lg">
|
||||
@@ -54,7 +63,7 @@ export function Ancillaries({
|
||||
</div>
|
||||
|
||||
<div className={styles.ancillaries}>
|
||||
{ancillaries.unique.slice(0, 4).map((ancillary) => (
|
||||
{ancillaries.availableUnique.slice(0, 4).map((ancillary) => (
|
||||
<WrappedAncillaryCard
|
||||
ancillary={ancillary}
|
||||
key={ancillary.id}
|
||||
@@ -65,7 +74,7 @@ export function Ancillaries({
|
||||
<div className={styles.mobileAncillaries}>
|
||||
<Carousel>
|
||||
<Carousel.Content>
|
||||
{ancillaries.unique.map((ancillary) => {
|
||||
{ancillaries.availableUnique.map((ancillary) => {
|
||||
return (
|
||||
<Carousel.Item key={ancillary.id}>
|
||||
<WrappedAncillaryCard ancillary={ancillary} />
|
||||
@@ -81,7 +90,7 @@ export function Ancillaries({
|
||||
|
||||
<AddedAncillaries
|
||||
booking={bookedRoom}
|
||||
ancillaries={ancillaries.unique}
|
||||
ancillaries={ancillaries.allUnique}
|
||||
/>
|
||||
|
||||
<AncillaryFlowModalWrapper>
|
||||
|
||||
@@ -17,7 +17,8 @@ import type {
|
||||
export function useAncillaries(
|
||||
ancillariesPromise: Promise<Ancillaries | null>,
|
||||
packages: Packages | null,
|
||||
user: User | null
|
||||
user: User | null,
|
||||
alreadyAcquiredAncillaryCodes: string[]
|
||||
) {
|
||||
const intl = useIntl()
|
||||
const bookedRoom = useMyStayStore((state) => state.bookedRoom)
|
||||
@@ -86,9 +87,23 @@ export function useAncillaries(
|
||||
return null
|
||||
}
|
||||
|
||||
const uniqueAncillaries = generateUniqueAncillaries(allAncillaries)
|
||||
const allUniqueAncillaries = generateUniqueAncillaries(allAncillaries)
|
||||
|
||||
return { all: allAncillaries, unique: uniqueAncillaries }
|
||||
const availableByCategory = alreadyAcquiredAncillaryCodes.length
|
||||
? filterOutAlreadyAcquiredAncillaries(
|
||||
allAncillaries,
|
||||
alreadyAcquiredAncillaryCodes
|
||||
)
|
||||
: allAncillaries
|
||||
|
||||
const availableUniqueAncillaries =
|
||||
generateUniqueAncillaries(availableByCategory)
|
||||
|
||||
return {
|
||||
availableByCategory,
|
||||
allUnique: allUniqueAncillaries,
|
||||
availableUnique: availableUniqueAncillaries,
|
||||
}
|
||||
}
|
||||
|
||||
function mapAncillaries(
|
||||
@@ -183,3 +198,19 @@ function addBreakfastPackage(
|
||||
...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
|
||||
)
|
||||
),
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -83,9 +83,9 @@ export const createAddAncillaryStore = (
|
||||
ancillaries,
|
||||
selectedCategory
|
||||
)
|
||||
const categories = ancillaries.map(
|
||||
(ancillary) => ancillary.translatedCategoryName
|
||||
)
|
||||
const categories = ancillaries
|
||||
.filter((anc) => !!anc.ancillaryContent.length)
|
||||
.map((ancillary) => ancillary.translatedCategoryName)
|
||||
const steps = {
|
||||
[AncillaryStepEnum.selectQuantity]: {
|
||||
step: AncillaryStepEnum.selectQuantity,
|
||||
|
||||
@@ -444,14 +444,28 @@ export const breakfastPackagesSchema = z
|
||||
data.attributes.packages.filter((pkg) => pkg.code?.match(/^(BRF\d+)$/gm))
|
||||
)
|
||||
|
||||
// Determine if ancillary requires quantity based on unit name. These ancillaries are special
|
||||
// since they are 1 per booking, but we have no other way than string matching on unit name
|
||||
// to determine this from the API at the moment.
|
||||
function getRequiresQuantity(unitName?: string) {
|
||||
return (unitName && unitName === "Late check-out") ||
|
||||
unitName === "Early check-in"
|
||||
enum SingleUseAncillaryIds {
|
||||
EarlyCheckIn = "0060",
|
||||
LateCheckOut = "0061",
|
||||
EarlyCheckinPilot = "0060999",
|
||||
LateCheckoutPilot = "0061999",
|
||||
}
|
||||
|
||||
// Determine if ancillary requires quantity based on ID. These ancillaries are special since they
|
||||
// are 1 per booking. The agreement is to use the same last digits in the ID for both early check-in
|
||||
// and late check-out ancillaries in order to identify them here regardless of language or market.
|
||||
// During the Pilot phase, the IDs are different but the same logic applies.
|
||||
function getRequiresQuantity(id: string) {
|
||||
const code = id.split("_").pop()
|
||||
|
||||
if (code) {
|
||||
return Object.values(SingleUseAncillaryIds).includes(
|
||||
code as SingleUseAncillaryIds
|
||||
)
|
||||
? false
|
||||
: true
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
export const ancillaryPackagesSchema = z
|
||||
@@ -485,7 +499,7 @@ export const ancillaryPackagesSchema = z
|
||||
requiresDeliveryTime: item.requiresDeliveryTime,
|
||||
translatedCategoryName: ancillary.categoryName,
|
||||
internalCategoryName: ancillary.internalCategoryName,
|
||||
requiresQuantity: getRequiresQuantity(item.unitName),
|
||||
requiresQuantity: getRequiresQuantity(item.id),
|
||||
})),
|
||||
}))
|
||||
.filter((ancillary) => ancillary.ancillaryContent.length > 0)
|
||||
|
||||
@@ -66,6 +66,6 @@ export const breakfastPackageSchema = z.object({
|
||||
|
||||
export const ancillaryPackageSchema = z.object({
|
||||
categoryName: z.string(),
|
||||
internalCategoryName: z.string(),
|
||||
internalCategoryName: z.string().optional(),
|
||||
ancillaryContent: z.array(ancillaryContentSchema),
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user