fix: display modify dates for already guaranteed changeable rates
This commit is contained in:
committed by
Michael Zetterberg
parent
b8a976db22
commit
2abd4c5c12
@@ -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}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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: [],
|
||||
|
||||
@@ -12,5 +12,7 @@ export interface BookingConfirmationRoomsProps
|
||||
mainRoom: Room & {
|
||||
bedType: Room["roomTypes"][number]
|
||||
}
|
||||
checkInTime: string
|
||||
checkOutTime: string
|
||||
linkedReservations: LinkedReservationSchema[]
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
export interface LinkedReservationProps {
|
||||
checkInTime: string
|
||||
checkOutTime: string
|
||||
confirmationNumber: string
|
||||
roomIndex: number
|
||||
}
|
||||
|
||||
@@ -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"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user