Merged in fix/SW-2553-sidepeeks (pull request #1919)
Fix/SW-2553 sidepeeks * fix(SW-2553): apply sidepeek display logic * chore: move convertToChildType and getPriceType utils * fix: apply PR requested changes * fix(SW-2553): fix roomNumber for multiroom * fix(SW-2553): fix sidepeek for my-stay page Approved-by: Michael Zetterberg Approved-by: Bianca Widstam Approved-by: Matilda Landström
This commit is contained in:
committed by
Bianca Widstam
parent
f5f9aba2e5
commit
0c7836fa59
@@ -86,6 +86,7 @@ export function LinkedReservation({
|
||||
checkOutTime={checkOutTime}
|
||||
img={data.room.images[0]}
|
||||
roomName={data.room.name}
|
||||
roomNumber={roomIndex + 1}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
.trigger {
|
||||
align-items: center;
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--Component-Button-Brand-Secondary-On-fill-Default);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
gap: var(--Space-x1);
|
||||
padding: var(--Space-x025) 0;
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
"use client"
|
||||
import { Button as ButtonRAC, DialogTrigger } from "react-aria-components"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import { BookingStatusEnum } from "@/constants/booking"
|
||||
import { trpc } from "@/lib/trpc/client"
|
||||
import { getBookedHotelRoom } from "@/server/routers/booking/utils"
|
||||
import { useBookingConfirmationStore } from "@/stores/booking-confirmation"
|
||||
|
||||
import { convertToChildType } from "@/components/HotelReservation/utils/convertToChildType"
|
||||
import { getPriceType } from "@/components/HotelReservation/utils/getPriceType"
|
||||
import BookedRoomSidePeek from "@/components/SidePeeks/BookedRoomSidePeek"
|
||||
|
||||
import styles from "./RoomDetailsSidePeek.module.css"
|
||||
|
||||
import type { BreakfastPackage } from "@/types/components/hotelReservation/breakfast"
|
||||
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
|
||||
import { BreakfastPackageEnum } from "@/types/enums/breakfast"
|
||||
import { PackageTypeEnum } from "@/types/enums/packages"
|
||||
import type { BookingConfirmationSchema } from "@/types/trpc/routers/booking/confirmation"
|
||||
|
||||
interface RoomDetailsSidePeekProps {
|
||||
booking: BookingConfirmationSchema
|
||||
roomNumber?: number
|
||||
}
|
||||
export default function RoomDetailsSidePeek({
|
||||
booking,
|
||||
roomNumber = 1,
|
||||
}: RoomDetailsSidePeekProps) {
|
||||
const intl = useIntl()
|
||||
const user = trpc.user.getSafely.useQuery()
|
||||
const roomCategories = useBookingConfirmationStore(
|
||||
(state) => state.roomCategories
|
||||
)
|
||||
const hotelRoom = getBookedHotelRoom(roomCategories, booking.roomTypeCode)
|
||||
const breakfastPackage = booking.packages.find(
|
||||
(pkg) => pkg.code === BreakfastPackageEnum.REGULAR_BREAKFAST
|
||||
)
|
||||
const breakfast: Omit<BreakfastPackage, "requestedPrice"> | null =
|
||||
breakfastPackage
|
||||
? {
|
||||
code: breakfastPackage.code,
|
||||
description: breakfastPackage.description,
|
||||
localPrice: {
|
||||
currency: breakfastPackage.currency,
|
||||
price: breakfastPackage.unitPrice,
|
||||
totalPrice: breakfastPackage.totalPrice,
|
||||
},
|
||||
packageType: PackageTypeEnum.BreakfastAdult,
|
||||
}
|
||||
: null
|
||||
const childrenInRoom = convertToChildType(
|
||||
booking.childrenAges,
|
||||
booking.childBedPreferences
|
||||
)
|
||||
const priceType = getPriceType(
|
||||
booking.cheques,
|
||||
booking.roomPoints,
|
||||
booking.vouchers
|
||||
)
|
||||
const featuresPackages = booking.packages.filter(
|
||||
(pkg) =>
|
||||
pkg.code === RoomPackageCodeEnum.PET_ROOM ||
|
||||
pkg.code === RoomPackageCodeEnum.ALLERGY_ROOM ||
|
||||
pkg.code === RoomPackageCodeEnum.ACCESSIBILITY_ROOM
|
||||
)
|
||||
const packages = featuresPackages.map((pkg) => ({
|
||||
code: pkg.code as RoomPackageCodeEnum,
|
||||
description: pkg.description,
|
||||
inventories: [],
|
||||
itemCode: "",
|
||||
localPrice: {
|
||||
currency: pkg.currency,
|
||||
price: pkg.unitPrice,
|
||||
totalPrice: pkg.totalPrice,
|
||||
},
|
||||
requestedPrice: {
|
||||
currency: pkg.currency,
|
||||
price: pkg.unitPrice,
|
||||
totalPrice: pkg.totalPrice,
|
||||
},
|
||||
}))
|
||||
const room = {
|
||||
...booking,
|
||||
bedType: {
|
||||
description: hotelRoom?.bedType.mainBed.description ?? "",
|
||||
roomTypeCode: hotelRoom?.bedType.code ?? "",
|
||||
},
|
||||
breakfast,
|
||||
childrenInRoom,
|
||||
isCancelled: booking.reservationStatus === BookingStatusEnum.Cancelled,
|
||||
packages,
|
||||
priceType,
|
||||
roomName: hotelRoom?.name ?? "",
|
||||
roomNumber,
|
||||
terms: booking.rateDefinition.cancellationText,
|
||||
}
|
||||
return (
|
||||
<DialogTrigger>
|
||||
<Typography variant="Body/Supporting text (caption)/smBold">
|
||||
<ButtonRAC className={styles.trigger}>
|
||||
<span>
|
||||
{intl.formatMessage({ defaultMessage: "View room details" })}
|
||||
</span>
|
||||
<MaterialIcon color="CurrentColor" icon="chevron_right" size={20} />
|
||||
</ButtonRAC>
|
||||
</Typography>
|
||||
<BookedRoomSidePeek
|
||||
hotelRoom={hotelRoom}
|
||||
room={room}
|
||||
user={user.data ?? null}
|
||||
/>
|
||||
</DialogTrigger>
|
||||
)
|
||||
}
|
||||
@@ -9,12 +9,13 @@ import { CancellationRuleEnum } from "@/constants/booking"
|
||||
import { dt } from "@/lib/dt"
|
||||
|
||||
import Image from "@/components/Image"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||
import useLang from "@/hooks/useLang"
|
||||
|
||||
import RoomDetailsSidePeek from "./RoomDetailsSidePeek"
|
||||
|
||||
import styles from "./room.module.css"
|
||||
|
||||
import type { RoomProps } from "@/types/components/hotelReservation/bookingConfirmation/rooms/room"
|
||||
@@ -25,6 +26,7 @@ export default function Room({
|
||||
checkOutTime,
|
||||
img,
|
||||
roomName,
|
||||
roomNumber = 1,
|
||||
}: RoomProps) {
|
||||
const intl = useIntl()
|
||||
const lang = useLang()
|
||||
@@ -113,16 +115,7 @@ export default function Room({
|
||||
<Subtitle color="uiTextHighContrast" type="two">
|
||||
{roomName}
|
||||
</Subtitle>
|
||||
<Link color="burgundy" href="" variant="icon">
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "View room details",
|
||||
})}
|
||||
<MaterialIcon
|
||||
icon="chevron_right"
|
||||
size={20}
|
||||
color="CurrentColor"
|
||||
/>
|
||||
</Link>
|
||||
<RoomDetailsSidePeek booking={booking} roomNumber={roomNumber} />
|
||||
</div>
|
||||
<ul className={styles.details}>
|
||||
<li className={styles.listItem}>
|
||||
|
||||
@@ -30,7 +30,7 @@ export default async function BookingConfirmation({
|
||||
if (!bookingConfirmation) {
|
||||
return notFound()
|
||||
}
|
||||
const { booking, hotel, room } = bookingConfirmation
|
||||
const { booking, hotel, room, roomCategories } = bookingConfirmation
|
||||
if (!room) {
|
||||
return notFound()
|
||||
}
|
||||
@@ -46,6 +46,7 @@ export default async function BookingConfirmation({
|
||||
currencyCode={booking.currencyCode}
|
||||
fromDate={booking.checkInDate}
|
||||
toDate={booking.checkOutDate}
|
||||
roomCategories={roomCategories}
|
||||
rooms={[
|
||||
mapRoomState(booking, room, intl),
|
||||
// null represents "known but not yet fetched rooms" and is used to render placeholders correctly
|
||||
|
||||
@@ -1,315 +0,0 @@
|
||||
"use client"
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { useState } from "react"
|
||||
import { Dialog } from "react-aria-components"
|
||||
import { FormProvider, useForm } from "react-hook-form"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import { trpc } from "@/lib/trpc/client"
|
||||
|
||||
import MembershipLevelIcon from "@/components/Levels/Icon"
|
||||
import Modal from "@/components/Modal"
|
||||
import { ModalContentWithActions } from "@/components/Modal/ModalContentWithActions"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import { toast } from "@/components/TempDesignSystem/Toasts"
|
||||
import useLang from "@/hooks/useLang"
|
||||
|
||||
import ModifyContact from "../ModifyContact"
|
||||
|
||||
import styles from "./guestDetails.module.css"
|
||||
|
||||
import {
|
||||
type ModifyContactSchema,
|
||||
modifyContactSchema,
|
||||
} from "@/types/components/hotelReservation/myStay/modifyContact"
|
||||
import { MODAL_STEPS } from "@/types/components/hotelReservation/myStay/myStay"
|
||||
import type { Room } from "@/types/stores/my-stay"
|
||||
import type { SafeUser } from "@/types/user"
|
||||
|
||||
interface DetailsProps {
|
||||
booking: Room
|
||||
user: SafeUser
|
||||
}
|
||||
|
||||
export default function Details({ booking, user }: DetailsProps) {
|
||||
const intl = useIntl()
|
||||
const lang = useLang()
|
||||
const router = useRouter()
|
||||
const utils = trpc.useUtils()
|
||||
const [currentStep, setCurrentStep] = useState(MODAL_STEPS.INITIAL)
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
|
||||
const [isModifyGuestDetailsOpen, setIsModifyGuestDetailsOpen] =
|
||||
useState(false)
|
||||
|
||||
const form = useForm<ModifyContactSchema>({
|
||||
resolver: zodResolver(modifyContactSchema),
|
||||
defaultValues: {
|
||||
firstName: booking.guest.firstName,
|
||||
lastName: booking.guest.lastName,
|
||||
email: booking.guest.email,
|
||||
phoneNumber: booking.guest.phoneNumber,
|
||||
countryCode: booking.guest.countryCode,
|
||||
},
|
||||
})
|
||||
|
||||
const isFirstStep = currentStep === MODAL_STEPS.INITIAL
|
||||
|
||||
const isMemberBooking =
|
||||
booking.guest.membershipNumber === user?.membership?.membershipNumber
|
||||
|
||||
const updateGuest = trpc.booking.update.useMutation({
|
||||
onMutate: () => setIsLoading(true),
|
||||
onSuccess: (data) => {
|
||||
if (data) {
|
||||
utils.booking.get.invalidate({
|
||||
confirmationNumber: data.confirmationNumber,
|
||||
})
|
||||
|
||||
toast.success(
|
||||
intl.formatMessage({
|
||||
defaultMessage: "Guest details updated",
|
||||
})
|
||||
)
|
||||
setIsModifyGuestDetailsOpen(false)
|
||||
setCurrentStep(MODAL_STEPS.INITIAL)
|
||||
} else {
|
||||
toast.error(
|
||||
intl.formatMessage({
|
||||
defaultMessage: "Failed to update guest details",
|
||||
})
|
||||
)
|
||||
}
|
||||
},
|
||||
onError: () => {
|
||||
toast.error(
|
||||
intl.formatMessage({
|
||||
defaultMessage: "Failed to update guest details",
|
||||
})
|
||||
)
|
||||
},
|
||||
onSettled: () => {
|
||||
setIsLoading(false)
|
||||
},
|
||||
})
|
||||
|
||||
async function onSubmit(data: ModifyContactSchema) {
|
||||
updateGuest.mutate({
|
||||
confirmationNumber: booking.confirmationNumber,
|
||||
guest: {
|
||||
email: data.email,
|
||||
phoneNumber: data.phoneNumber,
|
||||
countryCode: data.countryCode,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function handleModifyMemberDetails() {
|
||||
const expirationTime = Date.now() + 10 * 60 * 1000
|
||||
sessionStorage.setItem(
|
||||
"myStayReturnRoute",
|
||||
JSON.stringify({
|
||||
path: window.location.href,
|
||||
expiry: expirationTime,
|
||||
})
|
||||
)
|
||||
router.push(`/${lang}/scandic-friends/my-pages/profile/edit`)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.guestDetails}>
|
||||
{isMemberBooking && user.membership && (
|
||||
<div className={styles.userDetails}>
|
||||
<div className={styles.userDetailsTitle}>
|
||||
<Typography variant="Title/Overline/sm">
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Your member tier",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
</div>
|
||||
<div className={styles.memberLevel}>
|
||||
<MembershipLevelIcon
|
||||
level={user.membership.membershipLevel}
|
||||
color="red"
|
||||
rows={1}
|
||||
className={styles.memberLevelIcon}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.totalPoints}>
|
||||
<div className={styles.totalPointsText}>
|
||||
<MaterialIcon icon="diamond" color="Icon/Intense" />
|
||||
|
||||
<Typography variant="Title/Overline/sm">
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "My total points",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
</div>
|
||||
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p>{user.membership.currentPoints}</p>
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className={styles.guest}>
|
||||
<Typography variant="Body/Paragraph/mdBold">
|
||||
<p>
|
||||
{booking.guest.firstName} {booking.guest.lastName}
|
||||
</p>
|
||||
</Typography>
|
||||
{isMemberBooking && user.membership && (
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p className={styles.memberNumber}>
|
||||
{intl.formatMessage(
|
||||
{
|
||||
defaultMessage: "Member no. {nr}",
|
||||
},
|
||||
{
|
||||
nr: user.membership.membershipNumber,
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
</Typography>
|
||||
)}
|
||||
<div className={styles.contactInfoMobile}>
|
||||
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||
<p color="uiTextHighContrast">{booking.guest.email}</p>
|
||||
</Typography>
|
||||
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||
<p color="uiTextHighContrast">{booking.guest.phoneNumber}</p>
|
||||
</Typography>
|
||||
</div>
|
||||
<div className={styles.contactInfoDesktop}>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p color="uiTextHighContrast">{booking.guest.email}</p>
|
||||
</Typography>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p color="uiTextHighContrast">{booking.guest.phoneNumber}</p>
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
{isMemberBooking ? (
|
||||
<Button
|
||||
variant="icon"
|
||||
color="burgundy"
|
||||
intent={"secondary"}
|
||||
onClick={handleModifyMemberDetails}
|
||||
disabled={booking.isCancelled}
|
||||
size="small"
|
||||
>
|
||||
<MaterialIcon
|
||||
icon="edit"
|
||||
color="Icon/Interactive/Default"
|
||||
size={20}
|
||||
/>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<span>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Modify guest details",
|
||||
})}
|
||||
</span>
|
||||
</Typography>
|
||||
</Button>
|
||||
) : (
|
||||
<>
|
||||
<Button
|
||||
variant="icon"
|
||||
color="burgundy"
|
||||
intent="secondary"
|
||||
onClick={() =>
|
||||
setIsModifyGuestDetailsOpen(!isModifyGuestDetailsOpen)
|
||||
}
|
||||
disabled={booking.isCancelled}
|
||||
size="small"
|
||||
>
|
||||
<MaterialIcon
|
||||
icon="edit"
|
||||
color={
|
||||
booking.isCancelled
|
||||
? "Icon/Interactive/Disabled"
|
||||
: "Icon/Interactive/Default"
|
||||
}
|
||||
size={20}
|
||||
/>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<span>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Modify guest details",
|
||||
})}
|
||||
</span>
|
||||
</Typography>
|
||||
</Button>
|
||||
{isModifyGuestDetailsOpen && (
|
||||
<Modal
|
||||
withActions
|
||||
hideHeader
|
||||
isOpen={isModifyGuestDetailsOpen}
|
||||
onToggle={setIsModifyGuestDetailsOpen}
|
||||
>
|
||||
<Dialog
|
||||
aria-label={intl.formatMessage({
|
||||
defaultMessage: "Modify guest details",
|
||||
})}
|
||||
>
|
||||
{({ close }) => (
|
||||
<FormProvider {...form}>
|
||||
<ModalContentWithActions
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Modify guest details",
|
||||
})}
|
||||
onClose={() => setIsModifyGuestDetailsOpen(false)}
|
||||
content={
|
||||
booking.guest && (
|
||||
<ModifyContact
|
||||
guest={booking.guest}
|
||||
isFirstStep={isFirstStep}
|
||||
/>
|
||||
)
|
||||
}
|
||||
primaryAction={{
|
||||
label: isFirstStep
|
||||
? intl.formatMessage({
|
||||
defaultMessage: "Save updates",
|
||||
})
|
||||
: intl.formatMessage({
|
||||
defaultMessage: "Confirm",
|
||||
}),
|
||||
onClick: isFirstStep
|
||||
? () => setCurrentStep(MODAL_STEPS.CONFIRMATION)
|
||||
: () => form.handleSubmit(onSubmit)(),
|
||||
disabled: !form.formState.isValid || isLoading,
|
||||
intent: isFirstStep ? "secondary" : "primary",
|
||||
}}
|
||||
secondaryAction={{
|
||||
label: isFirstStep
|
||||
? intl.formatMessage({
|
||||
defaultMessage: "Back",
|
||||
})
|
||||
: intl.formatMessage({
|
||||
defaultMessage: "Cancel",
|
||||
}),
|
||||
onClick: () => {
|
||||
close()
|
||||
setCurrentStep(MODAL_STEPS.INITIAL)
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</FormProvider>
|
||||
)}
|
||||
</Dialog>
|
||||
</Modal>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,22 +1,322 @@
|
||||
"use client"
|
||||
import { useMyStayStore } from "@/stores/my-stay"
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { useState } from "react"
|
||||
import { Dialog } from "react-aria-components"
|
||||
import { FormProvider, useForm } from "react-hook-form"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import Details from "./Details"
|
||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import type { Room } from "@/types/stores/my-stay"
|
||||
import { trpc } from "@/lib/trpc/client"
|
||||
|
||||
import MembershipLevelIcon from "@/components/Levels/Icon"
|
||||
import Modal from "@/components/Modal"
|
||||
import { ModalContentWithActions } from "@/components/Modal/ModalContentWithActions"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import { toast } from "@/components/TempDesignSystem/Toasts"
|
||||
import useLang from "@/hooks/useLang"
|
||||
|
||||
import ModifyContact from "../ModifyContact"
|
||||
|
||||
import styles from "./guestDetails.module.css"
|
||||
|
||||
import {
|
||||
type ModifyContactSchema,
|
||||
modifyContactSchema,
|
||||
} from "@/types/components/hotelReservation/myStay/modifyContact"
|
||||
import { MODAL_STEPS } from "@/types/components/hotelReservation/myStay/myStay"
|
||||
import type { SafeUser } from "@/types/user"
|
||||
import type { Guest } from "@/server/routers/booking/output"
|
||||
|
||||
interface GuestDetailsProps {
|
||||
selectedRoom?: Room
|
||||
confirmationNumber: string
|
||||
guest: Guest
|
||||
isCancelled: boolean
|
||||
user: SafeUser
|
||||
}
|
||||
|
||||
export default function GuestDetails({
|
||||
selectedRoom,
|
||||
confirmationNumber,
|
||||
guest,
|
||||
isCancelled,
|
||||
user,
|
||||
}: GuestDetailsProps) {
|
||||
const booking = useMyStayStore((state) => state.bookedRoom)
|
||||
const room = selectedRoom ? selectedRoom : booking
|
||||
const intl = useIntl()
|
||||
const lang = useLang()
|
||||
const router = useRouter()
|
||||
const utils = trpc.useUtils()
|
||||
const [currentStep, setCurrentStep] = useState(MODAL_STEPS.INITIAL)
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
|
||||
return <Details booking={room} user={user} />
|
||||
const [isModifyGuestDetailsOpen, setIsModifyGuestDetailsOpen] =
|
||||
useState(false)
|
||||
|
||||
const form = useForm<ModifyContactSchema>({
|
||||
resolver: zodResolver(modifyContactSchema),
|
||||
defaultValues: {
|
||||
firstName: guest.firstName,
|
||||
lastName: guest.lastName,
|
||||
email: guest.email,
|
||||
phoneNumber: guest.phoneNumber,
|
||||
countryCode: guest.countryCode,
|
||||
},
|
||||
})
|
||||
|
||||
const isFirstStep = currentStep === MODAL_STEPS.INITIAL
|
||||
|
||||
const isMemberBooking =
|
||||
guest.membershipNumber === user?.membership?.membershipNumber
|
||||
|
||||
const updateGuest = trpc.booking.update.useMutation({
|
||||
onMutate: () => setIsLoading(true),
|
||||
onSuccess: (data) => {
|
||||
if (data) {
|
||||
utils.booking.get.invalidate({
|
||||
confirmationNumber: data.confirmationNumber,
|
||||
})
|
||||
|
||||
toast.success(
|
||||
intl.formatMessage({
|
||||
defaultMessage: "Guest details updated",
|
||||
})
|
||||
)
|
||||
setIsModifyGuestDetailsOpen(false)
|
||||
setCurrentStep(MODAL_STEPS.INITIAL)
|
||||
} else {
|
||||
toast.error(
|
||||
intl.formatMessage({
|
||||
defaultMessage: "Failed to update guest details",
|
||||
})
|
||||
)
|
||||
}
|
||||
},
|
||||
onError: () => {
|
||||
toast.error(
|
||||
intl.formatMessage({
|
||||
defaultMessage: "Failed to update guest details",
|
||||
})
|
||||
)
|
||||
},
|
||||
onSettled: () => {
|
||||
setIsLoading(false)
|
||||
},
|
||||
})
|
||||
|
||||
async function onSubmit(data: ModifyContactSchema) {
|
||||
updateGuest.mutate({
|
||||
confirmationNumber,
|
||||
guest: {
|
||||
email: data.email,
|
||||
phoneNumber: data.phoneNumber,
|
||||
countryCode: data.countryCode,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function handleModifyMemberDetails() {
|
||||
const expirationTime = Date.now() + 10 * 60 * 1000
|
||||
sessionStorage.setItem(
|
||||
"myStayReturnRoute",
|
||||
JSON.stringify({
|
||||
path: window.location.href,
|
||||
expiry: expirationTime,
|
||||
})
|
||||
)
|
||||
router.push(`/${lang}/scandic-friends/my-pages/profile/edit`)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.guestDetails}>
|
||||
{isMemberBooking && user.membership && (
|
||||
<div className={styles.userDetails}>
|
||||
<div className={styles.userDetailsTitle}>
|
||||
<Typography variant="Title/Overline/sm">
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Your member tier",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
</div>
|
||||
<div className={styles.memberLevel}>
|
||||
<MembershipLevelIcon
|
||||
level={user.membership.membershipLevel}
|
||||
color="red"
|
||||
rows={1}
|
||||
className={styles.memberLevelIcon}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.totalPoints}>
|
||||
<div className={styles.totalPointsText}>
|
||||
<MaterialIcon icon="diamond" color="Icon/Intense" />
|
||||
|
||||
<Typography variant="Title/Overline/sm">
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "My total points",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
</div>
|
||||
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p>{user.membership.currentPoints}</p>
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className={styles.guest}>
|
||||
<Typography variant="Body/Paragraph/mdBold">
|
||||
<p>
|
||||
{guest.firstName} {guest.lastName}
|
||||
</p>
|
||||
</Typography>
|
||||
{isMemberBooking && user.membership && (
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p className={styles.memberNumber}>
|
||||
{intl.formatMessage(
|
||||
{
|
||||
defaultMessage: "Member no. {nr}",
|
||||
},
|
||||
{
|
||||
nr: user.membership.membershipNumber,
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
</Typography>
|
||||
)}
|
||||
<div className={styles.contactInfoMobile}>
|
||||
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||
<p color="uiTextHighContrast">{guest.email}</p>
|
||||
</Typography>
|
||||
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||
<p color="uiTextHighContrast">{guest.phoneNumber}</p>
|
||||
</Typography>
|
||||
</div>
|
||||
<div className={styles.contactInfoDesktop}>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p color="uiTextHighContrast">{guest.email}</p>
|
||||
</Typography>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p color="uiTextHighContrast">{guest.phoneNumber}</p>
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
{isMemberBooking ? (
|
||||
<Button
|
||||
variant="icon"
|
||||
color="burgundy"
|
||||
intent="secondary"
|
||||
onClick={handleModifyMemberDetails}
|
||||
disabled={isCancelled}
|
||||
size="small"
|
||||
>
|
||||
<MaterialIcon
|
||||
icon="edit"
|
||||
color="Icon/Interactive/Default"
|
||||
size={20}
|
||||
/>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<span>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Modify guest details",
|
||||
})}
|
||||
</span>
|
||||
</Typography>
|
||||
</Button>
|
||||
) : (
|
||||
<>
|
||||
<Button
|
||||
variant="icon"
|
||||
color="burgundy"
|
||||
intent="secondary"
|
||||
onClick={() =>
|
||||
setIsModifyGuestDetailsOpen(!isModifyGuestDetailsOpen)
|
||||
}
|
||||
disabled={isCancelled}
|
||||
size="small"
|
||||
>
|
||||
<MaterialIcon
|
||||
icon="edit"
|
||||
color={
|
||||
isCancelled
|
||||
? "Icon/Interactive/Disabled"
|
||||
: "Icon/Interactive/Default"
|
||||
}
|
||||
size={20}
|
||||
/>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<span>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Modify guest details",
|
||||
})}
|
||||
</span>
|
||||
</Typography>
|
||||
</Button>
|
||||
{isModifyGuestDetailsOpen && (
|
||||
<Modal
|
||||
withActions
|
||||
hideHeader
|
||||
isOpen={isModifyGuestDetailsOpen}
|
||||
onToggle={setIsModifyGuestDetailsOpen}
|
||||
>
|
||||
<Dialog
|
||||
aria-label={intl.formatMessage({
|
||||
defaultMessage: "Modify guest details",
|
||||
})}
|
||||
>
|
||||
{({ close }) => (
|
||||
<FormProvider {...form}>
|
||||
<ModalContentWithActions
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Modify guest details",
|
||||
})}
|
||||
onClose={() => setIsModifyGuestDetailsOpen(false)}
|
||||
content={
|
||||
guest && (
|
||||
<ModifyContact
|
||||
guest={guest}
|
||||
isFirstStep={isFirstStep}
|
||||
/>
|
||||
)
|
||||
}
|
||||
primaryAction={{
|
||||
label: isFirstStep
|
||||
? intl.formatMessage({
|
||||
defaultMessage: "Save updates",
|
||||
})
|
||||
: intl.formatMessage({
|
||||
defaultMessage: "Confirm",
|
||||
}),
|
||||
onClick: isFirstStep
|
||||
? () => setCurrentStep(MODAL_STEPS.CONFIRMATION)
|
||||
: form.handleSubmit(onSubmit),
|
||||
disabled: !form.formState.isValid || isLoading,
|
||||
intent: isFirstStep ? "secondary" : "primary",
|
||||
}}
|
||||
secondaryAction={{
|
||||
label: isFirstStep
|
||||
? intl.formatMessage({
|
||||
defaultMessage: "Back",
|
||||
})
|
||||
: intl.formatMessage({
|
||||
defaultMessage: "Cancel",
|
||||
}),
|
||||
onClick: () => {
|
||||
close()
|
||||
setCurrentStep(MODAL_STEPS.INITIAL)
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</FormProvider>
|
||||
)}
|
||||
</Dialog>
|
||||
</Modal>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||
|
||||
import useSidePeekStore from "@/stores/sidepeek"
|
||||
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
|
||||
import styles from "./toggleSidePeek.module.css"
|
||||
|
||||
import { SidePeekEnum } from "@/types/components/hotelReservation/sidePeek"
|
||||
import type { ToggleSidePeekProps } from "@/types/components/hotelReservation/toggleSidePeekProps"
|
||||
|
||||
export default function ToggleSidePeek({
|
||||
hotelId,
|
||||
roomTypeCode,
|
||||
user,
|
||||
confirmationNumber,
|
||||
}: ToggleSidePeekProps) {
|
||||
const openSidePeek = useSidePeekStore((state) => state.openSidePeek)
|
||||
|
||||
return (
|
||||
<Button
|
||||
onClick={() =>
|
||||
openSidePeek({
|
||||
key: SidePeekEnum.bookedRoomDetails,
|
||||
hotelId,
|
||||
roomTypeCode,
|
||||
user,
|
||||
confirmationNumber,
|
||||
})
|
||||
}
|
||||
size="small"
|
||||
variant="icon"
|
||||
intent="text"
|
||||
wrapping
|
||||
>
|
||||
<div className={styles.iconContainer}>
|
||||
<MaterialIcon icon="pan_zoom" color="CurrentColor" />
|
||||
</div>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
@@ -1,13 +1,16 @@
|
||||
"use client"
|
||||
import { Button as ButtonRAC, DialogTrigger } from "react-aria-components"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import { dt } from "@/lib/dt"
|
||||
import { getBookedHotelRoom } from "@/server/routers/booking/utils"
|
||||
|
||||
import { IconForFeatureCode } from "@/components/HotelReservation/utils"
|
||||
import Image from "@/components/Image"
|
||||
import BookedRoomSidePeek from "@/components/SidePeeks/BookedRoomSidePeek"
|
||||
import Divider from "@/components/TempDesignSystem/Divider"
|
||||
import IconChip from "@/components/TempDesignSystem/IconChip"
|
||||
import useLang from "@/hooks/useLang"
|
||||
@@ -15,11 +18,11 @@ import { formatPrice } from "@/utils/numberFormatting"
|
||||
|
||||
import PriceType from "../../PriceType"
|
||||
import { hasModifiableRate } from "../../utils"
|
||||
import ToggleSidePeek from "./ToggleSidePeek"
|
||||
|
||||
import styles from "./multiRoom.module.css"
|
||||
|
||||
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
|
||||
import type { RoomCategories } from "@/types/hotel"
|
||||
import type { Room } from "@/types/stores/my-stay"
|
||||
import type { SafeUser } from "@/types/user"
|
||||
|
||||
@@ -27,9 +30,15 @@ interface MultiRoomProps {
|
||||
booking: Room
|
||||
roomNr: number
|
||||
user: SafeUser
|
||||
roomCategories: RoomCategories
|
||||
}
|
||||
|
||||
export default function MultiRoom({ booking, roomNr, user }: MultiRoomProps) {
|
||||
export default function MultiRoom({
|
||||
booking,
|
||||
roomNr,
|
||||
user,
|
||||
roomCategories,
|
||||
}: MultiRoomProps) {
|
||||
const intl = useIntl()
|
||||
const lang = useLang()
|
||||
|
||||
@@ -42,7 +51,6 @@ export default function MultiRoom({ booking, roomNr, user }: MultiRoomProps) {
|
||||
childrenAges,
|
||||
confirmationNumber,
|
||||
currencyCode,
|
||||
hotelId,
|
||||
packages,
|
||||
rateDefinition,
|
||||
room,
|
||||
@@ -94,6 +102,7 @@ export default function MultiRoom({ booking, roomNr, user }: MultiRoomProps) {
|
||||
breakfast.localPrice.currency
|
||||
)
|
||||
}
|
||||
const hotelRoom = getBookedHotelRoom(roomCategories, roomTypeCode)
|
||||
|
||||
return (
|
||||
<article className={styles.multiRoom}>
|
||||
@@ -167,12 +176,21 @@ export default function MultiRoom({ booking, roomNr, user }: MultiRoomProps) {
|
||||
</Typography>
|
||||
</div>
|
||||
<div className={styles.toggleSidePeek}>
|
||||
<ToggleSidePeek
|
||||
hotelId={hotelId}
|
||||
roomTypeCode={roomTypeCode}
|
||||
user={user}
|
||||
confirmationNumber={confirmationNumber}
|
||||
/>
|
||||
<DialogTrigger>
|
||||
<ButtonRAC
|
||||
aria-label={intl.formatMessage({
|
||||
defaultMessage: "View room details",
|
||||
})}
|
||||
className={styles.iconContainer}
|
||||
>
|
||||
<MaterialIcon icon="pan_zoom" color="CurrentColor" />
|
||||
</ButtonRAC>
|
||||
<BookedRoomSidePeek
|
||||
hotelRoom={hotelRoom}
|
||||
room={booking}
|
||||
user={user}
|
||||
/>
|
||||
</DialogTrigger>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
|
||||
@@ -31,6 +31,13 @@
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.iconContainer {
|
||||
display: flex;
|
||||
border: 1px solid var(--Base-Border-Subtle);
|
||||
border-radius: var(--Corner-radius-Small);
|
||||
padding: var(--Spacing-x-half);
|
||||
}
|
||||
|
||||
.roomName {
|
||||
color: var(--Scandic-Brand-Burgundy);
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
.iconContainer {
|
||||
display: flex;
|
||||
border: 1px solid var(--Base-Border-Subtle);
|
||||
border-radius: var(--Corner-radius-Small);
|
||||
padding: var(--Spacing-x-half);
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
"use client"
|
||||
import { Button as ButtonRAC, DialogTrigger } from "react-aria-components"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import DiscountIcon from "@scandic-hotels/design-system/Icons/DiscountIcon"
|
||||
@@ -6,6 +7,7 @@ import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import { dt } from "@/lib/dt"
|
||||
import { getBookedHotelRoom } from "@/server/routers/booking/utils"
|
||||
import { useMyStayStore } from "@/stores/my-stay"
|
||||
|
||||
import GuestDetails from "@/components/HotelReservation/MyStay/GuestDetails"
|
||||
@@ -14,26 +16,31 @@ import PriceType from "@/components/HotelReservation/MyStay/PriceType"
|
||||
import { hasModifiableRate } from "@/components/HotelReservation/MyStay/utils"
|
||||
import { IconForFeatureCode } from "@/components/HotelReservation/utils"
|
||||
import Image from "@/components/Image"
|
||||
import BookedRoomSidePeek from "@/components/SidePeeks/BookedRoomSidePeek"
|
||||
import SkeletonShimmer from "@/components/SkeletonShimmer"
|
||||
import IconChip from "@/components/TempDesignSystem/IconChip"
|
||||
import useLang from "@/hooks/useLang"
|
||||
import { formatPrice } from "@/utils/numberFormatting"
|
||||
|
||||
import ToggleSidePeek from "./ToggleSidePeek"
|
||||
|
||||
import styles from "./room.module.css"
|
||||
|
||||
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
|
||||
import type { Room } from "@/types/hotel"
|
||||
import type { Room, RoomCategories } from "@/types/hotel"
|
||||
import type { SafeUser } from "@/types/user"
|
||||
|
||||
interface RoomProps {
|
||||
bedType: Room["roomTypes"][number]
|
||||
image: Room["images"][number]
|
||||
user: SafeUser
|
||||
roomCategories: RoomCategories
|
||||
}
|
||||
|
||||
export default function SingleRoom({ bedType, image, user }: RoomProps) {
|
||||
export default function SingleRoom({
|
||||
bedType,
|
||||
image,
|
||||
user,
|
||||
roomCategories,
|
||||
}: RoomProps) {
|
||||
const intl = useIntl()
|
||||
const lang = useLang()
|
||||
|
||||
@@ -46,7 +53,7 @@ export default function SingleRoom({ bedType, image, user }: RoomProps) {
|
||||
childrenAges,
|
||||
confirmationNumber,
|
||||
formattedTotalPrice,
|
||||
hotel,
|
||||
guest,
|
||||
isCancelled,
|
||||
packages,
|
||||
priceType,
|
||||
@@ -57,10 +64,12 @@ export default function SingleRoom({ bedType, image, user }: RoomProps) {
|
||||
roomTypeCode,
|
||||
totalPrice,
|
||||
vouchers,
|
||||
bookedRoom,
|
||||
} = useMyStayStore((state) => ({
|
||||
adults: state.bookedRoom.adults,
|
||||
bookingCode: state.bookedRoom.bookingCode,
|
||||
breakfast: state.bookedRoom.breakfast,
|
||||
guest: state.bookedRoom.guest,
|
||||
checkInDate: state.bookedRoom.checkInDate,
|
||||
cheques: state.bookedRoom.cheques,
|
||||
childrenAges: state.bookedRoom.childrenAges,
|
||||
@@ -77,6 +86,7 @@ export default function SingleRoom({ bedType, image, user }: RoomProps) {
|
||||
roomTypeCode: state.bookedRoom.roomTypeCode,
|
||||
totalPrice: state.bookedRoom.totalPrice,
|
||||
vouchers: state.bookedRoom.vouchers,
|
||||
bookedRoom: state.bookedRoom,
|
||||
}))
|
||||
|
||||
if (!roomNumber) {
|
||||
@@ -149,6 +159,8 @@ export default function SingleRoom({ bedType, image, user }: RoomProps) {
|
||||
)
|
||||
}
|
||||
|
||||
const hotelRoom = getBookedHotelRoom(roomCategories, roomTypeCode)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<article className={styles.room}>
|
||||
@@ -187,11 +199,27 @@ export default function SingleRoom({ bedType, image, user }: RoomProps) {
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.sidePeek}>
|
||||
<ToggleSidePeek
|
||||
hotelId={hotel.operaId}
|
||||
roomTypeCode={roomTypeCode}
|
||||
intent="text"
|
||||
/>
|
||||
<DialogTrigger>
|
||||
<Typography variant="Body/Supporting text (caption)/smBold">
|
||||
<ButtonRAC className={styles.trigger}>
|
||||
<span>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "View room details",
|
||||
})}
|
||||
</span>
|
||||
<MaterialIcon
|
||||
color="CurrentColor"
|
||||
icon="chevron_right"
|
||||
size={20}
|
||||
/>
|
||||
</ButtonRAC>
|
||||
</Typography>
|
||||
<BookedRoomSidePeek
|
||||
hotelRoom={hotelRoom}
|
||||
room={bookedRoom}
|
||||
user={user}
|
||||
/>
|
||||
</DialogTrigger>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.booking}>
|
||||
@@ -401,7 +429,12 @@ export default function SingleRoom({ bedType, image, user }: RoomProps) {
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.guestDetailsDesktopWrapper}>
|
||||
<GuestDetails user={user} />
|
||||
<GuestDetails
|
||||
confirmationNumber={confirmationNumber}
|
||||
guest={guest}
|
||||
isCancelled={isCancelled}
|
||||
user={user}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -454,7 +487,12 @@ export default function SingleRoom({ bedType, image, user }: RoomProps) {
|
||||
|
||||
<PriceDetails />
|
||||
<div className={styles.guestDetailsMobileWrapper}>
|
||||
<GuestDetails user={user} />
|
||||
<GuestDetails
|
||||
confirmationNumber={confirmationNumber}
|
||||
guest={guest}
|
||||
isCancelled={isCancelled}
|
||||
user={user}
|
||||
/>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
@@ -6,6 +6,17 @@
|
||||
padding: var(--Spacing-x3) 0;
|
||||
}
|
||||
|
||||
.trigger {
|
||||
align-items: center;
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--Component-Button-Brand-Secondary-On-fill-Default);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
gap: var(--Space-x1);
|
||||
padding: var(--Space-x025) 0;
|
||||
}
|
||||
|
||||
.roomName {
|
||||
color: var(--Scandic-Brand-Burgundy);
|
||||
padding: 0 var(--Spacing-x2);
|
||||
|
||||
@@ -20,12 +20,15 @@ interface RoomsProps {
|
||||
|
||||
export default function Rooms({ user }: RoomsProps) {
|
||||
const intl = useIntl()
|
||||
const { allRoomsAreCancelled, room, rooms } = useMyStayStore((state) => ({
|
||||
allRoomsAreCancelled: state.allRoomsAreCancelled,
|
||||
hotel: state.hotel,
|
||||
room: state.bookedRoom.room,
|
||||
rooms: state.rooms,
|
||||
}))
|
||||
const { allRoomsAreCancelled, room, rooms, roomCategories } = useMyStayStore(
|
||||
(state) => ({
|
||||
allRoomsAreCancelled: state.allRoomsAreCancelled,
|
||||
hotel: state.hotel,
|
||||
room: state.bookedRoom.room,
|
||||
rooms: state.rooms,
|
||||
roomCategories: state.roomCategories,
|
||||
})
|
||||
)
|
||||
|
||||
if (!room) {
|
||||
return null
|
||||
@@ -50,6 +53,7 @@ export default function Rooms({ user }: RoomsProps) {
|
||||
bedType={room.bedType}
|
||||
image={room.images[0]}
|
||||
user={user}
|
||||
roomCategories={roomCategories}
|
||||
/>
|
||||
) : (
|
||||
<div className={styles.roomsContainer}>
|
||||
@@ -58,7 +62,12 @@ export default function Rooms({ user }: RoomsProps) {
|
||||
key={booking.confirmationNumber}
|
||||
className={styles.roomWrapper}
|
||||
>
|
||||
<MultiRoom booking={booking} roomNr={index + 1} user={user} />
|
||||
<MultiRoom
|
||||
booking={booking}
|
||||
roomNr={index + 1}
|
||||
user={user}
|
||||
roomCategories={roomCategories}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { BookingStatusEnum, CancellationRuleEnum } from "@/constants/booking"
|
||||
import { dt } from "@/lib/dt"
|
||||
|
||||
import { convertToChildType } from "@/components/HotelReservation/utils/convertToChildType"
|
||||
import { getPriceType } from "@/components/HotelReservation/utils/getPriceType"
|
||||
|
||||
import { formatChildBedPreferences } from "../utils"
|
||||
import { convertToChildType } from "./convertToChildType"
|
||||
import { getPriceType } from "./getPriceType"
|
||||
|
||||
import type { BreakfastPackage } from "@/types/components/hotelReservation/breakfast"
|
||||
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
|
||||
|
||||
@@ -13,8 +13,6 @@ import type { ToggleSidePeekProps } from "@/types/components/hotelReservation/to
|
||||
export default function ToggleSidePeek({
|
||||
hotelId,
|
||||
roomTypeCode,
|
||||
intent = "textInverted",
|
||||
title,
|
||||
}: ToggleSidePeekProps) {
|
||||
const intl = useIntl()
|
||||
const openSidePeek = useSidePeekStore((state) => state.openSidePeek)
|
||||
@@ -22,19 +20,19 @@ export default function ToggleSidePeek({
|
||||
return (
|
||||
<Button
|
||||
onClick={() =>
|
||||
openSidePeek({ key: SidePeekEnum.roomDetails, hotelId, roomTypeCode })
|
||||
openSidePeek({
|
||||
key: SidePeekEnum.roomDetails,
|
||||
hotelId,
|
||||
roomTypeCode,
|
||||
})
|
||||
}
|
||||
theme="base"
|
||||
intent="text"
|
||||
size="small"
|
||||
theme="base"
|
||||
variant="icon"
|
||||
intent={intent}
|
||||
wrapping
|
||||
>
|
||||
{title
|
||||
? title
|
||||
: intl.formatMessage({
|
||||
defaultMessage: "See room details",
|
||||
})}
|
||||
{intl.formatMessage({ defaultMessage: "Room details" })}
|
||||
<MaterialIcon icon="chevron_right" size={14} />
|
||||
</Button>
|
||||
)
|
||||
|
||||
@@ -51,14 +51,7 @@ export default function Details({ roomTypeCode }: { roomTypeCode: string }) {
|
||||
<RoomSize roomSize={roomSize} />
|
||||
<div className={styles.toggleSidePeek}>
|
||||
{roomTypeCode && (
|
||||
<ToggleSidePeek
|
||||
hotelId={hotelId}
|
||||
roomTypeCode={roomTypeCode}
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Room details",
|
||||
})}
|
||||
intent="text"
|
||||
/>
|
||||
<ToggleSidePeek hotelId={hotelId} roomTypeCode={roomTypeCode} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,27 +4,19 @@ import { trpc } from "@/lib/trpc/client"
|
||||
import useSidePeekStore from "@/stores/sidepeek"
|
||||
|
||||
import AmenitiesSidePeek from "@/components/SidePeeks/AmenitiesSidePeek"
|
||||
import BookedRoomSidePeek from "@/components/SidePeeks/BookedRoomSidePeek"
|
||||
import HotelSidePeek from "@/components/SidePeeks/HotelSidePeek"
|
||||
import RoomSidePeek from "@/components/SidePeeks/RoomSidePeek"
|
||||
import useLang from "@/hooks/useLang"
|
||||
|
||||
export default function HotelReservationSidePeek() {
|
||||
const {
|
||||
activeSidePeek,
|
||||
confirmationNumber,
|
||||
hotelId,
|
||||
roomTypeCode,
|
||||
showCTA,
|
||||
user,
|
||||
} = useSidePeekStore((state) => ({
|
||||
activeSidePeek: state.activeSidePeek,
|
||||
confirmationNumber: state.confirmationNumber,
|
||||
hotelId: state.hotelId,
|
||||
roomTypeCode: state.roomTypeCode,
|
||||
showCTA: state.showCTA,
|
||||
user: state.user,
|
||||
}))
|
||||
const { activeSidePeek, hotelId, roomTypeCode, showCTA } = useSidePeekStore(
|
||||
(state) => ({
|
||||
activeSidePeek: state.activeSidePeek,
|
||||
hotelId: state.hotelId,
|
||||
roomTypeCode: state.roomTypeCode,
|
||||
showCTA: state.showCTA,
|
||||
})
|
||||
)
|
||||
const close = useSidePeekStore((state) => state.closeSidePeek)
|
||||
const lang = useLang()
|
||||
|
||||
@@ -72,15 +64,6 @@ export default function HotelReservationSidePeek() {
|
||||
close={close}
|
||||
/>
|
||||
)}
|
||||
{selectedRoom && (
|
||||
<BookedRoomSidePeek
|
||||
room={selectedRoom}
|
||||
activeSidePeek={activeSidePeek}
|
||||
close={close}
|
||||
user={user}
|
||||
confirmationNumber={confirmationNumber}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user