diff --git a/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(standard)/details/page.tsx b/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(standard)/details/page.tsx index 42ae5fd3e..6a0ea2368 100644 --- a/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(standard)/details/page.tsx +++ b/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(standard)/details/page.tsx @@ -76,6 +76,7 @@ export default async function DetailsPage({ roomStayStartDate: booking.fromDate, roomStayEndDate: booking.toDate, roomTypeCode: room.roomTypeCode, + bookingCode: booking.bookingCode, } const packages = room.packages diff --git a/apps/scandic-web/components/Forms/BookingWidget/FormContent/BookingCode/TabletCodeInput/index.tsx b/apps/scandic-web/components/Forms/BookingWidget/FormContent/BookingCode/TabletCodeInput/index.tsx index 1be9f05d9..8b75d64ab 100644 --- a/apps/scandic-web/components/Forms/BookingWidget/FormContent/BookingCode/TabletCodeInput/index.tsx +++ b/apps/scandic-web/components/Forms/BookingWidget/FormContent/BookingCode/TabletCodeInput/index.tsx @@ -18,6 +18,7 @@ export default function TabletCodeInput({ {...register("bookingCode.value", { onChange: (e) => updateValue(e.target.value), })} + defaultValue={defaultValue} autoComplete="off" /> ) diff --git a/apps/scandic-web/components/Forms/BookingWidget/FormContent/BookingCode/booking-code.module.css b/apps/scandic-web/components/Forms/BookingWidget/FormContent/BookingCode/booking-code.module.css index 253d67c5f..91a44b1ca 100644 --- a/apps/scandic-web/components/Forms/BookingWidget/FormContent/BookingCode/booking-code.module.css +++ b/apps/scandic-web/components/Forms/BookingWidget/FormContent/BookingCode/booking-code.module.css @@ -31,6 +31,11 @@ width: 100%; } +.bookingCodeTooltip { + max-width: 560px; + margin-top: var(--Spacing-x2); +} + @media screen and (max-width: 767px) { .hideOnMobile { display: none; diff --git a/apps/scandic-web/components/Forms/BookingWidget/FormContent/BookingCode/index.tsx b/apps/scandic-web/components/Forms/BookingWidget/FormContent/BookingCode/index.tsx index 8b6fb1d1a..5fde16d5e 100644 --- a/apps/scandic-web/components/Forms/BookingWidget/FormContent/BookingCode/index.tsx +++ b/apps/scandic-web/components/Forms/BookingWidget/FormContent/BookingCode/index.tsx @@ -115,7 +115,7 @@ export default function BookingCode() { }, })} > - + {codeVoucher} @@ -228,7 +228,9 @@ function CodeRulesModal() { } title={codeVoucher} > - {bookingCodeTooltipText} + + {bookingCodeTooltipText} + ) } diff --git a/apps/scandic-web/components/HotelReservation/HotelCardListing/index.tsx b/apps/scandic-web/components/HotelReservation/HotelCardListing/index.tsx index 1b80b5a98..1c75c96d7 100644 --- a/apps/scandic-web/components/HotelReservation/HotelCardListing/index.tsx +++ b/apps/scandic-web/components/HotelReservation/HotelCardListing/index.tsx @@ -24,6 +24,8 @@ import { HotelCardListingTypeEnum, } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps" import { AlertTypeEnum } from "@/types/enums/alert" +import { BookingCodeFilterEnum } from "@/types/enums/bookingCodeFilter" +import { RateTypeEnum } from "@/types/enums/rateType" export default function HotelCardListing({ hotelData, @@ -55,10 +57,11 @@ export default function HotelCardListing({ ? sortedHotels.filter( (hotel) => !hotel.price || - activeCodeFilter === "all" || - (activeCodeFilter === "discounted" && - hotel.price?.public?.rateType?.toLowerCase() !== "regular") || - activeCodeFilter === hotel.price?.public?.rateType?.toLowerCase() + activeCodeFilter === BookingCodeFilterEnum.All || + (activeCodeFilter === BookingCodeFilterEnum.Discounted && + hotel.price?.public?.rateType !== RateTypeEnum.Regular) || + (activeCodeFilter === BookingCodeFilterEnum.Regular && + hotel.price?.public?.rateType === RateTypeEnum.Regular) ) : sortedHotels diff --git a/apps/scandic-web/components/HotelReservation/SelectHotel/BookingCodeFilter/index.tsx b/apps/scandic-web/components/HotelReservation/SelectHotel/BookingCodeFilter/index.tsx index 94ff93c62..cfcd29a7a 100644 --- a/apps/scandic-web/components/HotelReservation/SelectHotel/BookingCodeFilter/index.tsx +++ b/apps/scandic-web/components/HotelReservation/SelectHotel/BookingCodeFilter/index.tsx @@ -11,6 +11,8 @@ import styles from "./bookingCodeFilter.module.css" import type { Key } from "react" +import { BookingCodeFilterEnum } from "@/types/enums/bookingCodeFilter" + export default function BookingCodeFilter() { const intl = useIntl() const activeCodeFilter = useBookingCodeFilterStore( @@ -21,20 +23,20 @@ export default function BookingCodeFilter() { const bookingCodeFilterItems = [ { label: intl.formatMessage({ id: "Discounted rooms" }), - value: "discounted", + value: BookingCodeFilterEnum.Discounted, }, { label: intl.formatMessage({ id: "Full price rooms" }), - value: "regular", + value: BookingCodeFilterEnum.Regular, }, { label: intl.formatMessage({ id: "See all" }), - value: "all", + value: BookingCodeFilterEnum.All, }, ] function updateFilter(selectedFilter: Key) { - setFilter(selectedFilter as string) + setFilter(selectedFilter as BookingCodeFilterEnum) } return ( diff --git a/apps/scandic-web/components/HotelReservation/SelectHotel/SelectHotelMap/SelectHotelMapContent/index.tsx b/apps/scandic-web/components/HotelReservation/SelectHotel/SelectHotelMap/SelectHotelMapContent/index.tsx index b84a7d680..6e8db972f 100644 --- a/apps/scandic-web/components/HotelReservation/SelectHotel/SelectHotelMap/SelectHotelMapContent/index.tsx +++ b/apps/scandic-web/components/HotelReservation/SelectHotel/SelectHotelMap/SelectHotelMapContent/index.tsx @@ -28,6 +28,8 @@ import styles from "./selectHotelMapContent.module.css" import type { HotelData } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps" import type { SelectHotelMapProps } from "@/types/components/hotelReservation/selectHotel/map" +import { BookingCodeFilterEnum } from "@/types/enums/bookingCodeFilter" +import { RateTypeEnum } from "@/types/enums/rateType" const SKELETON_LOAD_DELAY = 750 @@ -83,10 +85,11 @@ export default function SelectHotelContent({ ? hotelPins.filter( (hotel) => !hotel.publicPrice || - activeCodeFilter === "all" || - (activeCodeFilter === "discounted" && - hotel.rateType?.toLowerCase() !== "regular") || - activeCodeFilter === hotel.rateType?.toLowerCase() + activeCodeFilter === BookingCodeFilterEnum.All || + (activeCodeFilter === BookingCodeFilterEnum.Discounted && + hotel.rateType !== RateTypeEnum.Regular) || + (activeCodeFilter === BookingCodeFilterEnum.Regular && + hotel.rateType === RateTypeEnum.Regular) ) : hotelPins return updatedHotelsList.filter((hotel) => diff --git a/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/RoomCard/FlexibilityOption/PriceList/index.tsx b/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/RoomCard/FlexibilityOption/PriceList/index.tsx index e5ce354f7..04ce5ab23 100644 --- a/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/RoomCard/FlexibilityOption/PriceList/index.tsx +++ b/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/RoomCard/FlexibilityOption/PriceList/index.tsx @@ -101,32 +101,57 @@ export default function PriceList({ )} -
-
- - {intl.formatMessage({ id: "Member price" })} - -
-
- {memberLocalPrice ? ( -
- - {totalMemberLocalPricePerNight} - - - {memberLocalPrice.currency} - - /{intl.formatMessage({ id: "night" })} - + {memberLocalPrice && !publicLocalPrice.regularPricePerNight ? ( +
+
+ + {intl.formatMessage({ id: "Member price" })} + +
+
+ {memberLocalPrice ? ( +
+ + {totalMemberLocalPricePerNight} + + + {memberLocalPrice.currency} + + /{intl.formatMessage({ id: "night" })} + + +
+ ) : ( + + - -
- ) : ( - - - - - )} - - + )} + + + ) : ( + publicLocalPrice.regularPricePerNight && ( +
+
+ + {intl.formatMessage({ id: "Regular price" })} + +
+
+
+ + {publicLocalPrice.regularPricePerNight} + + + {publicLocalPrice.currency} + + /{intl.formatMessage({ id: "night" })} + + +
+
+
+ ) + )} {showRequestedPrice && (
diff --git a/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/RoomCard/FlexibilityOption/PriceList/priceList.module.css b/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/RoomCard/FlexibilityOption/PriceList/priceList.module.css index 3209c4584..4c4fa0c47 100644 --- a/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/RoomCard/FlexibilityOption/PriceList/priceList.module.css +++ b/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/RoomCard/FlexibilityOption/PriceList/priceList.module.css @@ -17,6 +17,12 @@ gap: var(--Spacing-x-half); } +.priceStriked { + display: flex; + gap: var(--Spacing-x-half); + text-decoration: line-through; +} + .perNight { font-weight: 400; font-size: var(--typography-Caption-Regular-fontSize); diff --git a/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/RoomCard/FlexibilityOption/flexibilityOption.module.css b/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/RoomCard/FlexibilityOption/flexibilityOption.module.css index e9359a836..f63ad5baa 100644 --- a/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/RoomCard/FlexibilityOption/flexibilityOption.module.css +++ b/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/RoomCard/FlexibilityOption/flexibilityOption.module.css @@ -100,4 +100,5 @@ input[type="radio"]:checked + .card .checkIcon { .termsIcon { padding-right: var(--Spacing-x1); flex-shrink: 0; + flex-basis: 32px; } diff --git a/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/RoomCard/index.tsx b/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/RoomCard/index.tsx index 437cf0530..ccafe4846 100644 --- a/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/RoomCard/index.tsx +++ b/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/RoomCard/index.tsx @@ -9,7 +9,7 @@ import { useRatesStore } from "@/stores/select-rate" import ToggleSidePeek from "@/components/HotelReservation/EnterDetails/SelectedRoom/ToggleSidePeek" import { getRates } from "@/components/HotelReservation/SelectRate/utils" import { getIconForFeatureCode } from "@/components/HotelReservation/utils" -import { ErrorCircleIcon } from "@/components/Icons" +import { ErrorCircleIcon, PriceTagIcon } from "@/components/Icons" import ImageGallery from "@/components/ImageGallery" import Caption from "@/components/TempDesignSystem/Text/Caption" import Footnote from "@/components/TempDesignSystem/Text/Footnote" @@ -28,7 +28,9 @@ import { AvailabilityEnum } from "@/types/components/hotelReservation/selectHote import type { RoomCardProps } from "@/types/components/hotelReservation/selectRate/roomCard" import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter" import { HotelTypeEnum } from "@/types/enums/hotelType" -import type { Product } from "@/types/trpc/routers/hotel/roomAvailability" +import type { Product, RateDefinition } from "@/types/trpc/routers/hotel/roomAvailability" +import { RateTypeEnum } from "@/types/enums/rateType" +import { useSearchParams } from "next/navigation" function getBreakfastMessage( publicBreakfastIncluded: boolean, @@ -72,6 +74,9 @@ export default function RoomCard({ roomConfiguration }: RoomCardProps) { const lessThanFiveRoomsLeft = roomConfiguration.roomsLeft > 0 && roomConfiguration.roomsLeft < 5 + const searchParams = useSearchParams() + const bookingCode = searchParams.get("bookingCode") + const { hotelId, hotelType, @@ -138,39 +143,52 @@ export default function RoomCard({ roomConfiguration }: RoomCardProps) { const payLater = intl.formatMessage({ id: "Pay later" }) const payNow = intl.formatMessage({ id: "Pay now" }) - function getRate(rateCode: string) { + function getRate(rateCode: string, rateDefinition?: RateDefinition) { switch (rateCode) { case "change": return { isFlex: false, notAvailable: false, title: freeBooking, + terms: rateDefinition?.generalTerms, } case "flex": return { isFlex: true, notAvailable: false, title: freeCancelation, + terms: rateDefinition?.generalTerms, } case "save": return { isFlex: false, notAvailable: false, title: nonRefundable, + terms: rateDefinition?.generalTerms, + } + case "special": + return { + isFlex: rateDefinition?.cancellationRule === "CancellableBefore6PM", + notAvailable: false, + title: rateDefinition?.title ?? "", + terms: rateDefinition?.generalTerms, } default: throw new Error( - `Unknown key for rate, should be "change", "flex" or "save", but got ${rateCode}` + `Unknown key for rate, should be "change", "flex", "save" or "special", but got ${rateCode}` ) } } function getRateInfo(product: Product) { + const rateDefinition = rateDefinitions?.filter((rateDefinition) => + rateDefinition.rateCode === product.productType.public.rateCode + )[0] if ( !product.productType.public.rateCode && !product.productType.member?.rateCode ) { - const possibleRate = getRate(product.productType.public.rate) + const possibleRate = getRate(product.productType.public.rate, rateDefinition) if (possibleRate) { return { ...possibleRate, @@ -181,6 +199,7 @@ export default function RoomCard({ roomConfiguration }: RoomCardProps) { isFlex: false, notAvailable: true, title: "", + terms: undefined, } } @@ -198,14 +217,31 @@ export default function RoomCard({ roomConfiguration }: RoomCardProps) { ) } + if (!publicRate && !memberRate) { + throw new Error( + "We should never make it here without any single available rateCode" + ) + } + const specialRate = publicRate || memberRate + if (product.productType.public.rateType !== "Regular" && specialRate) { + return getRate(specialRate, rateDefinition) + } if (!publicRate || !memberRate) { - throw new Error("We should never make it where without rateCodes") + throw new Error( + "We should never make it here without both public and member rateCodes" + ) } const key = isUserLoggedIn && isMainRoom ? memberRate : publicRate - return getRate(key) + return getRate(key, rateDefinition) } + const isSpecialRate = + bookingCode && + roomConfiguration.products.every((item) => { + return item.productType.public.rateType !== RateTypeEnum.Regular + }) + return (
  • @@ -289,6 +325,12 @@ export default function RoomCard({ roomConfiguration }: RoomCardProps) { ) : ( <> {breakfastMessage} + {bookingCode ? ( + + + {bookingCode} + + ) : null} {roomConfiguration.products.map((product) => { const rate = getRateInfo(product) const isSelectedRateCode = @@ -311,6 +353,7 @@ export default function RoomCard({ roomConfiguration }: RoomCardProps) { roomType={roomConfiguration.roomType} roomTypeCode={roomConfiguration.roomTypeCode} title={rate.title} + priceInformation={rate.terms} /> ) })} diff --git a/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/RoomCard/roomCard.module.css b/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/RoomCard/roomCard.module.css index f48a84039..3d8ab37e2 100644 --- a/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/RoomCard/roomCard.module.css +++ b/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/RoomCard/roomCard.module.css @@ -97,4 +97,7 @@ div[data-multiroom="true"] .imageContainer { gap: var(--Spacing-x1); margin: 0; padding: var(--Spacing-x2); -} \ No newline at end of file +} +.strikedText { + text-decoration: line-through; +} diff --git a/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/index.tsx b/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/index.tsx index 1455021a8..60bcfa2a1 100644 --- a/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/index.tsx +++ b/apps/scandic-web/components/HotelReservation/SelectRate/RoomsContainer/Rooms/RoomSelectionPanel/index.tsx @@ -1,9 +1,11 @@ "use client" import { useIntl } from "react-intl" +import { useSearchParams } from "next/navigation" import { alternativeHotels } from "@/constants/routes/hotelReservation" import Alert from "@/components/TempDesignSystem/Alert" +import BookingCodeFilter from "@/components/HotelReservation/SelectHotel/BookingCodeFilter" import { useRoomContext } from "@/contexts/Room" import useLang from "@/hooks/useLang" @@ -14,14 +16,48 @@ import styles from "./roomSelectionPanel.module.css" import { AvailabilityEnum } from "@/types/components/hotelReservation/selectHotel/selectHotel" import { AlertTypeEnum } from "@/types/enums/alert" +import { RateTypeEnum } from "@/types/enums/rateType" +import { useBookingCodeFilterStore } from "@/stores/bookingCode-filter" +import { BookingCodeFilterEnum } from "@/types/enums/bookingCodeFilter" export default function RoomSelectionPanel() { const { rooms } = useRoomContext() + const searchParams = useSearchParams() + const bookingCode = searchParams.get("bookingCode") const intl = useIntl() const lang = useLang() const noAvailableRooms = rooms.every( (roomConfig) => roomConfig.status === AvailabilityEnum.NotAvailable ) + const activeCodeFilter = useBookingCodeFilterStore((state) => state.activeCodeFilter) + + let filteredRooms = rooms, isRegularRatesAvailableWithCode: boolean = false + if (bookingCode) { + isRegularRatesAvailableWithCode = + !!bookingCode ? + rooms?.some((room) => { + return ( + room.status === "Available" && + room.products.some( + (product) => + product.productType.public.rateType === RateTypeEnum.Regular + ) + ) + }) + : false + + filteredRooms = !isRegularRatesAvailableWithCode || activeCodeFilter === BookingCodeFilterEnum.All + ? rooms : rooms.filter((room) => { + return room.products.every( + (product) => + (activeCodeFilter === BookingCodeFilterEnum.Discounted && + product.productType.public.rateType !== RateTypeEnum.Regular) || + (activeCodeFilter === BookingCodeFilterEnum.Regular && + product.productType.public.rateType === RateTypeEnum.Regular) + ) + }) + } + return ( <> {noAvailableRooms ? ( @@ -41,8 +77,9 @@ export default function RoomSelectionPanel() {
    ) : null} + {bookingCode && isRegularRatesAvailableWithCode ? : null}
      - {rooms.map((roomConfiguration) => ( + {filteredRooms.map((roomConfiguration) => ( rate.cancellationRule === "Changeable" + (rate) => rate.cancellationRule === "Changeable" && rate.rateType === RateTypeEnum.Regular ), flex: rateDefinitions.filter( - (rate) => rate.cancellationRule === "CancellableBefore6PM" + (rate) => rate.cancellationRule === "CancellableBefore6PM" && rate.rateType === RateTypeEnum.Regular ), save: rateDefinitions.filter( - (rate) => rate.cancellationRule === "NotCancellable" + (rate) => rate.cancellationRule === "NotCancellable" && rate.rateType === RateTypeEnum.Regular ), + special: rateDefinitions.filter( + (rate) => rate.rateType !== RateTypeEnum.Regular + ) } } @@ -55,7 +59,8 @@ export function useRoomsAvailability( fromDateString: string, toDateString: string, lang: Lang, - childArray?: Child[] + childArray?: Child[], + bookingCode?: string, ) { const returnValue = trpc.hotel.availability.roomsCombinedAvailability.useQuery({ @@ -65,6 +70,7 @@ export function useRoomsAvailability( uniqueAdultsCount, childArray, lang, + bookingCode, }) const combinedAvailability = returnValue.data?.length diff --git a/apps/scandic-web/components/Modal/index.tsx b/apps/scandic-web/components/Modal/index.tsx index 242309d2f..4888be6d5 100644 --- a/apps/scandic-web/components/Modal/index.tsx +++ b/apps/scandic-web/components/Modal/index.tsx @@ -79,7 +79,9 @@ function InnerModal({ > {({ close }) => ( <> -
      +
      {title && ( diff --git a/apps/scandic-web/components/Modal/modal.module.css b/apps/scandic-web/components/Modal/modal.module.css index 87d9a7c84..a648b2793 100644 --- a/apps/scandic-web/components/Modal/modal.module.css +++ b/apps/scandic-web/components/Modal/modal.module.css @@ -67,6 +67,10 @@ justify-content: center; } +.verticalCenter { + align-items: center; +} + @media screen and (min-width: 768px) { .overlay { display: flex; diff --git a/apps/scandic-web/i18n/dictionaries/en.json b/apps/scandic-web/i18n/dictionaries/en.json index c8b416c7a..c61266e23 100644 --- a/apps/scandic-web/i18n/dictionaries/en.json +++ b/apps/scandic-web/i18n/dictionaries/en.json @@ -524,6 +524,7 @@ "Reference": "Reference", "Reference #{bookingNr}": "Reference #{bookingNr}", "Reference number": "Reference number", + "Regular price": "Regular price", "Relax": "Relax", "Remember code": "Remember code", "Remove card from member profile": "Remove card from member profile", diff --git a/apps/scandic-web/server/routers/hotels/query.ts b/apps/scandic-web/server/routers/hotels/query.ts index d8bda5f6c..bf0d33811 100644 --- a/apps/scandic-web/server/routers/hotels/query.ts +++ b/apps/scandic-web/server/routers/hotels/query.ts @@ -70,6 +70,7 @@ import type { HotelDataWithUrl } from "@/types/hotel" import type { HotelsAvailabilityInputSchema, HotelsByHotelIdsAvailabilityInputSchema, + RoomsAvailabilityInputSchema, } from "@/types/trpc/routers/hotel/availability" import type { HotelInput } from "@/types/trpc/routers/hotel/hotel" import type { CityLocation } from "@/types/trpc/routers/hotel/locations" @@ -801,6 +802,8 @@ export const hotelQueryRouter = router({ }) const bookingCodeAvailabilityResponse = await getHotelsAvailabilityByCity(input, apiLang, ctx.serviceToken) + + // If API or network failed with no response if (!bookingCodeAvailabilityResponse) { metrics.hotelsAvailabilityBookingCode.fail.add(1, { ...input, diff --git a/apps/scandic-web/server/routers/hotels/schemas/productTypePrice.ts b/apps/scandic-web/server/routers/hotels/schemas/productTypePrice.ts index b4a48ddf7..a5a0a4a10 100644 --- a/apps/scandic-web/server/routers/hotels/schemas/productTypePrice.ts +++ b/apps/scandic-web/server/routers/hotels/schemas/productTypePrice.ts @@ -6,6 +6,8 @@ export const priceSchema = z.object({ currency: z.nativeEnum(CurrencyEnum), pricePerNight: z.coerce.number(), pricePerStay: z.coerce.number(), + regularPricePerNight: z.coerce.number().optional(), + regularPricePerStay: z.coerce.number().optional(), }) export const productTypePriceSchema = z.object({ diff --git a/apps/scandic-web/server/routers/hotels/schemas/roomAvailability/configuration.ts b/apps/scandic-web/server/routers/hotels/schemas/roomAvailability/configuration.ts index 51a042be9..48b21766b 100644 --- a/apps/scandic-web/server/routers/hotels/schemas/roomAvailability/configuration.ts +++ b/apps/scandic-web/server/routers/hotels/schemas/roomAvailability/configuration.ts @@ -5,6 +5,7 @@ import { productSchema } from "./product" import { AvailabilityEnum } from "@/types/components/hotelReservation/selectHotel/selectHotel" import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter" +import { RateTypeEnum } from "@/types/enums/rateType" export const roomConfigurationSchema = z .object({ @@ -43,6 +44,11 @@ export const roomConfigurationSchema = z return product } + // Return rate even if single when special rates + if (product.productType.public.rateType !== RateTypeEnum.Regular) { + return product + } + /** * Reset both rateCodes if one is missing to show `No prices available` for the same reason as * mentioned above. @@ -67,12 +73,14 @@ export const roomConfigurationSchema = z /** * When all products miss at least one rateCode (member or public), we change the status to NotAvailable * since we cannot as of now (31 january) guarantee the flow with missing rateCodes. + * Exception Special rate (Booking code rates) * * TODO: (Maybe) notify somewhere that this happened */ const allProductsMissAtLeastOneRateCode = data.products.every( ({ productType }) => - !productType.public.rateCode || !productType.member?.rateCode + (!productType.public.rateCode || !productType.member?.rateCode) && + productType.public.rateType === RateTypeEnum.Regular ) if (allProductsMissAtLeastOneRateCode) { data.status = AvailabilityEnum.NotAvailable diff --git a/apps/scandic-web/server/routers/hotels/schemas/roomAvailability/rateDefinition.ts b/apps/scandic-web/server/routers/hotels/schemas/roomAvailability/rateDefinition.ts index 815c78447..2d7420f2e 100644 --- a/apps/scandic-web/server/routers/hotels/schemas/roomAvailability/rateDefinition.ts +++ b/apps/scandic-web/server/routers/hotels/schemas/roomAvailability/rateDefinition.ts @@ -3,7 +3,7 @@ import { z } from "zod" export const rateDefinitionSchema = z.object({ breakfastIncluded: z.boolean(), cancellationRule: z.string(), - cancellationText: z.string(), + cancellationText: z.string().optional(), generalTerms: z.array(z.string()), mustBeGuaranteed: z.boolean(), rateCode: z.string(), diff --git a/apps/scandic-web/stores/bookingCode-filter.ts b/apps/scandic-web/stores/bookingCode-filter.ts index 9659c87be..1806e70d8 100644 --- a/apps/scandic-web/stores/bookingCode-filter.ts +++ b/apps/scandic-web/stores/bookingCode-filter.ts @@ -1,13 +1,15 @@ import { create } from "zustand" +import { BookingCodeFilterEnum } from "@/types/enums/bookingCodeFilter" + interface BookingCodeFilterState { - activeCodeFilter: string - setFilter: (filter: string) => void + activeCodeFilter: keyof typeof BookingCodeFilterEnum + setFilter: (filter: BookingCodeFilterEnum) => void } export const useBookingCodeFilterStore = create( (set) => ({ - activeCodeFilter: "discounted", + activeCodeFilter: BookingCodeFilterEnum.Discounted, setFilter: (filter) => set({ activeCodeFilter: filter }), }) ) diff --git a/apps/scandic-web/types/components/hotelReservation/selectRate/roomsContainer.ts b/apps/scandic-web/types/components/hotelReservation/selectRate/roomsContainer.ts index e1f5eebf2..6b0a4c792 100644 --- a/apps/scandic-web/types/components/hotelReservation/selectRate/roomsContainer.ts +++ b/apps/scandic-web/types/components/hotelReservation/selectRate/roomsContainer.ts @@ -9,4 +9,5 @@ export interface RoomsContainerProps { hotelId: number toDate: Date hotelData: HotelData | null + bookingCode?: string } diff --git a/apps/scandic-web/types/enums/bookingCodeFilter.ts b/apps/scandic-web/types/enums/bookingCodeFilter.ts new file mode 100644 index 000000000..d0d875429 --- /dev/null +++ b/apps/scandic-web/types/enums/bookingCodeFilter.ts @@ -0,0 +1,5 @@ +export enum BookingCodeFilterEnum { + Discounted = "Discounted", + Regular = "Regular", + All = "All", +} diff --git a/apps/scandic-web/types/enums/rateType.ts b/apps/scandic-web/types/enums/rateType.ts new file mode 100644 index 000000000..a84b1c1b3 --- /dev/null +++ b/apps/scandic-web/types/enums/rateType.ts @@ -0,0 +1,10 @@ +export enum RateTypeEnum { + Arb = "Arb", + BonusCheque = "BonusCheque", + Company = "Company", + Promotion = "Promotion", + Redemption = "Redemption", + Regular = "Regular", + TravelAgent = "TravelAgent", + Voucher = "Voucher", +} diff --git a/apps/scandic-web/types/trpc/routers/hotel/availability.ts b/apps/scandic-web/types/trpc/routers/hotel/availability.ts index ad59e9e2f..aac9ba84f 100644 --- a/apps/scandic-web/types/trpc/routers/hotel/availability.ts +++ b/apps/scandic-web/types/trpc/routers/hotel/availability.ts @@ -3,6 +3,7 @@ import type { z } from "zod" import type { getHotelsByHotelIdsAvailabilityInputSchema, hotelsAvailabilityInputSchema, + roomsAvailabilityInputSchema, } from "@/server/routers/hotels/input" import type { hotelsAvailabilitySchema } from "@/server/routers/hotels/output" import type { productTypeSchema } from "@/server/routers/hotels/schemas/availability/productType" @@ -15,6 +16,9 @@ export type HotelsAvailabilityInputSchema = z.output< export type HotelsByHotelIdsAvailabilityInputSchema = z.output< typeof getHotelsByHotelIdsAvailabilityInputSchema > +export type RoomsAvailabilityInputSchema = z.output< + typeof roomsAvailabilityInputSchema +> export type ProductType = z.output export type ProductTypePrices = z.output