fix: display modify dates for already guaranteed changeable rates

This commit is contained in:
Simon Emanuelsson
2025-04-03 15:51:38 +02:00
committed by Michael Zetterberg
parent b8a976db22
commit 2abd4c5c12
17 changed files with 94 additions and 205 deletions

View File

@@ -15,6 +15,8 @@ import Retry from "./Retry"
import type { LinkedReservationProps } from "@/types/components/hotelReservation/bookingConfirmation/rooms/linkedReservation"
export function LinkedReservation({
checkInTime,
checkOutTime,
confirmationNumber,
roomIndex,
}: LinkedReservationProps) {
@@ -42,8 +44,10 @@ export function LinkedReservation({
return (
<Room
img={data.room.images[0]}
booking={data.booking}
checkInTime={checkInTime}
checkOutTime={checkOutTime}
img={data.room.images[0]}
roomName={data.room.name}
/>
)

View File

@@ -19,7 +19,13 @@ import styles from "./room.module.css"
import type { RoomProps } from "@/types/components/hotelReservation/bookingConfirmation/rooms/room"
export default function Room({ booking, img, roomName }: RoomProps) {
export default function Room({
booking,
checkInTime,
checkOutTime,
img,
roomName,
}: RoomProps) {
const intl = useIntl()
const lang = useLang()
@@ -115,7 +121,7 @@ export default function Room({ booking, img, roomName }: RoomProps) {
{ id: "{checkInDate} from {checkInTime}" },
{
checkInDate: fromDate.format("ddd, D MMM"),
checkInTime: fromDate.format("HH:mm"),
checkInTime: checkInTime,
}
)}
</Body>
@@ -129,7 +135,7 @@ export default function Room({ booking, img, roomName }: RoomProps) {
{ id: "{checkOutDate} from {checkOutTime}" },
{
checkOutDate: toDate.format("ddd, D MMM"),
checkOutTime: toDate.format("HH:mm"),
checkOutTime: checkOutTime,
}
)}
</Body>

View File

@@ -10,6 +10,8 @@ import type { BookingConfirmationRoomsProps } from "@/types/components/hotelRese
export default async function Rooms({
booking,
checkInTime,
checkOutTime,
mainRoom,
linkedReservations,
}: BookingConfirmationRoomsProps) {
@@ -25,6 +27,8 @@ export default async function Rooms({
) : null}
<Room
booking={booking}
checkInTime={checkInTime}
checkOutTime={checkOutTime}
img={mainRoom.images[0]}
roomName={mainRoom.name}
/>
@@ -39,6 +43,8 @@ export default async function Rooms({
)}
</Subtitle>
<LinkedReservation
checkInTime={checkInTime}
checkOutTime={checkOutTime}
confirmationNumber={reservation.confirmationNumber}
roomIndex={idx + 1}
/>

View File

@@ -56,6 +56,8 @@ export default async function BookingConfirmation({
<Alerts booking={booking} />
<Rooms
booking={booking}
checkInTime={hotel.hotelFacts.checkin.checkInTime}
checkOutTime={hotel.hotelFacts.checkin.checkOutTime}
mainRoom={room}
linkedReservations={booking.linkedReservations}
/>

View File

@@ -30,21 +30,13 @@ export default function BookingSummary({ hotel }: BookingSummaryProps) {
const bookedRoom = useMyStayRoomDetailsStore((state) => state.bookedRoom)
const {
isCancelled,
createDateTime,
guaranteeInfo,
checkInDate,
isPrePaid,
priceType,
} = bookedRoom
const { isCancelled, createDateTime, guaranteeInfo, priceType } = bookedRoom
const directionsUrl = `https://www.google.com/maps/dir/?api=1&destination=${hotel.location.latitude},${hotel.location.longitude}`
const bookingDate = dt(createDateTime).locale(lang).format("D MMMM YYYY")
const isPaid =
isPrePaid || dt(checkInDate).startOf("day").isBefore(dt().startOf("day"))
const isPaid = !!guaranteeInfo
const paymentMethod = guaranteeInfo?.paymentMethodDescription
?.toLocaleLowerCase()

View File

@@ -1,6 +1,4 @@
"use client"
import { useMemo } from "react"
import { useIntl } from "react-intl"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons"
@@ -58,38 +56,34 @@ export default function ActionPanel({ hotel }: ActionPanelProps) {
checkOutDate,
createDateTime,
canChangeDate,
isPrePaid,
priceType,
} = bookedRoom
const datetimeIsInThePast = useMemo(
() => isDatetimePast(checkInDate),
[checkInDate]
)
const datetimeIsInThePast = isDatetimePast(checkInDate)
const isDateModifyable = checkDateModifiable({
const isDateModifyable = checkDateModifiable(
canChangeDate,
datetimeIsInThePast,
isCancelled: bookedRoom.isCancelled,
isPrePaid,
isRewardNight: priceType === "points",
})
bookedRoom.isCancelled,
priceType === "points"
)
const isCancelable = checkCancelable({
bookedRoom,
linkedReservationRooms,
const isCancelable = checkCancelable(
bookedRoom.isCancelable,
datetimeIsInThePast,
})
linkedReservationRooms
)
const isGuaranteeable = checkGuaranteeable({
bookedRoom,
datetimeIsInThePast,
})
const isGuaranteeable = checkGuaranteeable(
!!bookedRoom.guaranteeInfo,
bookedRoom.isCancelled,
datetimeIsInThePast
)
const canDownloadInvoice = checkCanDownloadInvoice({
isCancelled: bookedRoom.isCancelled,
isPrePaid,
})
const canDownloadInvoice = checkCanDownloadInvoice(
bookedRoom.isCancelled,
!!bookedRoom.guaranteeInfo
)
const calendarEvent: EventAttributes = {
busyStatus: "FREE",

View File

@@ -1,91 +1,42 @@
import { CancellationRuleEnum } from "@/constants/booking"
import { dt } from "@/lib/dt"
import type { Room } from "@/stores/my-stay/myStayRoomDetailsStore"
interface ModificationConditions {
canModify: boolean
isNotPast: boolean
isNotCancelled: boolean
isNotPrePaid: boolean
isNotRewardNight: boolean
export function isDatetimePast(date: Date) {
return dt(date).hour(18).minute(0).second(0).isBefore(dt(), "seconds")
}
interface GuaranteeConditions {
isCancellableBefore6PM: boolean
hasNoGuaranteeInfo: boolean
isNotCancelled: boolean
isNotPast: boolean
}
export function isDatetimePast(date: Date): boolean {
return new Date(date) < new Date()
}
export function checkDateModifiable({
canChangeDate,
datetimeIsInThePast,
isCancelled,
isPrePaid,
isRewardNight,
}: {
canChangeDate: boolean
datetimeIsInThePast: boolean
isCancelled: boolean
isPrePaid: boolean
export function checkDateModifiable(
canChangeDate: boolean,
datetimeIsInThePast: boolean,
isCancelled: boolean,
isRewardNight: boolean
}): boolean {
const conditions: ModificationConditions = {
canModify: canChangeDate,
isNotPast: !datetimeIsInThePast,
isNotCancelled: !isCancelled,
isNotPrePaid: !isPrePaid,
isNotRewardNight: !isRewardNight,
}
return Object.values(conditions).every(Boolean)
) {
return canChangeDate && !datetimeIsInThePast && !isCancelled && !isRewardNight
}
export function checkCancelable({
bookedRoom,
linkedReservationRooms,
datetimeIsInThePast,
}: {
bookedRoom: Room
export function checkCancelable(
isCancelable: boolean,
datetimeIsInThePast: boolean,
linkedReservationRooms: Room[]
datetimeIsInThePast: boolean
}): boolean {
) {
const hasAnyCancelableRoom =
bookedRoom.isCancelable ||
linkedReservationRooms.some((room) => room.isCancelable)
isCancelable || linkedReservationRooms.some((room) => room.isCancelable)
return hasAnyCancelableRoom && !datetimeIsInThePast
}
export function checkGuaranteeable({
bookedRoom,
datetimeIsInThePast,
}: {
bookedRoom: Room
export function checkGuaranteeable(
guaranteeInfo: boolean,
isCancelled: boolean,
datetimeIsInThePast: boolean
}): boolean {
const conditions: GuaranteeConditions = {
isCancellableBefore6PM:
bookedRoom.rateDefinition.cancellationRule ===
CancellationRuleEnum.CancellableBefore6PM,
hasNoGuaranteeInfo: !bookedRoom.guaranteeInfo,
isNotCancelled: !bookedRoom.isCancelled,
isNotPast: !datetimeIsInThePast,
}
return Object.values(conditions).every(Boolean)
) {
return !guaranteeInfo && !isCancelled && !datetimeIsInThePast
}
export function checkCanDownloadInvoice({
isCancelled,
isPrePaid,
}: {
isCancelled: boolean
isPrePaid: boolean
}): boolean {
return !isCancelled && isPrePaid
export function checkCanDownloadInvoice(
isCancelled: boolean,
guaranteeInfo: boolean
) {
return !isCancelled && guaranteeInfo
}

View File

@@ -66,6 +66,7 @@ export function ReferenceCard({
const addRoomPrice = useMyStayTotalPriceStore(
(state) => state.actions.addRoomPrice
)
// Initialize store with server data
useEffect(() => {
// Add price and details for booked room (main room or single room)
@@ -95,17 +96,12 @@ export function ReferenceCard({
const {
confirmationNumber,
cancellationNumber,
checkInDate,
checkOutDate,
isCancelled,
bookingCode,
rateDefinition,
priceType,
} = bookedRoom
const fromDate = dt(checkInDate).locale(lang)
const toDate = dt(checkOutDate).locale(lang)
const isMultiRoom = bookedRoom.linkedReservations.length > 0
const directionsUrl = `https://www.google.com/maps/dir/?api=1&destination=${hotel.location.latitude},${hotel.location.longitude}`
@@ -228,7 +224,7 @@ export function ReferenceCard({
</Typography>
<Typography variant="Body/Paragraph/mdBold">
<p>
{`${fromDate.format("dddd, D MMMM")} ${intl.formatMessage({ id: "from" })} ${fromDate.format("HH:mm")}`}
{`${dt(booking.checkInDate).locale(lang).format("dddd, D MMMM")} ${intl.formatMessage({ id: "from" })} ${hotel.hotelFacts.checkin.checkInTime}`}
</p>
</Typography>
</div>
@@ -238,7 +234,7 @@ export function ReferenceCard({
</Typography>
<Typography variant="Body/Paragraph/mdBold">
<p>
{`${toDate.format("dddd, D MMMM")} ${intl.formatMessage({ id: "until" })} ${toDate.format("HH:mm")}`}
{`${dt(booking.checkOutDate).locale(lang).format("dddd, D MMMM")} ${intl.formatMessage({ id: "until" })} ${hotel.hotelFacts.checkin.checkOutTime}`}
</p>
</Typography>
</div>

View File

@@ -95,11 +95,11 @@ export default async function Rooms({
</Typography>
<TotalPrice
variant="Title/Subtitle/lg"
type={getPriceType({
rateDefinition: booking.rateDefinition,
vouchers: booking.vouchers,
cheques: booking.cheques,
})}
type={getPriceType(
booking.cheques,
booking.roomPoints,
booking.vouchers
)}
/>
</div>

View File

@@ -1,17 +1,11 @@
import type { PriceType } from "@/types/components/hotelReservation/myStay/myStay"
import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation"
type PriceTypeParams = Pick<
BookingConfirmation["booking"],
"rateDefinition" | "vouchers" | "cheques"
>
export function getPriceType({
rateDefinition,
vouchers,
cheques,
}: PriceTypeParams): PriceType {
if (rateDefinition.title === "Reward Night") return "points"
export function getPriceType(
cheques: number,
points: number,
vouchers: number
): PriceType {
if (points > 0) return "points"
if (vouchers > 0) return "voucher"
if (cheques > 0) return "cheque"
return "money"

View File

@@ -1,4 +1,4 @@
import { BookingStatusEnum, CancellationRuleEnum } from "@/constants/booking"
import { BookingStatusEnum } from "@/constants/booking"
import { dt } from "@/lib/dt"
import { formatChildBedPreferences } from "../utils"
@@ -81,16 +81,11 @@ export function mapRoomDetails({
booking.childBedPreferences
)
const isPrePaid =
!!booking.guaranteeInfo?.paymentMethodDescription ||
booking.rateDefinition.cancellationRule !==
CancellationRuleEnum.CancellableBefore6PM
const priceType = getPriceType({
rateDefinition: booking.rateDefinition,
vouchers: booking.vouchers,
cheques: booking.cheques,
})
const priceType = getPriceType(
booking.cheques,
booking.roomPoints,
booking.vouchers
)
return {
hotelId: booking.hotelId,
@@ -159,7 +154,6 @@ export function mapRoomDetails({
},
},
breakfast,
isPrePaid,
priceType,
}
}

View File

@@ -4,6 +4,7 @@ import "dayjs/locale/fi"
import "dayjs/locale/sv"
import d from "dayjs"
import nb from "dayjs/locale/nb"
import advancedFormat from "dayjs/plugin/advancedFormat"
import duration from "dayjs/plugin/duration"
import isSameOrAfter from "dayjs/plugin/isSameOrAfter"
@@ -19,43 +20,7 @@ import utc from "dayjs/plugin/utc"
* https://day.js.org/docs/en/customization/customization
* https://github.com/iamkun/dayjs/blob/dev/src/locale/nb.js
*/
d.locale("no", {
name: "no",
weekdays: "søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag".split("_"),
weekdaysShort: "sø._ma._ti._on._to._fr._lø.".split("_"),
weekdaysMin: "sø_ma_ti_on_to_fr_lø".split("_"),
months:
"januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember".split(
"_"
),
monthsShort:
"jan._feb._mars_april_mai_juni_juli_aug._sep._okt._nov._des.".split("_"),
ordinal: (n: any) => `${n}.`,
weekStart: 1,
formats: {
LT: "HH:mm",
LTS: "HH:mm:ss",
L: "DD.MM.YYYY",
LL: "D. MMMM YYYY",
LLL: "D. MMMM YYYY [kl.] HH:mm",
LLLL: "dddd D. MMMM YYYY [kl.] HH:mm",
},
relativeTime: {
future: "om %s",
past: "%s siden",
s: "noen sekunder",
m: "ett minutt",
mm: "%d minutter",
h: "en time",
hh: "%d timer",
d: "en dag",
dd: "%d dager",
M: "en måned",
MM: "%d måneder",
y: "ett år",
yy: "%d år",
},
})
d.locale("no", { ...nb, name: "no" }, true)
/**
* If more plugins are needed https://day.js.org/docs/en/plugin/plugin

View File

@@ -1,7 +1,6 @@
import { metrics } from "@opentelemetry/api"
import * as api from "@/lib/api"
import { dt } from "@/lib/dt"
import { badRequestError, serverErrorByStatus } from "@/server/errors/trpc"
import {
router,
@@ -145,24 +144,6 @@ export const bookingQueryRouter = router({
})
)
/**
* Add hotels check in and out times to booking check in and out date
* as that is date only (YYYY-MM-DD)
*/
const checkInTime = hotelData.hotel.hotelFacts.checkin.checkInTime
const [checkInHour, checkInMinute] = checkInTime.split(":")
const checkIn = dt(booking.data.checkInDate)
.set("hour", Number(checkInHour))
.set("minute", Number(checkInMinute))
const checkOutTime = hotelData.hotel.hotelFacts.checkin.checkOutTime
const [checkOutHour, checkOutMinute] = checkOutTime.split(":")
const checkOut = dt(booking.data.checkOutDate)
.set("hour", Number(checkOutHour))
.set("minute", Number(checkOutMinute))
booking.data.checkInDate = checkIn.toDate()
booking.data.checkOutDate = checkOut.toDate()
return {
...hotelData,
booking: booking.data,

View File

@@ -45,7 +45,6 @@ export type Room = Pick<
roomPrice: RoomPrice
breakfast: BreakfastPackage | null
mainRoom: boolean
isPrePaid: boolean
priceType: PriceType
}
@@ -137,7 +136,6 @@ export const useMyStayRoomDetailsStore = create<MyStayRoomDetailsState>(
breakfast: null,
linkedReservations: [],
isCancelable: false,
isPrePaid: false,
priceType: "money",
},
linkedReservationRooms: [],

View File

@@ -12,5 +12,7 @@ export interface BookingConfirmationRoomsProps
mainRoom: Room & {
bedType: Room["roomTypes"][number]
}
checkInTime: string
checkOutTime: string
linkedReservations: LinkedReservationSchema[]
}

View File

@@ -1,4 +1,6 @@
export interface LinkedReservationProps {
checkInTime: string
checkOutTime: string
confirmationNumber: string
roomIndex: number
}

View File

@@ -2,6 +2,8 @@ import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmat
export interface RoomProps {
booking: BookingConfirmation["booking"]
checkInTime: string
checkOutTime: string
img: NonNullable<BookingConfirmation["room"]>["images"][number]
roomName: NonNullable<BookingConfirmation["room"]>["name"]
}