Merged in chore/cleanup-booking-flow (pull request #2824)
chore: Cleanup booking-flow after migration * Remove unused types * Clean up exports, types, unused files etc in booking-flow Approved-by: Joakim Jäderberg
This commit is contained in:
@@ -5,7 +5,17 @@ import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import styles from "./promo.module.css"
|
||||
|
||||
import type { PromoProps } from "@scandic-hotels/booking-flow/types/components/promo/promoProps"
|
||||
type PromoProps = {
|
||||
buttonText: string
|
||||
href: string
|
||||
text: string
|
||||
title: string
|
||||
image?: {
|
||||
src: string
|
||||
altText: string
|
||||
altText_En: string
|
||||
}
|
||||
}
|
||||
|
||||
export default function Promo({
|
||||
buttonText,
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import { createContext } from "react"
|
||||
|
||||
import type { RatesStore } from "@/types/contexts/rates"
|
||||
|
||||
export const RatesContext = createContext<RatesStore | null>(null)
|
||||
@@ -1,62 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import { usePathname, useSearchParams } from "next/navigation"
|
||||
import { useMemo } from "react"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { createRatesStore } from "@/stores/select-rate"
|
||||
|
||||
import { RatesContext } from "@/contexts/Rates"
|
||||
|
||||
import type { RatesProviderProps } from "@/types/providers/rates"
|
||||
|
||||
export default function RatesProvider({
|
||||
booking,
|
||||
children,
|
||||
hotelType,
|
||||
roomCategories,
|
||||
roomsAvailability,
|
||||
vat,
|
||||
}: RatesProviderProps) {
|
||||
const pathname = usePathname()
|
||||
const searchParams = useSearchParams()
|
||||
const intl = useIntl()
|
||||
|
||||
const modifyRateIndex = searchParams.has("activeRoomIndex")
|
||||
? Number(searchParams.get("activeRoomIndex"))
|
||||
: undefined
|
||||
|
||||
const store = useMemo(() => {
|
||||
return createRatesStore({
|
||||
booking,
|
||||
hotelType,
|
||||
labels: {
|
||||
accessibilityRoom: intl.formatMessage({
|
||||
defaultMessage: "Accessible room",
|
||||
}),
|
||||
allergyRoom: intl.formatMessage({
|
||||
defaultMessage: "Allergy-friendly room",
|
||||
}),
|
||||
petRoom: intl.formatMessage({
|
||||
defaultMessage: "Pet-friendly room",
|
||||
}),
|
||||
},
|
||||
pathname,
|
||||
roomCategories,
|
||||
roomsAvailability,
|
||||
vat,
|
||||
initialActiveRoom: modifyRateIndex,
|
||||
})
|
||||
}, [
|
||||
booking,
|
||||
hotelType,
|
||||
intl,
|
||||
pathname,
|
||||
roomCategories,
|
||||
roomsAvailability,
|
||||
modifyRateIndex,
|
||||
vat,
|
||||
])
|
||||
|
||||
return <RatesContext.Provider value={store}>{children}</RatesContext.Provider>
|
||||
}
|
||||
@@ -1,537 +0,0 @@
|
||||
import { produce } from "immer"
|
||||
import { useContext } from "react"
|
||||
import { create, useStore } from "zustand"
|
||||
|
||||
import { BookingCodeFilterEnum } from "@scandic-hotels/booking-flow/stores/bookingCode-filter"
|
||||
import { serializeBookingSearchParams } from "@scandic-hotels/booking-flow/utils/url"
|
||||
import { SEARCH_TYPE_REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
|
||||
import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
|
||||
import { AvailabilityEnum } from "@scandic-hotels/trpc/enums/selectHotel"
|
||||
|
||||
import { RatesContext } from "@/contexts/Rates"
|
||||
|
||||
import {
|
||||
findDefaultCurrency,
|
||||
findProductInRoom,
|
||||
findSelectedRate,
|
||||
} from "./helpers"
|
||||
|
||||
import type {
|
||||
InitialState,
|
||||
RatesState,
|
||||
} from "@scandic-hotels/booking-flow/types/stores/rates"
|
||||
import type { Package, Packages } from "@scandic-hotels/trpc/types/packages"
|
||||
import type { PriceProduct } from "@scandic-hotels/trpc/types/roomAvailability"
|
||||
|
||||
export function createRatesStore({
|
||||
booking,
|
||||
hotelType,
|
||||
labels,
|
||||
pathname,
|
||||
roomCategories,
|
||||
roomsAvailability,
|
||||
initialActiveRoom,
|
||||
vat,
|
||||
}: InitialState) {
|
||||
function updateUrl(booking: RatesState["booking"], activeRoom: number = -1) {
|
||||
const searchParams = serializeBookingSearchParams(booking, {
|
||||
initialSearchParams: new URLSearchParams(window.location.search),
|
||||
})
|
||||
if (activeRoom >= 0) {
|
||||
searchParams.set("modifyRateIndex", activeRoom.toString())
|
||||
} else {
|
||||
searchParams.delete("modifyRateIndex")
|
||||
}
|
||||
window.history.replaceState({}, "", `${pathname}?${searchParams}`)
|
||||
}
|
||||
|
||||
const packageOptions = [
|
||||
{
|
||||
code: RoomPackageCodeEnum.ACCESSIBILITY_ROOM,
|
||||
description: labels.accessibilityRoom,
|
||||
},
|
||||
{
|
||||
code: RoomPackageCodeEnum.ALLERGY_ROOM,
|
||||
description: labels.allergyRoom,
|
||||
},
|
||||
{
|
||||
code: RoomPackageCodeEnum.PET_ROOM,
|
||||
description: labels.petRoom,
|
||||
},
|
||||
]
|
||||
|
||||
const roomsPackages: NonNullable<Packages>[] = []
|
||||
const roomConfigurations: RatesState["roomConfigurations"] = []
|
||||
if (roomsAvailability) {
|
||||
for (const availability of roomsAvailability) {
|
||||
if ("error" in availability) {
|
||||
// Availability request failed, default to empty array
|
||||
roomConfigurations.push([])
|
||||
roomsPackages.push([])
|
||||
} else {
|
||||
roomConfigurations.push(availability.roomConfigurations)
|
||||
roomsPackages.push(availability.packages)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const rateSummary: RatesState["rateSummary"] = []
|
||||
for (const [idx, room] of booking.rooms.entries()) {
|
||||
if (room.rateCode && room.roomTypeCode) {
|
||||
const roomConfiguration = roomConfigurations?.[idx]
|
||||
|
||||
const selectedRoom = findSelectedRate(
|
||||
room.rateCode,
|
||||
room.counterRateCode,
|
||||
room.roomTypeCode,
|
||||
roomConfiguration
|
||||
)
|
||||
|
||||
if (!selectedRoom) {
|
||||
booking.rooms[idx] = roomWithoutSelection(room)
|
||||
updateUrl(booking)
|
||||
continue
|
||||
}
|
||||
|
||||
const product = findProductInRoom(
|
||||
room.rateCode,
|
||||
selectedRoom,
|
||||
room.counterRateCode
|
||||
)
|
||||
if (product) {
|
||||
const roomPackages = roomsPackages[idx].filter((pkg) =>
|
||||
room.packages?.includes(pkg.code)
|
||||
)
|
||||
|
||||
rateSummary[idx] = {
|
||||
features: selectedRoom.features,
|
||||
product,
|
||||
packages: roomPackages,
|
||||
rate: product.rate,
|
||||
roomType: selectedRoom.roomType,
|
||||
roomTypeCode: selectedRoom.roomTypeCode,
|
||||
}
|
||||
} else {
|
||||
rateSummary[idx] = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let activeRoom = rateSummary.length
|
||||
if (initialActiveRoom !== undefined && initialActiveRoom >= 0) {
|
||||
activeRoom = initialActiveRoom
|
||||
} else if (rateSummary.length === booking.rooms.length) {
|
||||
// Finds the first unselected room and sets that to active
|
||||
// if no unselected rooms it will return -1 and close all rooms
|
||||
const unselectedRoomIndex = rateSummary.findIndex((rate) => !rate)
|
||||
activeRoom = unselectedRoomIndex
|
||||
}
|
||||
|
||||
const isRedemptionBooking = booking.searchType === SEARCH_TYPE_REDEMPTION
|
||||
|
||||
const defaultCurrency = findDefaultCurrency(roomsAvailability)
|
||||
|
||||
return create<RatesState>()((set) => {
|
||||
return {
|
||||
activeRoom,
|
||||
booking,
|
||||
packageOptions,
|
||||
hotelType,
|
||||
isRedemptionBooking,
|
||||
rateSummary,
|
||||
roomConfigurations,
|
||||
roomCategories,
|
||||
roomsPackages,
|
||||
roomsAvailability,
|
||||
vat,
|
||||
defaultCurrency,
|
||||
rooms: booking.rooms.map((room, idx) => {
|
||||
const roomConfiguration = roomConfigurations[idx]
|
||||
const roomPackages = roomsPackages[idx]
|
||||
const selectedPackages =
|
||||
room.packages
|
||||
?.map((code) => roomPackages.find((pkg) => pkg.code === code))
|
||||
.filter((pkg): pkg is Package => Boolean(pkg)) ?? []
|
||||
|
||||
const selectedRate =
|
||||
findSelectedRate(
|
||||
room.rateCode,
|
||||
room.counterRateCode,
|
||||
room.roomTypeCode,
|
||||
roomConfiguration
|
||||
) ?? null
|
||||
|
||||
let product = null
|
||||
if (selectedRate) {
|
||||
product = findProductInRoom(
|
||||
room.rateCode,
|
||||
selectedRate,
|
||||
room.counterRateCode
|
||||
)
|
||||
}
|
||||
const bookingCode = room.rateCode
|
||||
? room.bookingCode
|
||||
: booking.bookingCode
|
||||
const selectedFilter =
|
||||
bookingCode && !isRedemptionBooking
|
||||
? BookingCodeFilterEnum.Discounted
|
||||
: BookingCodeFilterEnum.All
|
||||
|
||||
return {
|
||||
actions: {
|
||||
appendRegularRates(roomConfigurations) {
|
||||
return set(
|
||||
produce((state: RatesState) => {
|
||||
state.rooms[idx].isFetchingAdditionalRate = false
|
||||
if (roomConfigurations) {
|
||||
const rooms = state.rooms[idx].rooms
|
||||
const updatedRooms = rooms.map((currentRoom) => {
|
||||
const incomingRoom = roomConfigurations.find(
|
||||
(room) =>
|
||||
room.roomType === currentRoom.roomType &&
|
||||
room.roomTypeCode === currentRoom.roomTypeCode
|
||||
)
|
||||
|
||||
if (incomingRoom) {
|
||||
let campaign = currentRoom.campaign
|
||||
if (incomingRoom.campaign.length) {
|
||||
const newCampaign = [
|
||||
...campaign,
|
||||
...incomingRoom.campaign,
|
||||
].reduce((cpns, cpn) => {
|
||||
if (cpns.has(cpn.rateDefinition.rateCode)) {
|
||||
return cpns
|
||||
}
|
||||
cpns.set(cpn.rateDefinition.rateCode, cpn)
|
||||
return cpns
|
||||
}, new Map<string, PriceProduct>())
|
||||
campaign = Array.from(newCampaign.values())
|
||||
}
|
||||
|
||||
const currentRoomAvailable =
|
||||
currentRoom.status === AvailabilityEnum.Available
|
||||
const incomingRoomAvailable =
|
||||
incomingRoom.status === AvailabilityEnum.Available
|
||||
let status = AvailabilityEnum.NotAvailable
|
||||
if (currentRoomAvailable || incomingRoomAvailable) {
|
||||
status = AvailabilityEnum.Available
|
||||
}
|
||||
|
||||
return {
|
||||
...currentRoom,
|
||||
campaign,
|
||||
products: [
|
||||
...currentRoom.products,
|
||||
...incomingRoom.products,
|
||||
],
|
||||
regular: incomingRoom.regular,
|
||||
status,
|
||||
}
|
||||
}
|
||||
|
||||
return currentRoom
|
||||
})
|
||||
|
||||
state.rooms[idx].rooms = updatedRooms
|
||||
} else {
|
||||
state.rooms[idx].rooms = []
|
||||
}
|
||||
})
|
||||
)
|
||||
},
|
||||
closeSection() {
|
||||
return set(
|
||||
produce((state: RatesState) => {
|
||||
if (state.rateSummary.length === state.booking.rooms.length) {
|
||||
state.activeRoom = -1
|
||||
} else {
|
||||
state.activeRoom = idx + 1
|
||||
}
|
||||
})
|
||||
)
|
||||
},
|
||||
modifyRate() {
|
||||
return set(
|
||||
produce((state: RatesState) => {
|
||||
state.activeRoom = idx
|
||||
})
|
||||
)
|
||||
},
|
||||
removeSelectedPackage(code) {
|
||||
return set(
|
||||
produce((state: RatesState) => {
|
||||
state.rooms[idx].isFetchingPackages = true
|
||||
const filteredSelectedPackages = state.rooms[
|
||||
idx
|
||||
].selectedPackages.filter((c) => c.code !== code)
|
||||
state.rooms[idx].selectedPackages = filteredSelectedPackages
|
||||
|
||||
if (
|
||||
state.rooms[idx].selectedRate?.product.bookingCode ||
|
||||
state.booking.bookingCode
|
||||
) {
|
||||
state.rooms[idx].selectedFilter =
|
||||
BookingCodeFilterEnum.Discounted
|
||||
}
|
||||
|
||||
if (filteredSelectedPackages.length) {
|
||||
state.booking.rooms[idx].packages =
|
||||
filteredSelectedPackages.map((pkg) => pkg.code)
|
||||
} else {
|
||||
state.booking.rooms[idx].packages = null
|
||||
}
|
||||
|
||||
updateUrl(state.booking, state.activeRoom)
|
||||
})
|
||||
)
|
||||
},
|
||||
removeSelectedPackages() {
|
||||
return set(
|
||||
produce((state: RatesState) => {
|
||||
state.rooms[idx].isFetchingPackages = true
|
||||
state.rooms[idx].selectedPackages = []
|
||||
|
||||
if (
|
||||
state.rooms[idx].selectedRate?.product.bookingCode ||
|
||||
state.booking.bookingCode
|
||||
) {
|
||||
state.rooms[idx].selectedFilter =
|
||||
BookingCodeFilterEnum.Discounted
|
||||
}
|
||||
|
||||
state.booking.rooms[idx].packages = null
|
||||
updateUrl(state.booking, state.activeRoom)
|
||||
})
|
||||
)
|
||||
},
|
||||
selectFilter(filter) {
|
||||
return set(
|
||||
produce((state: RatesState) => {
|
||||
state.rooms[idx].selectedFilter = filter
|
||||
state.rooms[idx].isFetchingAdditionalRate =
|
||||
filter === BookingCodeFilterEnum.All
|
||||
})
|
||||
)
|
||||
},
|
||||
selectRate(selectedRate, isUserLoggedIn) {
|
||||
return set(
|
||||
produce((state: RatesState) => {
|
||||
if (!selectedRate.product) {
|
||||
return
|
||||
}
|
||||
|
||||
state.rooms[idx].selectedRate = selectedRate
|
||||
state.rateSummary[idx] = {
|
||||
features: selectedRate.features,
|
||||
packages: state.rooms[idx].selectedPackages,
|
||||
product: selectedRate.product,
|
||||
rate: selectedRate.product.rate,
|
||||
roomType: selectedRate.roomType,
|
||||
roomTypeCode: selectedRate.roomTypeCode,
|
||||
}
|
||||
|
||||
const roomNr = idx + 1
|
||||
const isMainRoom = roomNr === 1
|
||||
|
||||
let productRateCode = ""
|
||||
if ("corporateCheque" in selectedRate.product) {
|
||||
productRateCode =
|
||||
selectedRate.product.corporateCheque.rateCode
|
||||
}
|
||||
|
||||
if ("redemption" in selectedRate.product) {
|
||||
productRateCode = selectedRate.product.redemption.rateCode
|
||||
}
|
||||
|
||||
if ("voucher" in selectedRate.product) {
|
||||
productRateCode = selectedRate.product.voucher.rateCode
|
||||
}
|
||||
|
||||
if (
|
||||
"public" in selectedRate.product &&
|
||||
selectedRate.product.public
|
||||
) {
|
||||
productRateCode = selectedRate.product.public.rateCode
|
||||
}
|
||||
|
||||
let hasMemberRate = false
|
||||
let memberRateCode = ""
|
||||
if (
|
||||
"member" in selectedRate.product &&
|
||||
selectedRate.product.member
|
||||
) {
|
||||
hasMemberRate = true
|
||||
memberRateCode = selectedRate.product.member.rateCode
|
||||
}
|
||||
|
||||
const isMemberRate =
|
||||
isUserLoggedIn && isMainRoom && hasMemberRate
|
||||
|
||||
state.rooms[idx].bookingRoom.rateCode = isMemberRate
|
||||
? memberRateCode
|
||||
: productRateCode
|
||||
if (!isMemberRate && hasMemberRate) {
|
||||
state.rooms[idx].bookingRoom.counterRateCode =
|
||||
memberRateCode
|
||||
}
|
||||
state.rooms[idx].bookingRoom.roomTypeCode =
|
||||
selectedRate.roomTypeCode
|
||||
state.rooms[idx].bookingRoom.bookingCode =
|
||||
selectedRate.product.bookingCode
|
||||
|
||||
const counterratecode = isMemberRate
|
||||
? productRateCode
|
||||
: memberRateCode
|
||||
if (counterratecode) {
|
||||
state.booking.rooms[idx].counterRateCode = counterratecode
|
||||
} else {
|
||||
state.booking.rooms[idx].counterRateCode = null
|
||||
}
|
||||
|
||||
const rateCode = isMemberRate
|
||||
? memberRateCode
|
||||
: productRateCode
|
||||
if (rateCode) {
|
||||
state.booking.rooms[idx].rateCode = rateCode
|
||||
}
|
||||
|
||||
if (selectedRate.product.bookingCode) {
|
||||
state.booking.rooms[idx].bookingCode =
|
||||
selectedRate.product.bookingCode
|
||||
} else {
|
||||
if (state.booking.rooms[idx].bookingCode) {
|
||||
state.booking.rooms[idx].bookingCode = null
|
||||
}
|
||||
}
|
||||
|
||||
state.booking.rooms[idx].roomTypeCode =
|
||||
selectedRate.roomTypeCode
|
||||
|
||||
if (state.rateSummary.length === state.booking.rooms.length) {
|
||||
state.activeRoom = -1
|
||||
} else {
|
||||
state.activeRoom = idx + 1
|
||||
}
|
||||
|
||||
updateUrl(state.booking)
|
||||
})
|
||||
)
|
||||
},
|
||||
selectPackages(selectedPackages) {
|
||||
return set(
|
||||
produce((state: RatesState) => {
|
||||
state.rooms[idx].isFetchingPackages = true
|
||||
const pkgs = state.roomsPackages[idx].filter((pkg) =>
|
||||
selectedPackages.includes(pkg.code)
|
||||
)
|
||||
state.rooms[idx].selectedPackages = pkgs
|
||||
|
||||
if (
|
||||
state.rooms[idx].selectedRate?.product.bookingCode ||
|
||||
state.booking.bookingCode
|
||||
) {
|
||||
state.rooms[idx].selectedFilter =
|
||||
BookingCodeFilterEnum.Discounted
|
||||
}
|
||||
|
||||
if (selectedPackages.length) {
|
||||
state.booking.rooms[idx].packages = selectedPackages
|
||||
} else {
|
||||
state.booking.rooms[idx].packages = null
|
||||
}
|
||||
|
||||
updateUrl(state.booking, state.activeRoom)
|
||||
})
|
||||
)
|
||||
},
|
||||
updateRooms(rooms) {
|
||||
return set(
|
||||
produce((state: RatesState) => {
|
||||
state.rooms[idx].isFetchingPackages = false
|
||||
if (rooms) {
|
||||
state.rooms[idx].rooms = rooms
|
||||
const rateSummaryRoom = state.rateSummary[idx]
|
||||
if (rateSummaryRoom) {
|
||||
const room = state.rooms[idx].bookingRoom
|
||||
const selectedRoom = findSelectedRate(
|
||||
room.rateCode,
|
||||
room.counterRateCode,
|
||||
room.roomTypeCode,
|
||||
rooms
|
||||
)
|
||||
|
||||
if (selectedRoom) {
|
||||
rateSummaryRoom.packages =
|
||||
state.rooms[idx].selectedPackages
|
||||
} else {
|
||||
state.booking.rooms[idx] = roomWithoutSelection(
|
||||
state.booking.rooms[idx]
|
||||
)
|
||||
|
||||
state.rateSummary[idx] = null
|
||||
state.rooms[idx].selectedRate = null
|
||||
|
||||
updateUrl(state.booking)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
state.rooms[idx].rooms = []
|
||||
if (state.rateSummary[idx]) {
|
||||
state.booking.rooms[idx] = roomWithoutSelection(
|
||||
state.booking.rooms[idx]
|
||||
)
|
||||
|
||||
state.rateSummary[idx] = null
|
||||
state.rooms[idx].selectedRate = null
|
||||
|
||||
updateUrl(state.booking)
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
},
|
||||
},
|
||||
|
||||
bookingRoom: room,
|
||||
isFetchingAdditionalRate: false,
|
||||
isFetchingPackages: false,
|
||||
rooms: roomConfiguration,
|
||||
selectedFilter,
|
||||
selectedPackages,
|
||||
selectedRate:
|
||||
selectedRate && product
|
||||
? {
|
||||
features: selectedRate.features,
|
||||
packages: selectedPackages,
|
||||
product,
|
||||
roomType: selectedRate.roomType,
|
||||
roomTypeCode: selectedRate.roomTypeCode,
|
||||
}
|
||||
: null,
|
||||
}
|
||||
}),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function useRatesStore<T>(selector: (store: RatesState) => T) {
|
||||
const store = useContext(RatesContext)
|
||||
|
||||
if (!store) {
|
||||
throw new Error("useRatesStore must be used within RatesProvider")
|
||||
}
|
||||
|
||||
return useStore(store, selector)
|
||||
}
|
||||
|
||||
function roomWithoutSelection(
|
||||
room: RatesState["booking"]["rooms"][number]
|
||||
): RatesState["booking"]["rooms"][number] {
|
||||
return {
|
||||
...room,
|
||||
rateCode: null,
|
||||
counterRateCode: null,
|
||||
roomTypeCode: null,
|
||||
bookingCode: null,
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,3 @@
|
||||
import type { BookingSearchType } from "@scandic-hotels/booking-flow/searchType"
|
||||
import type { Child } from "@scandic-hotels/trpc/types/child"
|
||||
import type { PackageEnum } from "@scandic-hotels/trpc/types/packages"
|
||||
import type { Product } from "@scandic-hotels/trpc/types/roomAvailability"
|
||||
|
||||
import type { Price } from "../price"
|
||||
@@ -11,21 +8,3 @@ export interface RoomPrice {
|
||||
}
|
||||
|
||||
export type RoomRate = Product
|
||||
|
||||
export type DetailsBooking = {
|
||||
hotelId: string
|
||||
fromDate: string
|
||||
toDate: string
|
||||
city?: string
|
||||
bookingCode?: string
|
||||
searchType?: BookingSearchType
|
||||
rooms: {
|
||||
adults: number
|
||||
rateCode: string
|
||||
roomTypeCode: string
|
||||
bookingCode?: string
|
||||
childrenInRoom?: Child[]
|
||||
counterRateCode?: string
|
||||
packages?: PackageEnum[]
|
||||
}[]
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
import type { createRatesStore } from "@/stores/select-rate"
|
||||
|
||||
export type RatesStore = ReturnType<typeof createRatesStore>
|
||||
@@ -1,7 +0,0 @@
|
||||
import type { Lang } from "@scandic-hotels/common/constants/language"
|
||||
import type { BreakfastPackages } from "@scandic-hotels/trpc/routers/hotels/output"
|
||||
import type { RoomCategories } from "@scandic-hotels/trpc/types/hotel"
|
||||
import type { Room } from "@scandic-hotels/trpc/types/room"
|
||||
|
||||
import type { SafeUser } from "@/types/user"
|
||||
import type { DetailsBooking } from "../components/hotelReservation/enterDetails/details"
|
||||
Reference in New Issue
Block a user