Merged in feat/SW-598 (pull request #1554)

feat: pass specialRequest.comment to create booking

* feat: pass specialRequest.comment to create booking


Approved-by: Simon.Emanuelsson
This commit is contained in:
Linus Flood
2025-03-18 10:43:28 +00:00
committed by Arvid Norlin
parent fc219aaec0
commit 0e0b065dd9
14 changed files with 89 additions and 51 deletions

View File

@@ -5,6 +5,7 @@ import { useIntl } from "react-intl"
import { useEnterDetailsStore } from "@/stores/enter-details"
import SpecialRequests from "@/components/HotelReservation/EnterDetails/Details/SpecialRequests"
import Button from "@/components/TempDesignSystem/Button"
import CountrySelect from "@/components/TempDesignSystem/Form/Country"
import Input from "@/components/TempDesignSystem/Form/Input"
@@ -53,6 +54,9 @@ export default function Details() {
lastName: initialData.lastName,
membershipNo: initialData.membershipNo,
phoneNumber: initialData.phoneNumber,
specialRequest: {
comment: room.specialRequest.comment,
},
},
})
@@ -114,6 +118,7 @@ export default function Details() {
type="tel"
/>
)}
<SpecialRequests />
</div>
<footer className={styles.footer}>
<Button

View File

@@ -1,5 +1,6 @@
import { z } from "zod"
import { specialRequestSchema } from "@/components/HotelReservation/EnterDetails/Details/SpecialRequests/schema"
import { phoneValidator } from "@/utils/zod/phoneValidator"
// stringMatcher regex is copied from current web as specified by requirements.
@@ -40,4 +41,5 @@ export const multiroomDetailsSchema = z.object({
}
return true
}, "Invalid membership number format"),
specialRequest: specialRequestSchema,
})

View File

@@ -6,6 +6,7 @@ import { useIntl } from "react-intl"
import { useEnterDetailsStore } from "@/stores/enter-details"
import SpecialRequests from "@/components/HotelReservation/EnterDetails/Details/SpecialRequests"
import Button from "@/components/TempDesignSystem/Button"
import CountrySelect from "@/components/TempDesignSystem/Form/Country"
import Input from "@/components/TempDesignSystem/Form/Input"
@@ -17,7 +18,6 @@ import JoinScandicFriendsCard from "./JoinScandicFriendsCard"
import MemberPriceModal from "./MemberPriceModal"
import { guestDetailsSchema, signedInDetailsSchema } from "./schema"
import Signup from "./Signup"
import SpecialRequests from "./SpecialRequests"
import styles from "./details.module.css"
@@ -64,10 +64,9 @@ export default function Details({ user }: DetailsProps) {
membershipNo: initialData.membershipNo,
phoneNumber: user?.phoneNumber ?? initialData.phoneNumber,
zipCode: "zipCode" in initialData ? initialData.zipCode : undefined,
specialRequests:
"specialRequests" in initialData
? initialData.specialRequests
: undefined,
specialRequest: {
comment: room.specialRequest.comment,
},
},
})

View File

