diff --git a/apps/scandic-web/components/HotelReservation/MyStay/BookingSummary/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/BookingSummary/index.tsx index 6b0bf7c4d..39eef31ff 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/BookingSummary/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/BookingSummary/index.tsx @@ -1,3 +1,8 @@ +"use client" + +import { useEffect } from "react" +import { useIntl } from "react-intl" + import { dt } from "@/lib/dt" import { @@ -12,10 +17,10 @@ import Body from "@/components/TempDesignSystem/Text/Body" import Caption from "@/components/TempDesignSystem/Text/Caption" import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" import { Toast } from "@/components/TempDesignSystem/Toasts" -import { getIntl } from "@/i18n" -import { getLang } from "@/i18n/serverContext" +import useLang from "@/hooks/useLang" import { formatPrice } from "@/utils/numberFormatting" +import { useMyStayTotalPriceStore } from "../stores/myStayTotalPrice" import SummaryCard from "./SummaryCard" import styles from "./bookingSummary.module.css" @@ -28,12 +33,22 @@ interface BookingSummaryProps { hotel: Hotel } -export default async function BookingSummary({ +export default function BookingSummary({ booking, hotel, }: BookingSummaryProps) { - const intl = await getIntl() - const lang = getLang() + const intl = useIntl() + const lang = useLang() + const { totalPrice, currencyCode, addRoomPrice } = useMyStayTotalPriceStore() + + useEffect(() => { + addRoomPrice({ + id: booking.confirmationNumber ?? "", + totalPrice: booking.totalPrice, + currencyCode: booking.currencyCode, + isMainBooking: true, + }) + }, [booking, addRoomPrice]) const directionsUrl = `https://www.google.com/maps/dir/?api=1&destination=${hotel.location.latitude},${hotel.location.longitude}` const isPaid = @@ -49,7 +64,7 @@ export default async function BookingSummary({
+
+
+ +
+
+
+
+ +
+
+
+ +
+
+
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ ) +} diff --git a/apps/scandic-web/components/HotelReservation/MyStay/LinkedReservation/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/LinkedReservation/index.tsx new file mode 100644 index 000000000..3a343dfd2 --- /dev/null +++ b/apps/scandic-web/components/HotelReservation/MyStay/LinkedReservation/index.tsx @@ -0,0 +1,108 @@ +"use client" +import { use, useEffect } from "react" +import { useIntl } from "react-intl" + +import { dt } from "@/lib/dt" + +import Caption from "@/components/TempDesignSystem/Text/Caption" +import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" +import useLang from "@/hooks/useLang" + +import { useMyStayTotalPriceStore } from "../stores/myStayTotalPrice" + +import styles from "./linkedReservation.module.css" + +import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation" + +interface LinkedReservationProps { + bookingPromise: Promise + index: number +} + +export default function LinkedReservation({ + bookingPromise, + index, +}: LinkedReservationProps) { + const intl = useIntl() + const lang = useLang() + + const { addRoomPrice } = useMyStayTotalPriceStore() + + const bookingConfirmation = use(bookingPromise) + const { booking } = bookingConfirmation ?? {} + + useEffect(() => { + if (booking) { + addRoomPrice({ + id: booking.confirmationNumber ?? "", + totalPrice: booking.totalPrice, + currencyCode: booking.currencyCode, + isMainBooking: false, + }) + } + }, [booking, addRoomPrice]) + + if (!booking) return null + + const fromDate = dt(booking.checkInDate).locale(lang) + const toDate = dt(booking.checkOutDate).locale(lang) + + return ( +
+
+ + {intl.formatMessage({ id: "Room" }) + " " + (index + 2)} + +
+
+ + {intl.formatMessage({ id: "Reference" })} {booking.confirmationNumber} + +
+ + {booking.childrenAges.length > 0 + ? intl.formatMessage( + { id: "{adults} adults, {children} children" }, + { + adults: booking.adults, + children: booking.childrenAges.length, + } + ) + : intl.formatMessage( + { id: "{adults} adults" }, + { + adults: booking.adults, + } + )} + +
+
+
+
+ + {intl.formatMessage({ id: "Check-in" })} + + + {`${fromDate.format("dddd, D MMMM")} `} + +
+
+ + {intl.formatMessage({ id: "Check-out" })} + + + {`${toDate.format("dddd, D MMMM")} `} + +
+
+
+ ) +} diff --git a/apps/scandic-web/components/HotelReservation/MyStay/LinkedReservation/linkedReservation.module.css b/apps/scandic-web/components/HotelReservation/MyStay/LinkedReservation/linkedReservation.module.css new file mode 100644 index 000000000..e2ef791a7 --- /dev/null +++ b/apps/scandic-web/components/HotelReservation/MyStay/LinkedReservation/linkedReservation.module.css @@ -0,0 +1,33 @@ +.linkedReservation { + display: flex; + flex-direction: row; + gap: var(--Spacing-x2); + background-color: var(--Base-Background-Primary-Normal); + padding: var(--Spacing-x3); + border-radius: var(--Corner-radius-Large); + margin-top: 20px; +} + +.title { + border-right: 1px solid var(--Base-Border-Normal); + width: 40%; + align-content: center; +} +.details { + display: flex; + padding: 0 var(--Spacing-x1); + gap: var(--Spacing-x2); + flex-direction: column; +} +.dates { + display: flex; + flex-direction: column; + gap: var(--Spacing-x2); + flex: 1; +} + +.date { + display: flex; + flex-direction: row; + justify-content: space-between; +} diff --git a/apps/scandic-web/components/HotelReservation/MyStay/ManageStay/ActionPanel/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/ManageStay/ActionPanel/index.tsx index f08044f46..7aa1f3098 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/ManageStay/ActionPanel/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/ManageStay/ActionPanel/index.tsx @@ -27,17 +27,19 @@ import type { EventAttributes } from "ics" import type { Hotel } from "@/types/hotel" import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation" +interface ActionPanelProps { + booking: BookingConfirmation["booking"] + hotel: Hotel + showCancelButton: boolean + onCancelClick: () => void +} + export default function ActionPanel({ booking, hotel, showCancelButton, onCancelClick, -}: { - booking: BookingConfirmation["booking"] - hotel: Hotel - showCancelButton: boolean - onCancelClick: () => void -}) { +}: ActionPanelProps) { const intl = useIntl() const lang = useLang() diff --git a/apps/scandic-web/components/HotelReservation/MyStay/ManageStay/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/ManageStay/index.tsx index 0c7cbf8d9..c32b5a38d 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/ManageStay/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/ManageStay/index.tsx @@ -24,17 +24,19 @@ import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmat type ActiveView = "actionPanel" | "cancelStay" +interface ManageStayProps { + booking: BookingConfirmation["booking"] + hotel: Hotel + setBookingStatus: (status: BookingStatusEnum) => void + bookingStatus: string | null +} + export default function ManageStay({ booking, hotel, setBookingStatus, bookingStatus, -}: { - booking: BookingConfirmation["booking"] - hotel: Hotel - setBookingStatus: (status: BookingStatusEnum) => void - bookingStatus: string | null -}) { +}: ManageStayProps) { const [isOpen, setIsOpen] = useState(false) const [animation, setAnimation] = useState( AnimationStateEnum.visible diff --git a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/index.tsx index 3a6a9dcb1..7b3685ef3 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/index.tsx @@ -17,22 +17,23 @@ import useLang from "@/hooks/useLang" import { formatPrice } from "@/utils/numberFormatting" import ManageStay from "../ManageStay" +import { useMyStayTotalPriceStore } from "../stores/myStayTotalPrice" import styles from "./referenceCard.module.css" import type { Hotel } from "@/types/hotel" import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation" -export function ReferenceCard({ - booking, - hotel, -}: { +interface ReferenceCardProps { booking: BookingConfirmation["booking"] hotel: Hotel -}) { +} + +export function ReferenceCard({ booking, hotel }: ReferenceCardProps) { const [bookingStatus, setBookingStatus] = useState(booking.reservationStatus) const intl = useIntl() const lang = useLang() + const { totalPrice, currencyCode } = useMyStayTotalPriceStore() const fromDate = dt(booking.checkInDate).locale(lang) const toDate = dt(booking.checkOutDate).locale(lang) @@ -43,6 +44,19 @@ export function ReferenceCard({ const directionsUrl = `https://www.google.com/maps/dir/?api=1&destination=${hotel.location.latitude},${hotel.location.longitude}` + const adults = + booking.adults + + (booking.linkedReservations?.reduce( + (acc, linkedReservation) => acc + linkedReservation.adults, + 0 + ) ?? 0) + const children = + booking.childrenAges.length + + (booking.linkedReservations?.reduce( + (acc, linkedReservation) => acc + linkedReservation.children, + 0 + ) ?? 0) + return (
@@ -56,9 +70,7 @@ export function ReferenceCard({ {/* TODO: Implement this: https://scandichotels.atlassian.net/browse/API2-2883 to get correct cancellation number */} - {isCancelled - ? booking.linkedReservations[0]?.cancellationNumber - : booking.confirmationNumber} + {isCancelled ? "" : booking.confirmationNumber}
@@ -75,14 +87,14 @@ export function ReferenceCard({ ? intl.formatMessage( { id: "{adults} adults, {children} children" }, { - adults: booking.adults, - children: booking.childrenAges.length, + adults: adults, + children: children, } ) : intl.formatMessage( { id: "{adults} adults" }, { - adults: booking.adults, + adults: adults, } )} @@ -121,7 +133,7 @@ export function ReferenceCard({ {intl.formatMessage({ id: "Total paid" })} - {formatPrice(intl, booking.totalPrice, booking.currencyCode)} + {formatPrice(intl, totalPrice, currencyCode)}
{!showCancelButton && ( @@ -150,7 +162,7 @@ export function ReferenceCard({ - {booking.rateDefinition.cancellationRule !== "NotCancellable" && ( + {booking.isModifiable && ( {intl.formatMessage( { diff --git a/apps/scandic-web/components/HotelReservation/MyStay/Room/GuestDetails.tsx b/apps/scandic-web/components/HotelReservation/MyStay/Room/GuestDetails.tsx index c00104a0a..a57bab45f 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/Room/GuestDetails.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/Room/GuestDetails.tsx @@ -14,15 +14,17 @@ import styles from "./room.module.css" import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation" import type { User } from "@/types/user" +interface GuestDetailsProps { + user: User | null + booking: BookingConfirmation["booking"] + isMobile?: boolean +} + export default function GuestDetails({ user, booking, isMobile = false, -}: { - user: User | null - booking: BookingConfirmation["booking"] - isMobile?: boolean -}) { +}: GuestDetailsProps) { const intl = useIntl() const lang = useLang() const router = useRouter() diff --git a/apps/scandic-web/components/HotelReservation/MyStay/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/index.tsx index 8622cc351..1497a3ef8 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/index.tsx @@ -1,4 +1,5 @@ import { notFound } from "next/navigation" +import { Suspense } from "react" import { homeHrefs } from "@/constants/homeHrefs" import { env } from "@/env/server" @@ -13,9 +14,11 @@ import Image from "@/components/Image" import { getIntl } from "@/i18n" import { getLang } from "@/i18n/serverContext" +import LinkedReservationSkeleton from "./LinkedReservation/LinkedReservationSkeleton" import { Ancillaries } from "./Ancillaries" import BookingSummary from "./BookingSummary" import { Header } from "./Header" +import LinkedReservation from "./LinkedReservation" import Promo from "./Promo" import { ReferenceCard } from "./ReferenceCard" import { Room } from "./Room" @@ -30,6 +33,12 @@ export async function MyStay({ reservationId }: { reservationId: string }) { const { booking, hotel, room } = bookingConfirmation + const linkedBookingPromises = booking.linkedReservations + ? booking.linkedReservations.map((linkedBooking) => { + return getBookingConfirmation(linkedBooking.confirmationNumber) + }) + : [] + const userResponse = await getProfileSafely() const user = userResponse && !("error" in userResponse) ? userResponse : null const intl = await getIntl() @@ -39,7 +48,6 @@ export async function MyStay({ reservationId }: { reservationId: string }) { const toDate = dt(booking.checkOutDate).format("YYYY-MM-DD") const hotelId = hotel.operaId const ancillaryInput = { fromDate, hotelId, toDate } - void getAncillaryPackages(ancillaryInput) const ancillaryPackages = await getAncillaryPackages(ancillaryInput) return ( @@ -70,7 +78,20 @@ export async function MyStay({ reservationId }: { reservationId: string }) { user={user} /> )} - +
+ + {booking.linkedReservations.map((linkedRes, index) => ( + } + > + + + ))} +
void + + // Get the calculated total + getTotalPrice: () => number +} + +export const useMyStayTotalPriceStore = create( + (set, get) => ({ + rooms: [], + totalPrice: 0, + currencyCode: "", + + addRoomPrice: (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) + } + + // Get currency from main booking or first room + const mainRoom = newRooms.find((r) => r.isMainBooking) || newRooms[0] + const currencyCode = mainRoom?.currencyCode || "" + + // Calculate total (only same currency for now) + const total = newRooms.reduce((sum, r) => { + if (r.currencyCode === currencyCode) { + return sum + r.totalPrice + } + return sum + }, 0) + + return { + rooms: newRooms, + totalPrice: total, + currencyCode, + } + }) + }, + + getTotalPrice: () => { + return get().totalPrice + }, + }) +) diff --git a/apps/scandic-web/types/components/hotelReservation/bookingConfirmation/rooms.ts b/apps/scandic-web/types/components/hotelReservation/bookingConfirmation/rooms.ts index 67d33273b..c954a3ac4 100644 --- a/apps/scandic-web/types/components/hotelReservation/bookingConfirmation/rooms.ts +++ b/apps/scandic-web/types/components/hotelReservation/bookingConfirmation/rooms.ts @@ -12,5 +12,5 @@ export interface BookingConfirmationRoomsProps mainRoom: Room & { bedType: Room["roomTypes"][number] } - linkedReservations: LinkedReservationSchema[] + linkedReservations?: LinkedReservationSchema[] | null }