Feat/SW-1370/Guarantee my stay ancillaries * feat(SW-1370): guarantee for ancillaries * feat(SW-1370): remove console log * feat(SW-1370): add translations * feat(SW-1370): small fix * feat(SW-1370): fix must be guaranteed * feat(SW-1370): fix logic and comments pr * feat(SW-1370): fix comments pr * feat(SW-1370): fix comments pr * feat(SW-1370): add translation * feat(SW-1370): add translation and fix pr comment * feat(SW-1370): fix pr comment * feat(SW-1370): fix encoding path refId issue * feat(SW-1370): refactor AddAncillaryStore usage and introduce context provider * feat(SW-1370): refactor * feat(SW-1370): refactor ancillaries * feat(SW-1370): fix merge Approved-by: Simon.Emanuelsson
237 lines
7.8 KiB
TypeScript
237 lines
7.8 KiB
TypeScript
"use client"
|
|
|
|
import { useState } from "react"
|
|
import { useIntl } from "react-intl"
|
|
|
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
|
|
|
import { BookingStatusEnum } from "@/constants/booking"
|
|
import { dt } from "@/lib/dt"
|
|
|
|
import { BookingCodeIcon, CheckCircleIcon } from "@/components/Icons"
|
|
import CrossCircleIcon from "@/components/Icons/CrossCircle"
|
|
import SkeletonShimmer from "@/components/SkeletonShimmer"
|
|
import Button from "@/components/TempDesignSystem/Button"
|
|
import Divider from "@/components/TempDesignSystem/Divider"
|
|
import IconChip from "@/components/TempDesignSystem/IconChip"
|
|
import Link from "@/components/TempDesignSystem/Link"
|
|
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
|
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
|
import { useGuaranteePaymentFailedToast } from "@/hooks/booking/useGuaranteePaymentFailedToast"
|
|
import useLang from "@/hooks/useLang"
|
|
import { formatPrice } from "@/utils/numberFormatting"
|
|
|
|
import ManageStay from "../ManageStay"
|
|
import { useMyStayRoomDetailsStore } from "../stores/myStayRoomDetailsStore"
|
|
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"
|
|
import { type CreditCard, type User } from "@/types/user"
|
|
|
|
interface ReferenceCardProps {
|
|
booking: BookingConfirmation["booking"]
|
|
hotel: Hotel
|
|
user: User | null
|
|
savedCreditCards: CreditCard[] | null
|
|
refId: string
|
|
}
|
|
|
|
export function ReferenceCard({
|
|
booking,
|
|
hotel,
|
|
user,
|
|
savedCreditCards,
|
|
refId,
|
|
}: ReferenceCardProps) {
|
|
const [bookingStatus, setBookingStatus] = useState(booking.reservationStatus)
|
|
const intl = useIntl()
|
|
const lang = useLang()
|
|
const { totalPrice, currencyCode } = useMyStayTotalPriceStore()
|
|
const { rooms } = useMyStayRoomDetailsStore()
|
|
|
|
const fromDate = rooms[0]
|
|
? dt(rooms[0].checkInDate).locale(lang)
|
|
: dt(booking.checkInDate).locale(lang)
|
|
const toDate = rooms[0]
|
|
? dt(rooms[0].checkOutDate).locale(lang)
|
|
: dt(booking.checkOutDate).locale(lang)
|
|
|
|
const isCancelled = bookingStatus === BookingStatusEnum.Cancelled
|
|
useGuaranteePaymentFailedToast()
|
|
|
|
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)
|
|
|
|
const adultsMsg = intl.formatMessage(
|
|
{ id: "{adults, plural, one {# adult} other {# adults}}" },
|
|
{
|
|
adults: adults,
|
|
}
|
|
)
|
|
|
|
const childrenMsg = intl.formatMessage(
|
|
{
|
|
id: "{children, plural, one {# child} other {# children}}",
|
|
},
|
|
{
|
|
children: children,
|
|
}
|
|
)
|
|
|
|
const adultsOnlyMsg = adultsMsg
|
|
const adultsAndChildrenMsg = [adultsMsg, childrenMsg].join(", ")
|
|
|
|
return (
|
|
<div className={styles.referenceCard}>
|
|
<div className={styles.referenceRow}>
|
|
<Subtitle color="uiTextHighContrast" className={styles.titleMobile}>
|
|
{intl.formatMessage({ id: "Reference" })}
|
|
</Subtitle>
|
|
<Subtitle color="uiTextHighContrast" className={styles.titleDesktop}>
|
|
{isCancelled
|
|
? intl.formatMessage({ id: "Cancellation number" })
|
|
: intl.formatMessage({ id: "Reference number" })}
|
|
</Subtitle>
|
|
<Subtitle color="uiTextHighContrast">
|
|
{isCancelled
|
|
? booking.cancellationNumber
|
|
: booking.confirmationNumber}
|
|
</Subtitle>
|
|
</div>
|
|
<Divider color="primaryLightSubtle" className={styles.divider} />
|
|
<div className={styles.referenceRow}>
|
|
<Caption
|
|
textTransform="uppercase"
|
|
type="bold"
|
|
color="uiTextHighContrast"
|
|
>
|
|
{intl.formatMessage({ id: "Guests" })}
|
|
</Caption>
|
|
<Caption type="bold" color="uiTextHighContrast">
|
|
{children > 0 ? adultsAndChildrenMsg : adultsOnlyMsg}
|
|
</Caption>
|
|
</div>
|
|
<div className={styles.referenceRow}>
|
|
<Caption
|
|
textTransform="uppercase"
|
|
type="bold"
|
|
color="uiTextHighContrast"
|
|
>
|
|
{intl.formatMessage({ id: "Check-in" })}
|
|
</Caption>
|
|
<Caption type="bold" color="uiTextHighContrast">
|
|
{`${fromDate.format("dddd, D MMMM")} ${intl.formatMessage({ id: "from" })} ${fromDate.format("HH:mm")}`}
|
|
</Caption>
|
|
</div>
|
|
<div className={styles.referenceRow}>
|
|
<Caption
|
|
textTransform="uppercase"
|
|
type="bold"
|
|
color="uiTextHighContrast"
|
|
>
|
|
{intl.formatMessage({ id: "Check-out" })}
|
|
</Caption>
|
|
<Caption type="bold" color="uiTextHighContrast">
|
|
{`${toDate.format("dddd, D MMMM")} ${intl.formatMessage({ id: "until" })} ${toDate.format("HH:mm")}`}
|
|
</Caption>
|
|
</div>
|
|
{booking.guaranteeInfo && (
|
|
<div className={styles.guaranteed}>
|
|
<CheckCircleIcon color="green" height={20} width={20} />
|
|
<Typography variant="Body/Supporting text (caption)/smRegular">
|
|
<p className={styles.guaranteedText}>
|
|
<strong>
|
|
{intl.formatMessage({ id: "Booking guaranteed." })}
|
|
</strong>{" "}
|
|
{intl.formatMessage({
|
|
id: "Your stay remains available for check-in after 18:00.",
|
|
})}
|
|
</p>
|
|
</Typography>
|
|
</div>
|
|
)}
|
|
<Divider color="primaryLightSubtle" className={styles.divider} />
|
|
<div className={styles.referenceRow}>
|
|
<Caption
|
|
textTransform="uppercase"
|
|
type="bold"
|
|
color="uiTextHighContrast"
|
|
>
|
|
{intl.formatMessage({ id: "Total" })}
|
|
</Caption>
|
|
|
|
{totalPrice ? (
|
|
<Caption type="bold" color="uiTextHighContrast">
|
|
{formatPrice(intl, totalPrice, currencyCode)}
|
|
</Caption>
|
|
) : (
|
|
<SkeletonShimmer width="50px" height="18px" />
|
|
)}
|
|
</div>
|
|
{booking?.bookingCode && (
|
|
<div className={styles.referenceRow}>
|
|
<Caption>{intl.formatMessage({ id: "Booking code" })}</Caption>
|
|
<IconChip color={"blue"} icon={<BookingCodeIcon color="blue" />}>
|
|
<Caption className={styles.bookingCode} color="blue">
|
|
<strong>{intl.formatMessage({ id: "Booking code" })}</strong>
|
|
{booking.bookingCode}
|
|
</Caption>
|
|
</IconChip>
|
|
</div>
|
|
)}
|
|
{isCancelled && (
|
|
<div className={styles.referenceRow}>
|
|
<IconChip
|
|
color={"red"}
|
|
icon={<CrossCircleIcon width={20} height={20} color="red" />}
|
|
>
|
|
<Caption color={"red"}>
|
|
<strong>{intl.formatMessage({ id: "Status" })}:</strong>{" "}
|
|
{intl.formatMessage({ id: "Cancelled" })}
|
|
</Caption>
|
|
</IconChip>
|
|
</div>
|
|
)}
|
|
<div className={styles.actionArea}>
|
|
<ManageStay
|
|
booking={booking}
|
|
hotel={hotel}
|
|
user={user}
|
|
setBookingStatus={setBookingStatus}
|
|
bookingStatus={bookingStatus}
|
|
savedCreditCards={savedCreditCards}
|
|
refId={refId}
|
|
/>
|
|
<Button fullWidth intent="secondary" asChild>
|
|
<Link href={directionsUrl} target="_blank">
|
|
{intl.formatMessage({ id: "Get directions" })}
|
|
</Link>
|
|
</Button>
|
|
</div>
|
|
{booking.isModifiable && (
|
|
<Caption className={styles.note} color="uiTextHighContrast">
|
|
{booking.rateDefinition.generalTerms.map((term) => (
|
|
<span key={term}>{term} </span>
|
|
))}
|
|
</Caption>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|