Merged in feat/SW-1737-design-mystay-multiroom (pull request #1565)

Feat/SW-1737 design mystay multiroom

* feat(SW-1737) Fixed member view of guest details

* feat(SW-1737) fix merge issues

* feat(SW-1737) Fixed price details

* feat(SW-1737) removed unused imports

* feat(SW-1737) removed true as statement

* feat(SW-1737) updated store handling

* feat(SW-1737) fixed bug showing double numbers

* feat(SW-1737) small design fixed

* feat(SW-1737) fixed rebase errors

* feat(SW-1737) fixed create booking error with dates

* feat(SW-1737) fixed view multiroom as singleroom

* feat(SW-1737) fixes for multiroom

* feat(SW-1737) fixed bookingsummary

* feat(SW-1737) dont hide modify dates

* feat(SW-1737) updated breakfast to handle number

* feat(SW-1737) Added red color if member rate

* feat(SW-1737) fix PR comments

* feat(SW-1737) updated member tiers svg

* feat(SW-1737) updated how to handle paymentMethodDescription

* feat(SW-1737) fixes after testing mystay

* feat(SW-1737) updated Room type to just use whats used

* feat(SW-1737) fixed access

* feat(SW-1737) refactor my stay after PR comments

* feat(SW-1737) fix roomNumber translation

* feat(SW-1737) removed log


Approved-by: Arvid Norlin
This commit is contained in:
Pontus Dreij
2025-03-24 09:30:10 +00:00
parent c5e294c7ea
commit 74c5b47319
117 changed files with 5899 additions and 1901 deletions

View File

@@ -0,0 +1,50 @@
import { create } from "zustand"
type ActiveView =
| "actionPanel"
| "cancelStay"
| "modifyStay"
| "guaranteeLateArrival"
interface ManageStayState {
isOpen: boolean
activeView: ActiveView
currentStep: number
isLoading: boolean
actions: {
setIsOpen: (isOpen: boolean) => void
setActiveView: (view: ActiveView) => void
setCurrentStep: (step: number) => void
setIsLoading: (isLoading: boolean) => void
handleForward: () => void
handleCloseView: () => void
handleCloseModal: () => void
}
}
export const useManageStayStore = create<ManageStayState>((set) => ({
isOpen: false,
activeView: "actionPanel",
currentStep: 1,
isLoading: false,
actions: {
setIsOpen: (isOpen) => set({ isOpen }),
setActiveView: (activeView) => set({ activeView }),
setCurrentStep: (currentStep) => set({ currentStep }),
setIsLoading: (isLoading) => set({ isLoading }),
handleForward: () =>
set((state) => ({ currentStep: state.currentStep + 1 })),
handleCloseView: () =>
set({
currentStep: 1,
isLoading: false,
activeView: "actionPanel",
}),
handleCloseModal: () =>
set({
currentStep: 1,
isOpen: false,
activeView: "actionPanel",
}),
},
}))

View File

@@ -0,0 +1,187 @@
import { create } from "zustand"
import type { BreakfastPackage } from "@/types/components/hotelReservation/breakfast"
import type { BedTypeSchema } from "@/types/components/hotelReservation/enterDetails/bedType"
import type { RoomPrice } from "@/types/components/hotelReservation/enterDetails/details"
import type { Child } from "@/types/components/hotelReservation/selectRate/selectRate"
import type { Packages } from "@/types/requests/packages"
import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation"
export type Room = Pick<
BookingConfirmation["booking"],
| "hotelId"
| "adults"
| "checkInDate"
| "checkOutDate"
| "childrenAges"
| "createDateTime"
| "rateDefinition"
| "guaranteeInfo"
| "linkedReservations"
| "confirmationNumber"
| "cancellationNumber"
| "bookingCode"
| "isModifiable"
| "isCancelable"
| "multiRoom"
| "canChangeDate"
| "guest"
| "roomTypeCode"
| "currencyCode"
| "vatPercentage"
> & {
roomName: string
roomNumber: number | null
isCancelled: boolean
childrenInRoom: Child[]
childrenAsString: string
terms: string | null
packages: Packages | null
bedType: BedTypeSchema
roomPrice: RoomPrice
breakfast: BreakfastPackage | false
mainRoom: boolean
}
interface MyStayRoomDetailsState {
bookedRoom: Room
linkedReservationRooms: Room[]
actions: {
addBookedRoom: (room: Room) => void
updateBookedRoom: (room: Room) => void
addLinkedReservationRoom: (room: Room) => void
updateLinkedReservationRoom: (room: Room) => void
}
}
export const useMyStayRoomDetailsStore = create<MyStayRoomDetailsState>(
(set) => ({
bookedRoom: {
hotelId: "",
roomTypeCode: "",
adults: 0,
childrenAges: [],
checkInDate: new Date(),
checkOutDate: new Date(),
confirmationNumber: "",
cancellationNumber: null,
bookingCode: null,
currencyCode: "",
guest: {
email: "",
firstName: "",
lastName: "",
membershipNumber: "",
phoneNumber: "",
countryCode: "",
},
rateDefinition: {
breakfastIncluded: false,
cancellationRule: null,
cancellationText: null,
generalTerms: [],
isMemberRate: false,
mustBeGuaranteed: false,
rateCode: "",
title: null,
},
reservationStatus: "",
roomPrice: {
perNight: {
requested: {
price: 0,
currency: "",
},
local: {
price: 0,
currency: "",
},
},
perStay: {
requested: {
price: 0,
currency: "",
},
local: {
price: 0,
currency: "",
},
},
},
vatPercentage: 0,
vatAmount: 0,
totalPriceExVat: 0,
createDateTime: new Date(),
canChangeDate: false,
multiRoom: false,
mainRoom: false,
roomName: "",
roomNumber: null,
isCancelled: false,
childrenInRoom: [],
childrenAsString: "",
terms: null,
packages: null,
bedType: {
description: "",
roomTypeCode: "",
},
breakfast: false,
linkedReservations: [],
isCancelable: false,
isModifiable: false,
},
linkedReservationRooms: [],
actions: {
addBookedRoom: (room) => {
set({ bookedRoom: room })
},
updateBookedRoom: (room) => {
set({ bookedRoom: room })
},
addLinkedReservationRoom: (room) => {
set((state) => {
// Check if room exists in bookedRooms
const existsInBookedRoom =
state.bookedRoom.confirmationNumber === room.confirmationNumber
if (existsInBookedRoom) {
return state
}
// Check if room with this ID already exists in linkedReservationRooms
const existingIndex = state.linkedReservationRooms.findIndex(
(r) => r.confirmationNumber === room.confirmationNumber
)
let newRooms = [...state.linkedReservationRooms]
if (existingIndex >= 0) {
// Update existing room
newRooms[existingIndex] = room
} else {
// Add new room
newRooms.push(room)
}
return {
linkedReservationRooms: newRooms,
}
})
},
updateLinkedReservationRoom: (room) => {
set((state) => {
const existingIndex = state.linkedReservationRooms.findIndex(
(r) => r.confirmationNumber === room.confirmationNumber
)
let newRooms = [...state.linkedReservationRooms]
if (existingIndex >= 0) {
newRooms[existingIndex] = room
}
return {
linkedReservationRooms: newRooms,
}
})
},
},
})
)

View File

@@ -0,0 +1,68 @@
import { create } from "zustand"
interface RoomPrice {
id: string
totalPrice: number
currencyCode: string
isMainBooking?: boolean
}
interface MyStayTotalPriceState {
rooms: RoomPrice[]
totalPrice: number | null
currencyCode: string
actions: {
// Add a single room price
addRoomPrice: (room: RoomPrice) => void
// Get the calculated total
getTotalPrice: () => number | null
}
}
export const useMyStayTotalPriceStore = create<MyStayTotalPriceState>(
(set, get) => ({
rooms: [],
totalPrice: null,
currencyCode: "",
actions: {
addRoomPrice: (room) => {
set((state) => {
// Check if room with this ID already exists
const existingIndex = state.rooms.findIndex((r) => r.id === room.id)
let newRooms = [...state.rooms]
if (existingIndex >= 0) {
// Update existing room
newRooms[existingIndex] = room
} else {
// Add new room
newRooms.push(room)
}
// Get currency from main booking or first room
const mainRoom = newRooms.find((r) => r.isMainBooking) || newRooms[0]
const currencyCode = mainRoom?.currencyCode || ""
// Calculate total (only same currency for now)
const total = newRooms.reduce((sum, r) => {
if (r.currencyCode === currencyCode) {
return sum + r.totalPrice
}
return sum
}, 0)
return {
rooms: newRooms,
totalPrice: total,
currencyCode,
}
})
},
getTotalPrice: () => {
return get().totalPrice
},
},
})
)

View File

@@ -3,22 +3,29 @@ import { create } from "zustand"
import { trackOpenSidePeekEvent } from "@/utils/tracking"
import type { SidePeekEnum } from "@/types/components/hotelReservation/sidePeek"
import type { User } from "@/types/user"
interface SidePeekState {
activeSidePeek: SidePeekEnum | null
hotelId: string | null
roomTypeCode: string | null
showCTA: boolean
user: User | null
confirmationNumber: string
openSidePeek: ({
key,
hotelId,
roomTypeCode,
showCTA,
user,
confirmationNumber,
}: {
key: SidePeekEnum | null
hotelId: string
roomTypeCode?: string
showCTA?: boolean
user?: User
confirmationNumber?: string
}) => void
closeSidePeek: () => void
}
@@ -28,12 +35,33 @@ const useSidePeekStore = create<SidePeekState>((set) => ({
hotelId: null,
roomTypeCode: null,
showCTA: true,
openSidePeek: ({ key, hotelId, roomTypeCode, showCTA }) => {
user: null,
confirmationNumber: "",
openSidePeek: ({
key,
hotelId,
roomTypeCode,
showCTA,
user,
confirmationNumber,
}) => {
trackOpenSidePeekEvent(key, hotelId, window.location.pathname, roomTypeCode)
set({ activeSidePeek: key, hotelId, roomTypeCode, showCTA })
set({
activeSidePeek: key,
hotelId,
roomTypeCode,
showCTA,
user,
confirmationNumber,
})
},
closeSidePeek: () =>
set({ activeSidePeek: null, hotelId: null, roomTypeCode: null }),
set({
activeSidePeek: null,
hotelId: null,
roomTypeCode: null,
confirmationNumber: "",
}),
}))
export default useSidePeekStore