Merged in fix/STAY-73-opt-in-email (pull request #3028)
Fix/STAY-73 opt in email * fix: let user opt-in for modification email when adding ancillaries * fix: add toast when successfully removing an ancillary Approved-by: Erik Tiekstra Approved-by: Elin Svedin
This commit is contained in:
@@ -20,6 +20,8 @@ import { useAddAncillaryStore } from "@/stores/my-stay/add-ancillary-flow"
|
||||
import useLang from "@/hooks/useLang"
|
||||
import { trackUpdatePaymentMethod } from "@/utils/tracking"
|
||||
|
||||
import { ancillaryError } from "../../../schema"
|
||||
|
||||
import styles from "./confirmationStep.module.css"
|
||||
|
||||
import type { ConfirmationStepProps } from "@/types/components/myPages/myStay/ancillaries"
|
||||
@@ -219,6 +221,12 @@ export default function ConfirmationStep({
|
||||
<Checkbox
|
||||
name="termsAndConditions"
|
||||
registerOptions={{ required: true }}
|
||||
errorCodeMessages={{
|
||||
[ancillaryError.TERMS_NOT_ACCEPTED]: intl.formatMessage({
|
||||
id: "common.mustAcceptTermsError",
|
||||
defaultMessage: "You must accept the terms and conditions",
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||
<span>
|
||||
@@ -229,6 +237,17 @@ export default function ConfirmationStep({
|
||||
</span>
|
||||
</Typography>
|
||||
</Checkbox>
|
||||
<Checkbox name="emailOptOut">
|
||||
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||
<span>
|
||||
{intl.formatMessage({
|
||||
id: "addAncillary.confirmationStep.optInForEmail",
|
||||
defaultMessage:
|
||||
"I want to receive an updated booking confirmation email reflecting this add-on",
|
||||
})}
|
||||
</span>
|
||||
</Typography>
|
||||
</Checkbox>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -97,6 +97,7 @@ export default function AddAncillaryFlowModal({
|
||||
deliveryTime: defaultDeliveryTime,
|
||||
optionalText: "",
|
||||
termsAndConditions: false,
|
||||
optInEmail: false,
|
||||
paymentMethod: booking.guaranteeInfo
|
||||
? PaymentMethodEnum.card
|
||||
: savedCreditCards?.length
|
||||
@@ -152,6 +153,7 @@ export default function AddAncillaryFlowModal({
|
||||
ancillaryDeliveryTime: selectedAncillary?.requiresDeliveryTime
|
||||
? data.deliveryTime
|
||||
: undefined,
|
||||
emailOptOut: !data.optInEmail,
|
||||
packages: packages,
|
||||
language: lang,
|
||||
},
|
||||
|
||||
@@ -7,6 +7,11 @@ const quantitySchemaWithoutRefine = z.object({
|
||||
quantityWithCard: z.number().nullable(),
|
||||
})
|
||||
|
||||
export const ancillaryError = {
|
||||
TERMS_NOT_ACCEPTED: "TERMS_NOT_ACCEPTED",
|
||||
MIN_QUANTITY_NOT_REACHED: "MIN_QUANTITY_NOT_REACHED",
|
||||
} as const
|
||||
|
||||
export const quantitySchema = z
|
||||
.object({})
|
||||
.merge(quantitySchemaWithoutRefine)
|
||||
@@ -14,7 +19,7 @@ export const quantitySchema = z
|
||||
(data) =>
|
||||
(data.quantityWithPoints ?? 0) > 0 || (data.quantityWithCard ?? 0) > 0,
|
||||
{
|
||||
message: "You must select at least one quantity",
|
||||
message: ancillaryError.MIN_QUANTITY_NOT_REACHED,
|
||||
path: ["quantityWithCard"],
|
||||
}
|
||||
)
|
||||
@@ -23,7 +28,10 @@ export const ancillaryFormSchema = z
|
||||
.object({
|
||||
deliveryTime: z.string(),
|
||||
optionalText: z.string(),
|
||||
termsAndConditions: z.boolean(),
|
||||
termsAndConditions: z
|
||||
.boolean()
|
||||
.refine((value) => value === true, ancillaryError.TERMS_NOT_ACCEPTED),
|
||||
optInEmail: z.boolean(),
|
||||
paymentMethod: nullableStringValidator,
|
||||
})
|
||||
.merge(quantitySchemaWithoutRefine)
|
||||
@@ -31,7 +39,7 @@ export const ancillaryFormSchema = z
|
||||
(data) =>
|
||||
(data.quantityWithPoints ?? 0) > 0 || (data.quantityWithCard ?? 0) > 0,
|
||||
{
|
||||
message: "You must select at least one quantity",
|
||||
message: ancillaryError.MIN_QUANTITY_NOT_REACHED,
|
||||
path: ["quantityWithCard"],
|
||||
}
|
||||
)
|
||||
|
||||
@@ -76,6 +76,13 @@ export default function RemoveButton({
|
||||
if (!data) {
|
||||
throw new Error()
|
||||
}
|
||||
toast.success(
|
||||
intl.formatMessage({
|
||||
id: "myStay.removeAncillary.success",
|
||||
defaultMessage:
|
||||
"The product has now been removed from your booking.",
|
||||
})
|
||||
)
|
||||
utils.booking.get.invalidate({
|
||||
refId: refId,
|
||||
})
|
||||
|
||||
@@ -56,6 +56,7 @@ export default function GuaranteeAncillaryHandler({
|
||||
ancillaryDeliveryTime: selectedAncillary.requiresDeliveryTime
|
||||
? formData.deliveryTime
|
||||
: undefined,
|
||||
emailOptOut: !formData.optInEmail,
|
||||
packages,
|
||||
language: lang,
|
||||
},
|
||||
|
||||
@@ -8,11 +8,22 @@ import { signupErrors } from "@scandic-hotels/trpc/routers/user/schemas"
|
||||
|
||||
import { editProfileErrors } from "@/components/Forms/Edit/Profile/schema"
|
||||
import { findMyBookingErrors } from "@/components/HotelReservation/FindMyBooking/schema"
|
||||
import { ancillaryError } from "@/components/HotelReservation/MyStay/Ancillaries/AddAncillaryFlow/schema"
|
||||
|
||||
import type { IntlShape } from "react-intl"
|
||||
|
||||
export function getErrorMessage(intl: IntlShape, errorCode?: string) {
|
||||
switch (errorCode) {
|
||||
case ancillaryError.MIN_QUANTITY_NOT_REACHED:
|
||||
return intl.formatMessage({
|
||||
id: "addAncillary.selectQuantityStep.minQuantityNotReached",
|
||||
defaultMessage: "You must select at least one quantity",
|
||||
})
|
||||
case ancillaryError.TERMS_NOT_ACCEPTED:
|
||||
return intl.formatMessage({
|
||||
id: "addAncillary.confirmationStep.termsAndConditionsNotice",
|
||||
defaultMessage: "You must accept the terms and conditions to proceed",
|
||||
})
|
||||
case findMyBookingErrors.BOOKING_NUMBER_INVALID:
|
||||
return intl.formatMessage({
|
||||
id: "error.invalidBookingNumber",
|
||||
|
||||
@@ -14,6 +14,7 @@ export const addPackageInput = z.object({
|
||||
comment: z.string().optional(),
|
||||
})
|
||||
),
|
||||
emailOptOut: z.boolean(),
|
||||
language: z.nativeEnum(Lang).transform((val) => langToApiLang[val]),
|
||||
})
|
||||
|
||||
|
||||
@@ -145,7 +145,7 @@ export const bookingMutationRouter = router({
|
||||
headers,
|
||||
body: body,
|
||||
},
|
||||
{ language }
|
||||
{ language, emailOptOut: input.emailOptOut }
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
|
||||
Reference in New Issue
Block a user