@@ -2,6 +2,7 @@ import { z } from "zod"
import { dt } from "@/lib/dt"
import { specialRequestSchema } from "@/components/HotelReservation/EnterDetails/Details/SpecialRequests/schema"
import { phoneValidator } from "@/utils/zod/phoneValidator"
// stringMatcher regex is copied from current web as specified by requirements.
@@ -10,30 +11,6 @@ const stringMatcher =
const isValidString = (key: string) => stringMatcher.test(key)
export enum FloorPreference {
LOW = "Low floor",
HIGH = "High floor",
}
export enum ElevatorPreference {
AWAY_FROM_ELEVATOR = "Away from elevator",
NEAR_ELEVATOR = "Near elevator",
}
const specialRequestsSchema = z
.object({
floorPreference: z
.nativeEnum(FloorPreference)
.or(z.literal("").transform((_) => undefined))
.optional(),
elevatorPreference: z
.nativeEnum(ElevatorPreference)
.or(z.literal("").transform((_) => undefined))
.optional(),
comments: z.string().default(""),
})
.optional()
export const baseDetailsSchema = z.object({
countryCode: z.string().min(1, { message: "Country is required" }),
email: z.string().email({ message: "Email address is required" }),
@@ -50,7 +27,7 @@ export const baseDetailsSchema = z.object({
message: "Last name can't contain any special characters",
}),
phoneNumber: phoneValidator(),
specialRequests: specialRequestsSchema,
specialRequest: specialRequestSchema,
})
export const notJoinDetailsSchema = baseDetailsSchema.merge(
@@ -116,5 +93,5 @@ export const signedInDetailsSchema = z.object({
.transform((_) => false),
dateOfBirth: z.string().default(""),
zipCode: z.string().default(""),
specialRequests: specialRequestsSchema,
specialRequest: specialRequestSchema,
})

View File

@@ -39,7 +39,7 @@ export default function SpecialRequests() {
<Select
label={intl.formatMessage({ id: "Floor preference" })}
name="specialRequests.floorPreference"
name="specialRequest.floorPreference"
items={[
noPreferenceItem,
{
@@ -54,7 +54,7 @@ export default function SpecialRequests() {
/>
<Select
label={intl.formatMessage({ id: "Elevator preference" })}
name="specialRequests.elevatorPreference"
name="specialRequest.elevatorPreference"
items={[
noPreferenceItem,
{
@@ -75,7 +75,7 @@ export default function SpecialRequests() {
label={intl.formatMessage({
id: "Is there anything else you would like us to know before your arrival?",
})}
name="specialRequests.comments"
name="specialRequest.comment"
/>
</div>
</div>

View File

@@ -0,0 +1,25 @@
import { z } from "zod"
export enum FloorPreference {
LOW = "Low floor",
HIGH = "High floor",
}
export enum ElevatorPreference {
AWAY_FROM_ELEVATOR = "Away from elevator",
NEAR_ELEVATOR = "Near elevator",
}
export const specialRequestSchema = z
.object({
floorPreference: z
.nativeEnum(FloorPreference)
.or(z.literal("").transform((_) => undefined))
.optional(),
elevatorPreference: z
.nativeEnum(ElevatorPreference)
.or(z.literal("").transform((_) => undefined))
.optional(),
comment: z.string().default(""),
})
.optional()

View File

@@ -255,24 +255,24 @@ export default function PaymentClient({
const guarantee = data.guarantee
const useSavedCard = savedCreditCard
? {
card: {
alias: savedCreditCard.alias,
expiryDate: savedCreditCard.expirationDate,
cardType: savedCreditCard.cardType,
},
}
card: {
alias: savedCreditCard.alias,
expiryDate: savedCreditCard.expirationDate,
cardType: savedCreditCard.cardType,
},
}
: {}
const shouldUsePayment = !isFlexRate || guarantee
const payment = shouldUsePayment
? {
paymentMethod: paymentMethod,
...useSavedCard,
success: `${paymentRedirectUrl}/success`,
error: `${paymentRedirectUrl}/error`,
cancel: `${paymentRedirectUrl}/cancel`,
}
paymentMethod: paymentMethod,
...useSavedCard,
success: `${paymentRedirectUrl}/success`,
error: `${paymentRedirectUrl}/error`,
cancel: `${paymentRedirectUrl}/cancel`,
}
: undefined
trackPaymentEvent({
@@ -334,7 +334,7 @@ export default function PaymentClient({
},
rateCode:
(room.guest.join || room.guest.membershipNo) &&
booking.rooms[idx].counterRateCode
booking.rooms[idx].counterRateCode
? booking.rooms[idx].counterRateCode
: booking.rooms[idx].rateCode,
roomPrice: {
@@ -343,6 +343,11 @@ export default function PaymentClient({
},
roomTypeCode: room.bedType!.roomTypeCode, // A selection has been made in order to get to this step.
smsConfirmationRequested: data.smsConfirmation,
specialRequest: {
comment: room.specialRequest.comment
? room.specialRequest.comment
: undefined,
},
})),
})
},
@@ -445,7 +450,7 @@ export default function PaymentClient({
value={paymentMethod}
label={
PAYMENT_METHOD_TITLES[
paymentMethod as PaymentMethodEnum
paymentMethod as PaymentMethodEnum
]
}
/>

View File

@@ -67,6 +67,9 @@ const rooms: RoomState[] = [
isAvailable: true,
mustBeGuaranteed: false,
isFlexRate: false,
specialRequest: {
comment: "",
},
},
steps: {
[StepEnum.selectBed]: {
@@ -107,6 +110,9 @@ const rooms: RoomState[] = [
isAvailable: true,
mustBeGuaranteed: false,
isFlexRate: false,
specialRequest: {
comment: "",
},
},
steps: {
[StepEnum.selectBed]: {

View File

@@ -1,4 +1,5 @@
"use client"
import {
Label as AriaLabel,
Text,
@@ -47,12 +48,11 @@ export default function TextArea({
validationBehavior="aria"
value={field.value}
>
<AriaLabel className={styles.container} htmlFor={name}>
<AriaLabel className={styles.container}>
<Body asChild fontOnly>
<AriaTextArea
{...field}
aria-labelledby={field.name}
id={name}
placeholder={placeholder}
readOnly={readOnly}
required={!!registerOptions.required}

View File

@@ -126,7 +126,13 @@ export default function EnterDetailsProvider({
storedRoom.room.guest
)
}
if (
!currentRoom.room.specialRequest.comment &&
storedRoom.room.specialRequest.comment
) {
currentRoom.room.specialRequest.comment =
storedRoom.room.specialRequest.comment
}
const validGuest =
idx > 0
? multiroomDetailsSchema.safeParse(currentRoom.room.guest)

View File

@@ -29,6 +29,9 @@ const roomsSchema = z
phoneNumber: z.string(),
}),
smsConfirmationRequested: z.boolean(),
specialRequest: z.object({
comment: z.string().optional(),
}),
packages: z.object({
breakfast: z.boolean(),
allergyFriendly: z.boolean(),

View File

@@ -111,6 +111,9 @@ export function createDetailsStore(
? deepmerge(defaultGuestState, extractGuestFromUser(user))
: defaultGuestState,
roomPrice: getRoomPrice(room.roomRate, isMember && idx === 0),
specialRequest: {
comment: "",
},
},
currentStep,
@@ -377,6 +380,10 @@ export function createDetailsStore(
currentRoom.guest.join = data.join
currentRoom.guest.lastName = data.lastName
if (data.specialRequest?.comment) {
currentRoom.specialRequest.comment = data.specialRequest.comment
}
if (data.join) {
currentRoom.guest.membershipNo = undefined
} else {

View File

@@ -51,6 +51,9 @@ export interface Room extends InitialRoomData {
childrenInRoom: Child[] | undefined
guest: DetailsSchema | MultiroomDetailsSchema | SignedInDetailsSchema
roomPrice: RoomPrice
specialRequest: {
comment: string
}
}
export interface RoomState {