diff --git a/apps/scandic-web/components/HotelReservation/MyStay/BookingSummary/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/BookingSummary/index.tsx index 39eef31ff..9040073ca 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/BookingSummary/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/BookingSummary/index.tsx @@ -20,6 +20,7 @@ import { Toast } from "@/components/TempDesignSystem/Toasts" import useLang from "@/hooks/useLang" import { formatPrice } from "@/utils/numberFormatting" +import { useMyStayRoomDetailsStore } from "../stores/myStayRoomDetailsStore" import { useMyStayTotalPriceStore } from "../stores/myStayTotalPrice" import SummaryCard from "./SummaryCard" @@ -40,15 +41,26 @@ export default function BookingSummary({ const intl = useIntl() const lang = useLang() const { totalPrice, currencyCode, addRoomPrice } = useMyStayTotalPriceStore() + const { addRoomDetails } = useMyStayRoomDetailsStore() useEffect(() => { + // Add price information addRoomPrice({ id: booking.confirmationNumber ?? "", totalPrice: booking.totalPrice, currencyCode: booking.currencyCode, isMainBooking: true, }) - }, [booking, addRoomPrice]) + + // Add room details + addRoomDetails({ + id: booking.confirmationNumber ?? "", + roomName: booking.roomTypeCode || "Main Room", + roomTypeCode: booking.roomTypeCode || "", + rateDefinition: booking.rateDefinition, + isMainBooking: true, + }) + }, [booking, addRoomPrice, addRoomDetails]) const directionsUrl = `https://www.google.com/maps/dir/?api=1&destination=${hotel.location.latitude},${hotel.location.longitude}` const isPaid = @@ -130,7 +142,7 @@ export default function BookingSummary({ {hotel.specialAlerts.length > 0 && (
-
-
-
- - {intl.formatMessage({ id: "Cancellation cost" })} - - - {stayDetails.nightsText}, {stayDetails.adultsText} - {booking.childrenAges?.length > 0 - ? `, ${stayDetails.childrenText}` - : ""} - -
-
- - 0 {booking.currencyCode} - -
-
+ {booking.multiRoom && ( + <> + + {intl.formatMessage({ id: "Select rooms" })} + + +
+ {getValues("rooms").map((room, index) => { + // Find room details from store by confirmationNumber + const roomDetail = roomDetails.find( + (detail) => detail.id === room.confirmationNumber + ) + + return ( +
+ +
+ + {intl.formatMessage({ id: "Room" })} {index + 1} + + {roomDetail && ( + <> + + {roomDetail.roomName} + + + )} +
+
+
+ ) + })} +
+ + )} + {getValues("rooms").some((room) => room.checked) && ( + + )} ) } diff --git a/apps/scandic-web/components/HotelReservation/MyStay/CancelStay/FinalConfirmation/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/CancelStay/FinalConfirmation/index.tsx index ea97e67f5..95d7e08ea 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/CancelStay/FinalConfirmation/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/CancelStay/FinalConfirmation/index.tsx @@ -1,21 +1,12 @@ import { useIntl } from "react-intl" import Body from "@/components/TempDesignSystem/Text/Body" -import Caption from "@/components/TempDesignSystem/Text/Caption" -import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" + +import PriceContainer from "../Pricecontainer" import styles from "../cancelStay.module.css" -import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation" - -interface FinalConfirmationProps { - booking: BookingConfirmation["booking"] - stayDetails: { - nightsText: string - adultsText: string - childrenText: string - } -} +import type { FinalConfirmationProps } from "@/types/components/hotelReservation/myStay/cancelStay" export function FinalConfirmation({ booking, @@ -32,24 +23,7 @@ export function FinalConfirmation({ })} -
-
- - {intl.formatMessage({ id: "Cancellation cost" })} - - - {stayDetails.nightsText}, {stayDetails.adultsText} - {booking.childrenAges?.length > 0 - ? `, ${stayDetails.childrenText}` - : ""} - -
-
- - 0 {booking.currencyCode} - -
-
+ ) } diff --git a/apps/scandic-web/components/HotelReservation/MyStay/CancelStay/Pricecontainer/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/CancelStay/Pricecontainer/index.tsx new file mode 100644 index 000000000..c4e554039 --- /dev/null +++ b/apps/scandic-web/components/HotelReservation/MyStay/CancelStay/Pricecontainer/index.tsx @@ -0,0 +1,45 @@ +import { useFormContext } from "react-hook-form" +import { useIntl } from "react-intl" + +import Caption from "@/components/TempDesignSystem/Text/Caption" +import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" + +import { getCheckedRoomsCounts } from "../utils" + +import styles from "../cancelStay.module.css" + +import type { + FormValues, + PriceContainerProps, +} from "@/types/components/hotelReservation/myStay/cancelStay" + +export default function PriceContainer({ + booking, + stayDetails, +}: PriceContainerProps) { + const intl = useIntl() + const { getValues } = useFormContext() + + const checkedRoomsDetails = getCheckedRoomsCounts(booking, getValues, intl) + + return ( +
+
+ + {intl.formatMessage({ id: "Cancellation cost" })} + + + {stayDetails.nightsText}, {checkedRoomsDetails.adultsText} + {checkedRoomsDetails.totalChildren > 0 + ? `, ${checkedRoomsDetails.childrenText}` + : ""} + +
+
+ + 0 {booking.currencyCode} + +
+
+ ) +} diff --git a/apps/scandic-web/components/HotelReservation/MyStay/CancelStay/cancelStay.module.css b/apps/scandic-web/components/HotelReservation/MyStay/CancelStay/cancelStay.module.css index 0666e1f4e..37d181d5f 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/CancelStay/cancelStay.module.css +++ b/apps/scandic-web/components/HotelReservation/MyStay/CancelStay/cancelStay.module.css @@ -13,12 +13,36 @@ justify-content: flex-end; } +.rooms { + display: flex; + flex-direction: column; + gap: var(--Spacing-x1); +} + +.roomContainer { + display: flex; + padding: var(--Spacing-x2); + background-color: var(--Base-Background-Primary-Normal); + border-radius: var(--Corner-radius-Medium); + align-items: center; + gap: var(--Spacing-x1); +} + +.roomInfo { + display: flex; + flex-direction: column; +} + .info { border-right: 1px solid var(--Base-Border-Subtle); padding-right: var(--Spacing-x2); text-align: right; + display: flex; + flex-direction: column; } .price { padding-left: var(--Spacing-x2); + display: flex; + align-items: center; } diff --git a/apps/scandic-web/components/HotelReservation/MyStay/CancelStay/hooks/useCancelStay.ts b/apps/scandic-web/components/HotelReservation/MyStay/CancelStay/hooks/useCancelStay.ts index cca46e736..6caf57467 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/CancelStay/hooks/useCancelStay.ts +++ b/apps/scandic-web/components/HotelReservation/MyStay/CancelStay/hooks/useCancelStay.ts @@ -6,14 +6,22 @@ import { trpc } from "@/lib/trpc/client" import { toast } from "@/components/TempDesignSystem/Toasts" import useLang from "@/hooks/useLang" -import type { CancelStayProps } from ".." +import type { + CancelStayProps, + FormValues, +} from "@/types/components/hotelReservation/myStay/cancelStay" + +interface UseCancelStayProps extends Omit { + getFormValues: () => FormValues // Function to get form values +} export default function useCancelStay({ booking, setBookingStatus, handleCloseModal, handleBackToManageStay, -}: Omit) { + getFormValues, +}: UseCancelStayProps) { const intl = useIntl() const lang = useLang() const [currentStep, setCurrentStep] = useState(1) @@ -21,39 +29,9 @@ export default function useCancelStay({ const cancelStay = trpc.booking.cancel.useMutation({ onMutate: () => setIsLoading(true), - onSuccess: (result) => { - if (!result) { - toast.error( - intl.formatMessage({ - id: "Something went wrong. Please try again later.", - }) - ) - return - } - - setBookingStatus() - toast.success( - intl.formatMessage( - { - id: "Your stay was cancelled. Cancellation cost: 0 {currency}. We’re sorry to see that the plans didn’t work out", - }, - { currency: booking.currencyCode } - ) - ) - }, - onError: () => { - toast.error( - intl.formatMessage({ - id: "Something went wrong. Please try again later.", - }) - ) - }, - onSettled: () => { - handleCloseModal() - }, }) - function handleCancelStay() { + async function handleCancelStay() { if (!booking.confirmationNumber) { toast.error( intl.formatMessage({ @@ -63,10 +41,86 @@ export default function useCancelStay({ return } - cancelStay.mutate({ - confirmationNumber: booking.confirmationNumber, - language: lang, - }) + setIsLoading(true) + + try { + // Get form values using the provided getter function + const formValues = getFormValues() + const { rooms } = formValues + const checkedRooms = rooms.filter((room) => room.checked) + + const results = [] + const errors = [] + + // Process each checked room sequentially + for (const room of checkedRooms) { + const confirmationNumber = + room.confirmationNumber || booking.confirmationNumber + + try { + const result = await cancelStay.mutateAsync({ + confirmationNumber: confirmationNumber, + language: lang, + }) + + if (result) { + results.push(room.id) + } else { + errors.push(room.id) + } + } catch (error) { + console.error( + `Error cancelling room ${room.confirmationNumber}:`, + error + ) + toast.error( + intl.formatMessage({ + id: "Something went wrong. Please try again later.", + }) + ) + errors.push(room.id) + } + } + + // Handle results + if (results.length > 0 && errors.length === 0) { + // All rooms were cancelled successfully + setBookingStatus() + toast.success( + intl.formatMessage( + { + id: "Your stay was cancelled. Cancellation cost: 0 {currency}. We're sorry to see that the plans didn't work out", + }, + { currency: booking.currencyCode } + ) + ) + } else if (results.length > 0 && errors.length > 0) { + // Some rooms were cancelled, some failed + setBookingStatus() + toast.warning( + intl.formatMessage({ + id: "Some rooms were cancelled successfully, but we encountered issues with others. Please contact customer service for assistance.", + }) + ) + } else { + // All rooms failed to cancel + toast.error( + intl.formatMessage({ + id: "Something went wrong. Please try again later.", + }) + ) + } + + handleCloseModal() + } catch (error) { + console.error("Error in handleCancelStay:", error) + toast.error( + intl.formatMessage({ + id: "Something went wrong. Please try again later.", + }) + ) + setIsLoading(false) + } } function handleCloseCancelStay() { diff --git a/apps/scandic-web/components/HotelReservation/MyStay/CancelStay/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/CancelStay/index.tsx index a45b9e14a..1ad6ca3e8 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/CancelStay/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/CancelStay/index.tsx @@ -1,24 +1,29 @@ "use client" +import { zodResolver } from "@hookform/resolvers/zod" +import { FormProvider, useForm } from "react-hook-form" import { useIntl } from "react-intl" +import Alert from "@/components/TempDesignSystem/Alert" import useLang from "@/hooks/useLang" import { ModalContent } from "../ManageStay/ModalContent" +import { useMyStayRoomDetailsStore } from "../stores/myStayRoomDetailsStore" import useCancelStay from "./hooks/useCancelStay" import { CancelStayConfirmation } from "./Confirmation" import { FinalConfirmation } from "./FinalConfirmation" -import { formatStayDetails } from "./utils" +import { formatStayDetails, getDefaultRooms } from "./utils" -import type { Hotel } from "@/types/hotel" -import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation" +import { + type CancelStayProps, + cancelStaySchema, + type FormValues, +} from "@/types/components/hotelReservation/myStay/cancelStay" +import { AlertTypeEnum } from "@/types/enums/alert" -export interface CancelStayProps { - booking: BookingConfirmation["booking"] - hotel: Hotel - setBookingStatus: () => void - handleCloseModal: () => void - handleBackToManageStay: () => void +const MODAL_STEPS = { + INITIAL: 1, + CONFIRMATION: 2, } export default function CancelStay({ @@ -30,6 +35,17 @@ export default function CancelStay({ }: CancelStayProps) { const intl = useIntl() const lang = useLang() + const { rooms: roomDetails } = useMyStayRoomDetailsStore() + + const { mainRoom } = booking + + const form = useForm({ + resolver: zodResolver(cancelStaySchema), + defaultValues: { + rooms: getDefaultRooms(booking), + }, + }) + const { currentStep, isLoading, @@ -41,47 +57,82 @@ export default function CancelStay({ setBookingStatus, handleCloseModal, handleBackToManageStay, + getFormValues: form.getValues, }) const stayDetails = formatStayDetails({ booking, lang, intl }) - const isFirstStep = currentStep === 1 + const isFirstStep = currentStep === MODAL_STEPS.INITIAL + + function getModalCopy() { + if (isFirstStep) { + return { + title: intl.formatMessage({ id: "Cancel stay" }), + primaryLabel: intl.formatMessage({ id: "Cancel stay" }), + secondaryLabel: intl.formatMessage({ id: "Back" }), + } + } else { + return { + title: intl.formatMessage({ id: "Confirm cancellation" }), + primaryLabel: intl.formatMessage({ id: "Confirm cancellation" }), + secondaryLabel: intl.formatMessage({ id: "Don't cancel" }), + } + } + } + + function getModalContent() { + if (mainRoom && isFirstStep) + return ( + + ) + + if (mainRoom && !isFirstStep) + return + + if (!mainRoom && isFirstStep) + return ( + + ) + } + + const { rooms } = form.watch() + const isFormValid = rooms?.some((room) => room.checked) return ( - <> + - ) : ( - - ) + content={getModalContent()} + primaryAction={ + mainRoom + ? { + label: getModalCopy().primaryLabel, + onClick: isFirstStep ? handleForward : handleCancelStay, + intent: isFirstStep ? "secondary" : "primary", + isLoading: isLoading, + disabled: !isFormValid, + } + : null } - primaryAction={{ - label: isFirstStep - ? intl.formatMessage({ id: "Cancel stay" }) - : intl.formatMessage({ id: "Confirm cancellation" }), - onClick: isFirstStep ? handleForward : handleCancelStay, - intent: isFirstStep ? "secondary" : "primary", - isLoading: isLoading, - }} secondaryAction={{ - label: isFirstStep - ? intl.formatMessage({ id: "Back" }) - : intl.formatMessage({ id: "Don't cancel" }), + label: getModalCopy().secondaryLabel, onClick: isFirstStep ? handleCloseCancelStay : handleCloseModal, intent: "text", }} /> - + ) } diff --git a/apps/scandic-web/components/HotelReservation/MyStay/CancelStay/utils.ts b/apps/scandic-web/components/HotelReservation/MyStay/CancelStay/utils.ts index 05eb83012..1d4571a7b 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/CancelStay/utils.ts +++ b/apps/scandic-web/components/HotelReservation/MyStay/CancelStay/utils.ts @@ -1,9 +1,28 @@ import { dt } from "@/lib/dt" +import type { UseFormReturn } from "react-hook-form" import type { IntlShape } from "react-intl" +import type { FormValues } from "@/types/components/hotelReservation/myStay/cancelStay" import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation" +export function getDefaultRooms(booking: BookingConfirmation["booking"]) { + const { multiRoom, confirmationNumber, linkedReservations = [] } = booking + + if (!multiRoom) { + return [{ id: "1", checked: true, confirmationNumber }] + } + + const mainRoom = { id: "1", checked: false, confirmationNumber } + const linkedRooms = linkedReservations.map((reservation, index) => ({ + id: `${index + 2}`, + checked: false, + confirmationNumber: reservation.confirmationNumber, + })) + + return [mainRoom, ...linkedRooms] +} + export function formatStayDetails({ booking, lang, @@ -13,6 +32,20 @@ export function formatStayDetails({ lang: string intl: IntlShape }) { + const { multiRoom } = booking + const totalAdults = multiRoom + ? (booking.adults ?? 0) + + (booking.linkedReservations ?? []).reduce((acc, reservation) => { + return acc + (reservation.adults ?? 0) + }, 0) + : (booking.adults ?? 0) + const totalChildren = multiRoom + ? booking.childrenAges?.length + + (booking.linkedReservations ?? []).reduce((acc, reservation) => { + return acc + reservation.children + }, 0) + : booking.childrenAges?.length + const checkInDate = dt(booking.checkInDate) .locale(lang) .format("dddd D MMM YYYY") @@ -27,11 +60,11 @@ export function formatStayDetails({ ) const adultsText = intl.formatMessage( { id: "{totalAdults, plural, one {# adult} other {# adults}}" }, - { totalAdults: booking.adults } + { totalAdults: totalAdults } ) const childrenText = intl.formatMessage( { id: "{totalChildren, plural, one {# child} other {# children}}" }, - { totalChildren: booking.childrenAges?.length } + { totalChildren: totalChildren } ) return { @@ -42,3 +75,72 @@ export function formatStayDetails({ childrenText, } } + +function getMatchedRooms( + booking: BookingConfirmation["booking"], + checkedConfirmationNumbers: string[] +) { + let matchedRooms = [] + + // Main booking + if (checkedConfirmationNumbers.includes(booking.confirmationNumber ?? "")) { + matchedRooms.push({ + adults: booking.adults ?? 0, + children: booking.childrenAges?.length ?? 0, + }) + } + + // Linked reservations + if (booking.linkedReservations) { + const matchedLinkedRooms = booking.linkedReservations + .filter((reservation) => + checkedConfirmationNumbers.includes(reservation.confirmationNumber) + ) + .map((reservation) => ({ + adults: reservation.adults ?? 0, + children: reservation.children ?? 0, + })) + + matchedRooms = [...matchedRooms, ...matchedLinkedRooms] + } + + return matchedRooms +} + +function calculateTotals(matchedRooms: { adults: number; children: number }[]) { + const totalAdults = matchedRooms.reduce((sum, room) => sum + room.adults, 0) + const totalChildren = matchedRooms.reduce( + (sum, room) => sum + room.children, + 0 + ) + return { totalAdults, totalChildren } +} + +export const getCheckedRoomsCounts = ( + booking: BookingConfirmation["booking"], + getValues: UseFormReturn["getValues"], + intl: IntlShape +) => { + const formRooms = getValues("rooms") + const checkedFormRooms = formRooms.filter((room) => room.checked) + const checkedConfirmationNumbers = checkedFormRooms + .map((room) => room.confirmationNumber) + .filter( + (confirmationNumber): confirmationNumber is string => + confirmationNumber !== null && confirmationNumber !== undefined + ) + + const matchedRooms = getMatchedRooms(booking, checkedConfirmationNumbers) + const { totalAdults, totalChildren } = calculateTotals(matchedRooms) + + const adultsText = intl.formatMessage( + { id: "{totalAdults, plural, one {# adult} other {# adults}}" }, + { totalAdults: totalAdults } + ) + const childrenText = intl.formatMessage( + { id: "{totalChildren, plural, one {# child} other {# children}}" }, + { totalChildren: totalChildren } + ) + + return { adultsText, childrenText, totalChildren } +} diff --git a/apps/scandic-web/components/HotelReservation/MyStay/LinkedReservation/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/LinkedReservation/index.tsx index 3a343dfd2..a510fcf2c 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/LinkedReservation/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/LinkedReservation/index.tsx @@ -8,6 +8,7 @@ import Caption from "@/components/TempDesignSystem/Text/Caption" import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" import useLang from "@/hooks/useLang" +import { useMyStayRoomDetailsStore } from "../stores/myStayRoomDetailsStore" import { useMyStayTotalPriceStore } from "../stores/myStayTotalPrice" import styles from "./linkedReservation.module.css" @@ -27,9 +28,10 @@ export default function LinkedReservation({ const lang = useLang() const { addRoomPrice } = useMyStayTotalPriceStore() + const { addRoomDetails } = useMyStayRoomDetailsStore() const bookingConfirmation = use(bookingPromise) - const { booking } = bookingConfirmation ?? {} + const { booking, room } = bookingConfirmation ?? {} useEffect(() => { if (booking) { @@ -39,8 +41,17 @@ export default function LinkedReservation({ currencyCode: booking.currencyCode, isMainBooking: false, }) + + // Add room details to the store + addRoomDetails({ + id: booking.confirmationNumber ?? "", + roomName: room?.name || booking.roomTypeCode || "Room", + roomTypeCode: booking.roomTypeCode || "", + rateDefinition: booking.rateDefinition, + isMainBooking: false, + }) } - }, [booking, addRoomPrice]) + }, [booking, room, addRoomPrice, addRoomDetails]) if (!booking) return null @@ -58,7 +69,7 @@ export default function LinkedReservation({ {intl.formatMessage({ id: "Reference" })} {booking.confirmationNumber} -
+
{booking.childrenAges.length > 0 ? intl.formatMessage( diff --git a/apps/scandic-web/components/HotelReservation/MyStay/ManageStay/ModalContent/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/ManageStay/ModalContent/index.tsx index e33cf4cac..6e279cd3a 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/ManageStay/ModalContent/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/ManageStay/ModalContent/index.tsx @@ -14,12 +14,13 @@ interface ModalContentProps { onClick: () => void intent?: "primary" | "secondary" | "text" isLoading?: boolean - } + disabled?: boolean + } | null secondaryAction: { label: string onClick: () => void intent?: "primary" | "secondary" | "text" - } + } | null onClose: () => void } @@ -40,22 +41,26 @@ export function ModalContent({
{content}
- - + {secondaryAction && ( + + )} + {primaryAction && ( + + )}
) diff --git a/apps/scandic-web/components/HotelReservation/MyStay/ManageStay/ModalContent/modalContent.module.css b/apps/scandic-web/components/HotelReservation/MyStay/ManageStay/ModalContent/modalContent.module.css index fe5dcffbd..53020cc3d 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/ManageStay/ModalContent/modalContent.module.css +++ b/apps/scandic-web/components/HotelReservation/MyStay/ManageStay/ModalContent/modalContent.module.css @@ -5,6 +5,8 @@ flex-direction: column; gap: var(--Spacing-x3); padding: var(--Spacing-x1) var(--Spacing-x3) var(--Spacing-x4); + max-height: 70vh; + overflow-y: auto; } .header { diff --git a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/index.tsx index 7b3685ef3..a9aff9505 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/index.tsx @@ -69,8 +69,9 @@ export function ReferenceCard({ booking, hotel }: ReferenceCardProps) { : intl.formatMessage({ id: "Reference number" })} - {/* TODO: Implement this: https://scandichotels.atlassian.net/browse/API2-2883 to get correct cancellation number */} - {isCancelled ? "" : booking.confirmationNumber} + {isCancelled + ? booking.cancellationNumber + : booking.confirmationNumber}
@@ -83,7 +84,7 @@ export function ReferenceCard({ booking, hotel }: ReferenceCardProps) { {intl.formatMessage({ id: "Guests" })} - {booking.childrenAges.length > 0 + {children > 0 ? intl.formatMessage( { id: "{adults} adults, {children} children" }, { diff --git a/apps/scandic-web/components/HotelReservation/MyStay/Room/room.module.css b/apps/scandic-web/components/HotelReservation/MyStay/Room/room.module.css index 31fbe647f..e6489bb1a 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/Room/room.module.css +++ b/apps/scandic-web/components/HotelReservation/MyStay/Room/room.module.css @@ -98,6 +98,22 @@ } } +.imagePlaceholder { + height: 100%; + width: 100%; + background-color: #fff; + background-image: linear-gradient(45deg, #000000 25%, transparent 25%), + linear-gradient(-45deg, #000000 25%, transparent 25%), + linear-gradient(45deg, transparent 75%, #000000 75%), + linear-gradient(-45deg, transparent 75%, #000000 75%); + background-size: 120px 120px; + background-position: + 0 0, + 0 60px, + 60px -60px, + -60px 0; +} + .roomDetails { display: grid; gap: var(--Spacing-x5); diff --git a/apps/scandic-web/components/HotelReservation/MyStay/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/index.tsx index 1497a3ef8..3cc607fc7 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/index.tsx @@ -59,7 +59,7 @@ export async function MyStay({ reservationId }: { reservationId: string }) { className={styles.image} src={ hotel.gallery?.heroImages[0]?.imageSizes.large ?? - room?.images[0]?.imageSizes.large ?? + hotel.galleryImages[0]?.imageSizes.large ?? "" } alt={hotel.name} diff --git a/apps/scandic-web/components/HotelReservation/MyStay/stores/myStayRoomDetailsStore.ts b/apps/scandic-web/components/HotelReservation/MyStay/stores/myStayRoomDetailsStore.ts new file mode 100644 index 000000000..0b0dbc5cf --- /dev/null +++ b/apps/scandic-web/components/HotelReservation/MyStay/stores/myStayRoomDetailsStore.ts @@ -0,0 +1,58 @@ +import { create } from "zustand" + +interface RoomDetails { + id: string + roomName: string + roomTypeCode: string + rateDefinition: { + breakfastIncluded: boolean + cancellationRule: string | null + cancellationText: string | null + generalTerms: string[] + isMemberRate: boolean + mustBeGuaranteed: boolean + rateCode: string | null + title: string | null + } + isMainBooking?: boolean +} + +interface MyStayRoomDetailsState { + rooms: RoomDetails[] + + // Add a single room's details + addRoomDetails: (room: RoomDetails) => void + + // Get room details by confirmationNumber + getRoomDetails: (confirmationNumber: string) => RoomDetails | undefined +} + +export const useMyStayRoomDetailsStore = create( + (set, get) => ({ + rooms: [], + + addRoomDetails: (room) => { + set((state) => { + // Check if room with this ID already exists + const existingIndex = state.rooms.findIndex((r) => r.id === room.id) + let newRooms = [...state.rooms] + + if (existingIndex >= 0) { + // Update existing room + newRooms[existingIndex] = room + } else { + // Add new room + newRooms.push(room) + } + + return { + rooms: newRooms, + } + }) + }, + + getRoomDetails: (confirmationNumber) => { + return get().rooms.find((room) => room.id === confirmationNumber) + }, + }) +) diff --git a/apps/scandic-web/i18n/dictionaries/da.json b/apps/scandic-web/i18n/dictionaries/da.json index e580e78da..d805e8b71 100644 --- a/apps/scandic-web/i18n/dictionaries/da.json +++ b/apps/scandic-web/i18n/dictionaries/da.json @@ -58,6 +58,7 @@ "Arrival date": "Ankomstdato", "As our Close Friend": "Som vores nære ven", "As our {level}": "Som vores {level}", + "As this is a multiroom stay, the cancellation has to be done by the person who made the booking. Please call 08-517 517 00 to talk to our customer service if you would need further assistance.": "Da dette er et ophold med flere værelser, skal annullereringen gennemføres af personen, der har booket opholdet. Bedes du ringe til vores kundeservice på 08-517 517 00, hvis du har brug for yderligere hjælp.", "At latest": "Senest", "At the hotel": "På hotellet", "Attractions": "Attraktioner", @@ -155,6 +156,7 @@ "Confirm cancellation": "Bekræft annullerering", "Contact information": "Kontaktoplysninger", "Contact our memberservice": "Contact our memberservice", + "Contact the person who booked the stay": "Kontakt personen, der bookede opholdet", "Contact us": "Kontakt os", "Continue": "Blive ved", "Copied to clipboard": "Copied to clipboard", @@ -579,6 +581,7 @@ "Select payment method": "Vælg betalingsmetode", "Select quantity": "Vælg antal", "Select room": "Zimmer auswählen", + "Select rooms": "Vælg værelser", "Select your language": "Vælg dit sprog", "Shopping": "Shopping", "Shopping & Dining": "Shopping & Spisning", @@ -592,6 +595,7 @@ "Sign up to Scandic Friends": "Tilmeld dig Scandic Friends", "Signing up...": "Tilmelder...", "Skip to main content": "Spring over og gå til hovedindhold", + "Some rooms were cancelled successfully, but we encountered issues with others. Please contact customer service for assistance.": "Nogle værelser blev annulleret med succes, men vi stødte på problemer med andre. Bedes du kontakte kundeservice for hjælp.", "Something went wrong and we couldn't add your card. Please try again later.": "Noget gik galt, og vi kunne ikke tilføje dit kort. Prøv venligst igen senere.", "Something went wrong and we couldn't remove your card. Please try again later.": "Noget gik galt, og vi kunne ikke fjerne dit kort. Prøv venligst igen senere.", "Something went wrong!": "Noget gik galt!", @@ -731,6 +735,7 @@ "Your points to spend": "Dine brugbare point", "Your room": "Dit værelse", "Your selected bed type will be provided based on availability": "Din valgte sengtype vil blive stillet til rådighed baseret på tilgængelighed", + "Your stay was cancelled. Cancellation cost: 0 {currency}. We're sorry to see that the plans didn't work out": "Dit ophold blev annulleret. Annullereringspris: 0 {currency}. Vi beklager, at planerne ikke fungerede ud", "Your stay was cancelled. Cancellation cost: 0 {currency}. We’re sorry to see that the plans didn’t work out": "Dit ophold blev annulleret. Annullereringspris: 0 {currency}. Vi beklager, at planerne ikke fungerede ud", "Zip code": "Postnummer", "Zoo": "Zoo", diff --git a/apps/scandic-web/i18n/dictionaries/de.json b/apps/scandic-web/i18n/dictionaries/de.json index c6045dc1e..9a764804a 100644 --- a/apps/scandic-web/i18n/dictionaries/de.json +++ b/apps/scandic-web/i18n/dictionaries/de.json @@ -58,6 +58,7 @@ "Arrival date": "Ankunftsdatum", "As our Close Friend": "Als unser enger Freund", "As our {level}": "Als unser {level}", + "As this is a multiroom stay, the cancellation has to be done by the person who made the booking. Please call 08-517 517 00 to talk to our customer service if you would need further assistance.": "Da dies ein Mehrzimmer-Aufenthalt ist, muss die Stornierung von der Person, die die Buchung getätigt hat, durchgeführt werden. Bitte rufen Sie uns unter der Telefonnummer 08-517 517 00 an, wenn Sie weitere Hilfe benötigen.", "At latest": "Spätestens", "At the hotel": "Im Hotel", "Attraction": "Attraktion", @@ -156,6 +157,7 @@ "Confirm cancellation": "Stornierung bestätigen", "Contact information": "Kontaktinformationen", "Contact our memberservice": "Contact our memberservice", + "Contact the person who booked the stay": "Kontakt personen, der Buchung getätigt hat", "Contact us": "Kontaktieren Sie uns", "Continue": "Weitermachen", "Copied to clipboard": "Copied to clipboard", @@ -581,6 +583,7 @@ "Select payment method": "Zahlungsart auswählen", "Select quantity": "Menge auswählen", "Select room": "Vælg værelse", + "Select rooms": "Zimmer auswählen", "Select your language": "Wählen Sie Ihre Sprache", "Shopping": "Einkaufen", "Shopping & Dining": "Einkaufen & Essen", @@ -594,6 +597,7 @@ "Sign up to Scandic Friends": "Treten Sie Scandic Friends bei", "Signing up...": "Registrierung läuft...", "Skip to main content": "Direkt zum Inhalt", + "Some rooms were cancelled successfully, but we encountered issues with others. Please contact customer service for assistance.": "Einige Zimmer wurden erfolgreich storniert, aber wir stießen auf Probleme mit anderen. Bitte kontaktieren Sie unseren Kundensupport für weitere Hilfe.", "Something went wrong and we couldn't add your card. Please try again later.": "Ein Fehler ist aufgetreten und wir konnten Ihre Karte nicht hinzufügen. Bitte versuchen Sie es später erneut.", "Something went wrong and we couldn't remove your card. Please try again later.": "Ein Fehler ist aufgetreten und wir konnten Ihre Karte nicht entfernen. Bitte versuchen Sie es später noch einmal.", "Something went wrong!": "Etwas ist schief gelaufen!", @@ -732,6 +736,7 @@ "Your points to spend": "Meine Punkte", "Your room": "Ihr Zimmer", "Your selected bed type will be provided based on availability": "Ihre ausgewählte Bettart wird basierend auf der Verfügbarkeit bereitgestellt", + "Your stay was cancelled. Cancellation cost: 0 {currency}. We're sorry to see that the plans didn't work out": "Ihr Aufenthalt wurde storniert. Stornierungskosten: 0 {currency}. Es tut uns leid, dass die Pläne nicht funktionierten", "Your stay was cancelled. Cancellation cost: 0 {currency}. We’re sorry to see that the plans didn’t work out": "Ihr Aufenthalt wurde storniert. Stornierungskosten: 0 {currency}. Es tut uns leid, dass die Pläne nicht funktionierten", "Zip code": "PLZ", "Zoo": "Zoo", diff --git a/apps/scandic-web/i18n/dictionaries/en.json b/apps/scandic-web/i18n/dictionaries/en.json index c4ea104e3..3dff4decb 100644 --- a/apps/scandic-web/i18n/dictionaries/en.json +++ b/apps/scandic-web/i18n/dictionaries/en.json @@ -58,6 +58,7 @@ "Arrival date": "Arrival date", "As our Close Friend": "As our Close Friend", "As our {level}": "As our {level}", + "As this is a multiroom stay, the cancellation has to be done by the person who made the booking. Please call 08-517 517 00 to talk to our customer service if you would need further assistance.": "As this is a multiroom stay, the cancellation has to be done by the person who made the booking. Please call 08-517 517 00 to talk to our customer service if you would need further assistance.", "At latest": "At latest", "At the hotel": "At the hotel", "Attractions": "Attractions", @@ -156,6 +157,7 @@ "Confirm cancellation": "Confirm cancellation", "Contact information": "Contact information", "Contact our memberservice": "Contact our memberservice", + "Contact the person who booked the stay": "Contact the person who booked the stay", "Contact us": "Contact us", "Continue": "Continue", "Continue to room {nextRoomNumber}": "Continue to room {nextRoomNumber}", @@ -585,6 +587,7 @@ "Select payment method": "Select payment method", "Select quantity": "Select quantity", "Select room": "Select room", + "Select rooms": "Select rooms", "Select your language": "Select your language", "Shopping": "Shopping", "Shopping & Dining": "Shopping & Dining", @@ -598,6 +601,7 @@ "Sign up to Scandic Friends": "Sign up to Scandic Friends", "Signing up...": "Signing up...", "Skip to main content": "Skip to main content", + "Some rooms were cancelled successfully, but we encountered issues with others. Please contact customer service for assistance.": "Some rooms were cancelled successfully, but we encountered issues with others. Please contact customer service for assistance.", "Something went wrong and we couldn't add your card. Please try again later.": "Something went wrong and we couldn't add your card. Please try again later.", "Something went wrong and we couldn't remove your card. Please try again later.": "Something went wrong and we couldn't remove your card. Please try again later.", "Something went wrong!": "Something went wrong!", @@ -737,6 +741,7 @@ "Your points to spend": "Your points to spend", "Your room": "Your room", "Your selected bed type will be provided based on availability": "Your selected bed type will be provided based on availability", + "Your stay was cancelled. Cancellation cost: 0 {currency}. We're sorry to see that the plans didn't work out": "Your stay was cancelled. Cancellation cost: 0 {currency}. We're sorry to see that the plans didn't work out", "Your stay was cancelled. Cancellation cost: 0 {currency}. We’re sorry to see that the plans didn’t work out": "Your stay was cancelled. Cancellation cost: 0 {currency}. We’re sorry to see that the plans didn’t work out", "Zip code": "Zip code", "Zoo": "Zoo", diff --git a/apps/scandic-web/i18n/dictionaries/fi.json b/apps/scandic-web/i18n/dictionaries/fi.json index c41aa64f2..b565b35d4 100644 --- a/apps/scandic-web/i18n/dictionaries/fi.json +++ b/apps/scandic-web/i18n/dictionaries/fi.json @@ -57,6 +57,7 @@ "Arrival date": "Saapumispäivä", "As our Close Friend": "Läheisenä ystävänämme", "As our {level}": "{level}-etu", + "As this is a multiroom stay, the cancellation has to be done by the person who made the booking. Please call 08-517 517 00 to talk to our customer service if you would need further assistance.": "Koska tämä on monihuoneinen majoitus, peruutus on tehtävä henkilölle, joka teki varauksen. Ota yhteyttä asiakaspalveluun apua varten, jos tarvitset lisää apua.", "At latest": "Viimeistään", "At the hotel": "Hotellissa", "Attractions": "Nähtävyydet", @@ -155,6 +156,7 @@ "Confirm cancellation": "Vahvista peruutus", "Contact information": "Yhteystiedot", "Contact our memberservice": "Contact our memberservice", + "Contact the person who booked the stay": "Ota yhteyttä henkilölle, joka teki varauksen", "Contact us": "Ota meihin yhteyttä", "Continue": "Jatkaa", "Copied to clipboard": "Copied to clipboard", @@ -581,6 +583,7 @@ "Select payment method": "Valitse maksutapa", "Select quantity": "Valitse määrä", "Select room": "Valitse huone", + "Select rooms": "Valitse huoneet", "Select your language": "Valitse kieli", "Shopping": "Ostokset", "Shopping & Dining": "Ostokset & Ravintolat", @@ -594,6 +597,7 @@ "Sign up to Scandic Friends": "Liity Scandic Friends -jäseneksi", "Signing up...": "Rekisteröidytään...", "Skip to main content": "Siirry pääsisältöön", + "Some rooms were cancelled successfully, but we encountered issues with others. Please contact customer service for assistance.": "Joitakin huoneita peruutettiin onnistuneesti, mutta esiintyi ongelmia muiden kanssa. Ota yhteyttä asiakaspalveluun apua varten.", "Something went wrong and we couldn't add your card. Please try again later.": "Jotain meni pieleen, emmekä voineet lisätä korttiasi. Yritä myöhemmin uudelleen.", "Something went wrong and we couldn't remove your card. Please try again later.": "Jotain meni pieleen, emmekä voineet poistaa korttiasi. Yritä myöhemmin uudelleen.", "Something went wrong!": "Jotain meni pieleen!", @@ -732,6 +736,7 @@ "Your points to spend": "Käytettävissä olevat pisteesi", "Your room": "Sinun huoneesi", "Your selected bed type will be provided based on availability": "Valitun vuodetyypin toimitetaan saatavuuden mukaan", + "Your stay was cancelled. Cancellation cost: 0 {currency}. We're sorry to see that the plans didn't work out": "Majoituksesi peruutettiin. Peruutusmaksu: 0 {currency}. Emme voi käyttää sitä, että suunnitellut majoitukset eivät toiminneet", "Your stay was cancelled. Cancellation cost: 0 {currency}. We’re sorry to see that the plans didn’t work out": "Majoituksesi peruutettiin. Peruutusmaksu: 0 {currency}. Emme voi käyttää sitä, että suunnitellut majoitukset eivät toiminneet", "Zip code": "Postinumero", "Zoo": "Eläintarha", diff --git a/apps/scandic-web/i18n/dictionaries/no.json b/apps/scandic-web/i18n/dictionaries/no.json index a1e0572ed..adf432055 100644 --- a/apps/scandic-web/i18n/dictionaries/no.json +++ b/apps/scandic-web/i18n/dictionaries/no.json @@ -57,6 +57,7 @@ "Arrival date": "Ankomstdato", "As our Close Friend": "Som vår nære venn", "As our {level}": "Som vår {level}", + "As this is a multiroom stay, the cancellation has to be done by the person who made the booking. Please call 08-517 517 00 to talk to our customer service if you would need further assistance.": "Som dette er et ophold med flere rom, må annullereringen gjøres av personen som booket opholdet. Vennligst ring 08-517 517 00 til vår kundeservice hvis du trenger mer hjelp.", "At latest": "Senest", "At the hotel": "På hotellet", "Attractions": "Attraksjoner", @@ -154,6 +155,7 @@ "Confirm cancellation": "Bekræft annullerering", "Contact information": "Kontaktinformasjon", "Contact our memberservice": "Contact our memberservice", + "Contact the person who booked the stay": "Kontakt personen som booket opholdet", "Contact us": "Kontakt oss", "Continue": "Fortsette", "Copied to clipboard": "Copied to clipboard", @@ -577,6 +579,7 @@ "Select payment method": "Velg betalingsmetode", "Select quantity": "Velg antall", "Select room": "Velg rom", + "Select rooms": "Velg rom", "Select your language": "Velg språk", "Shopping": "Shopping", "Shopping & Dining": "Shopping & Spisesteder", @@ -590,6 +593,7 @@ "Sign up to Scandic Friends": "Bli med i Scandic Friends", "Signing up...": "Registrerer...", "Skip to main content": "Gå videre til hovedsiden", + "Some rooms were cancelled successfully, but we encountered issues with others. Please contact customer service for assistance.": "Noen rom ble annulleret vellykket, men vi møtte problemer med andre. Vennligst kontakt kundeservice for hjelp.", "Something went wrong and we couldn't add your card. Please try again later.": "Noe gikk galt, og vi kunne ikke legge til kortet ditt. Prøv igjen senere.", "Something went wrong and we couldn't remove your card. Please try again later.": "Noe gikk galt, og vi kunne ikke fjerne kortet ditt. Vennligst prøv igjen senere.", "Something went wrong!": "Noe gikk galt!", @@ -728,6 +732,7 @@ "Your points to spend": "Dine brukbare poeng", "Your room": "Rommet ditt", "Your selected bed type will be provided based on availability": "Din valgte sengtype vil blive stillet til rådighed baseret på tilgængelighed", + "Your stay was cancelled. Cancellation cost: 0 {currency}. We're sorry to see that the plans didn't work out": "Ditt ophold ble annulleret. Annullereringspris: 0 {currency}. Vi beklager at planene ikke fungerte ut", "Your stay was cancelled. Cancellation cost: 0 {currency}. We’re sorry to see that the plans didn’t work out": "Dit ophold blev annulleret. Annullereringspris: 0 {currency}. Vi beklager, at planerne ikke fungerede ud", "Zip code": "Post kode", "Zoo": "Dyrehage", diff --git a/apps/scandic-web/i18n/dictionaries/sv.json b/apps/scandic-web/i18n/dictionaries/sv.json index 02a9cc346..f1c5b5967 100644 --- a/apps/scandic-web/i18n/dictionaries/sv.json +++ b/apps/scandic-web/i18n/dictionaries/sv.json @@ -57,6 +57,7 @@ "Arrival date": "Ankomstdatum", "As our Close Friend": "Som vår nära vän", "As our {level}": "Som vår {level}", + "As this is a multiroom stay, the cancellation has to be done by the person who made the booking. Please call 08-517 517 00 to talk to our customer service if you would need further assistance.": "Då detta är en vistelse med flera rum måste avbokningen göras av personen som bokade vistelsen. Kontakta vår kundsupport på 08-517 517 00 om du behöver mer hjälp.", "At latest": "Senast", "At the hotel": "På hotellet", "Attractions": "Sevärdheter", @@ -154,6 +155,7 @@ "Confirm cancellation": "Bekräfta avbokning", "Contact information": "Kontaktinformation", "Contact our memberservice": "Contact our memberservice", + "Contact the person who booked the stay": "Kontakta personen som bokade vistelsen", "Contact us": "Kontakta oss", "Continue": "Fortsätt", "Copied to clipboard": "Copied to clipboard", @@ -577,6 +579,7 @@ "Select payment method": "Välj betalningsmetod", "Select quantity": "Välj antal", "Select room": "Välj rum", + "Select rooms": "Välj rum", "Select your language": "Välj ditt språk", "Shopping": "Shopping", "Shopping & Dining": "Shopping & Mat", @@ -590,6 +593,7 @@ "Sign up to Scandic Friends": "Bli medlem i Scandic Friends", "Signing up...": "Registrerar...", "Skip to main content": "Fortsätt till huvudinnehåll", + "Some rooms were cancelled successfully, but we encountered issues with others. Please contact customer service for assistance.": "Några rum blev avbokade, men vi stötte på problem med andra. Kontakta kundsupporten för hjälp.", "Something went wrong and we couldn't add your card. Please try again later.": "Något gick fel och vi kunde inte lägga till ditt kort. Försök igen senare.", "Something went wrong and we couldn't remove your card. Please try again later.": "Något gick fel och vi kunde inte ta bort ditt kort. Försök igen senare.", "Something went wrong!": "Något gick fel!", @@ -728,6 +732,7 @@ "Your points to spend": "Dina spenderbara poäng", "Your room": "Ditt rum", "Your selected bed type will be provided based on availability": "Din valda sängtyp kommer att tillhandahållas baserat på tillgänglighet", + "Your stay was cancelled. Cancellation cost: 0 {currency}. We're sorry to see that the plans didn't work out": "Din vistelse blev avbokad. Avbokningskostnad: 0 {currency}. Vi beklagar att planerna inte fungerade.", "Your stay was cancelled. Cancellation cost: 0 {currency}. We’re sorry to see that the plans didn’t work out": "Din vistelse blev avbokad. Avbokningskostnad: 0 {currency}. Vi beklagar att planerna inte fungerade ut", "Zip code": "Postnummer", "Zoo": "Djurpark", diff --git a/apps/scandic-web/server/routers/booking/output.ts b/apps/scandic-web/server/routers/booking/output.ts index 74b8832b5..f37f5c4b3 100644 --- a/apps/scandic-web/server/routers/booking/output.ts +++ b/apps/scandic-web/server/routers/booking/output.ts @@ -190,6 +190,7 @@ export const bookingConfirmationSchema = z attributes: z.object({ adults: z.number().int(), ancillary: ancillarySchema, + cancellationNumber: z.string().nullable().default(""), checkInDate: z.date({ coerce: true }), checkOutDate: z.date({ coerce: true }), childBedPreferences: z.array(childBedPreferencesSchema).default([]), @@ -204,6 +205,8 @@ export const bookingConfirmationSchema = z linkedReservationSchema ), hotelId: z.string(), + mainRoom: z.boolean(), + multiRoom: z.boolean(), packages: z.array(packageSchema).default([]), rateDefinition: rateDefinitionSchema, reservationStatus: z.string().nullable().default(""), diff --git a/apps/scandic-web/types/components/checkbox/index.ts b/apps/scandic-web/types/components/checkbox/index.ts index 8588b7401..80c8220f7 100644 --- a/apps/scandic-web/types/components/checkbox/index.ts +++ b/apps/scandic-web/types/components/checkbox/index.ts @@ -1,4 +1,4 @@ -import { RegisterOptions } from "react-hook-form" +import type { RegisterOptions } from "react-hook-form" export interface CheckboxProps extends React.InputHTMLAttributes { diff --git a/apps/scandic-web/types/components/hotelReservation/myStay/cancelStay.ts b/apps/scandic-web/types/components/hotelReservation/myStay/cancelStay.ts new file mode 100644 index 000000000..9e03f3b32 --- /dev/null +++ b/apps/scandic-web/types/components/hotelReservation/myStay/cancelStay.ts @@ -0,0 +1,66 @@ +import { z } from "zod" + +import type { Hotel } from "@/types/hotel" +import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation" + +export const cancelStaySchema = z.object({ + rooms: z.array( + z.object({ + id: z.string().optional(), + checked: z.boolean().optional(), + confirmationNumber: z.string().nullable().optional(), + }) + ), +}) + +export interface CancelStayProps { + booking: BookingConfirmation["booking"] + hotel: Hotel + setBookingStatus: () => void + handleCloseModal: () => void + handleBackToManageStay: () => void +} + +export type FormValues = z.infer + +export interface RoomDetails { + id: string + roomName: string + roomTypeCode: string + rateDefinition: { + breakfastIncluded: boolean + cancellationRule: string | null + cancellationText: string | null + generalTerms: string[] + isMemberRate: boolean + mustBeGuaranteed: boolean + rateCode: string | null + title: string | null + } + isMainBooking?: boolean +} + +export interface StayDetails { + checkInDate: string + checkOutDate: string + nightsText: string + adultsText: string + childrenText: string +} + +export interface CancelStayConfirmationProps { + hotel: Hotel + booking: BookingConfirmation["booking"] + stayDetails: StayDetails + roomDetails?: RoomDetails[] +} + +export interface FinalConfirmationProps { + booking: BookingConfirmation["booking"] + stayDetails: StayDetails +} + +export interface PriceContainerProps { + booking: BookingConfirmation["booking"] + stayDetails: StayDetails +}