diff --git a/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AllAncillariesModal/SelectAncillaryStep/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AllAncillariesModal/SelectAncillaryStep/index.tsx
index c1baef079..fa08d76d9 100644
--- a/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AllAncillariesModal/SelectAncillaryStep/index.tsx
+++ b/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AllAncillariesModal/SelectAncillaryStep/index.tsx
@@ -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({
{categories.map((categoryName) => (
-
+ {categoryName
+ ? categoryName
+ : intl.formatMessage({
+ id: "common.other",
+ defaultMessage: "Other",
+ })}
+
))}
diff --git a/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AllAncillariesModal/input.tsx b/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AllAncillariesModal/index.tsx
similarity index 100%
rename from apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AllAncillariesModal/input.tsx
rename to apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AllAncillariesModal/index.tsx
diff --git a/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/index.tsx
index 3938b6f91..8c6508960 100644
--- a/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/index.tsx
+++ b/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/index.tsx
@@ -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,60 +28,69 @@ 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 (
-
+
- {ancillaries.unique.length > 0 && bookedRoom.canModifyAncillaries && (
- <>
-
-
-
- {intl.formatMessage({
- id: "ancillaries.upgradeYourStay",
- defaultMessage: "Upgrade your stay",
- })}
-
-
-
-
+ {ancillaries.availableUnique.length > 0 &&
+ bookedRoom.canModifyAncillaries && (
+ <>
+
+
+
+ {intl.formatMessage({
+ id: "ancillaries.upgradeYourStay",
+ defaultMessage: "Upgrade your stay",
+ })}
+
+
+
-
-
- {ancillaries.unique.slice(0, 4).map((ancillary) => (
-
- ))}
-
+
+ {ancillaries.availableUnique.slice(0, 4).map((ancillary) => (
+
+ ))}
+
-
-
-
- {ancillaries.unique.map((ancillary) => {
- return (
-
-
-
- )
- })}
-
-
-
-
- >
- )}
+
+
+
+ {ancillaries.availableUnique.map((ancillary) => {
+ return (
+
+
+
+ )
+ })}
+
+
+
+
+ >
+ )}
diff --git a/apps/scandic-web/hooks/useAncillaries.ts b/apps/scandic-web/hooks/useAncillaries.ts
index 595cbe2c7..9cdd06e53 100644
--- a/apps/scandic-web/hooks/useAncillaries.ts
+++ b/apps/scandic-web/hooks/useAncillaries.ts
@@ -17,7 +17,8 @@ import type {
export function useAncillaries(
ancillariesPromise: Promise,
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
+ )
+ ),
+ }))
+}
diff --git a/apps/scandic-web/stores/my-stay/add-ancillary-flow.ts b/apps/scandic-web/stores/my-stay/add-ancillary-flow.ts
index 950c0caaa..9a4ab1ce2 100644
--- a/apps/scandic-web/stores/my-stay/add-ancillary-flow.ts
+++ b/apps/scandic-web/stores/my-stay/add-ancillary-flow.ts
@@ -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,
diff --git a/packages/trpc/lib/routers/hotels/output.ts b/packages/trpc/lib/routers/hotels/output.ts
index ad20e4ed5..14f025e42 100644
--- a/packages/trpc/lib/routers/hotels/output.ts
+++ b/packages/trpc/lib/routers/hotels/output.ts
@@ -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"
- ? false
- : true
+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)
diff --git a/packages/trpc/lib/routers/hotels/schemas/packages.ts b/packages/trpc/lib/routers/hotels/schemas/packages.ts
index 907ea3d95..0603f5340 100644
--- a/packages/trpc/lib/routers/hotels/schemas/packages.ts
+++ b/packages/trpc/lib/routers/hotels/schemas/packages.ts
@@ -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),
})