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 useLang from "@/hooks/useLang"
|
||||||
import { trackUpdatePaymentMethod } from "@/utils/tracking"
|
import { trackUpdatePaymentMethod } from "@/utils/tracking"
|
||||||
|
|
||||||
|
import { ancillaryError } from "../../../schema"
|
||||||
|
|
||||||
import styles from "./confirmationStep.module.css"
|
import styles from "./confirmationStep.module.css"
|
||||||
|
|
||||||
import type { ConfirmationStepProps } from "@/types/components/myPages/myStay/ancillaries"
|
import type { ConfirmationStepProps } from "@/types/components/myPages/myStay/ancillaries"
|
||||||
@@ -219,6 +221,12 @@ export default function ConfirmationStep({
|
|||||||
<Checkbox
|
<Checkbox
|
||||||
name="termsAndConditions"
|
name="termsAndConditions"
|
||||||
registerOptions={{ required: true }}
|
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">
|
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||||
<span>
|
<span>
|
||||||
@@ -229,6 +237,17 @@ export default function ConfirmationStep({
|
|||||||
</span>
|
</span>
|
||||||
</Typography>
|
</Typography>
|
||||||
</Checkbox>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ export default function AddAncillaryFlowModal({
|
|||||||
deliveryTime: defaultDeliveryTime,
|
deliveryTime: defaultDeliveryTime,
|
||||||
optionalText: "",
|
optionalText: "",
|
||||||
termsAndConditions: false,
|
termsAndConditions: false,
|
||||||
|
optInEmail: false,
|
||||||
paymentMethod: booking.guaranteeInfo
|
paymentMethod: booking.guaranteeInfo
|
||||||
? PaymentMethodEnum.card
|
? PaymentMethodEnum.card
|
||||||
: savedCreditCards?.length
|
: savedCreditCards?.length
|
||||||
@@ -152,6 +153,7 @@ export default function AddAncillaryFlowModal({
|
|||||||
ancillaryDeliveryTime: selectedAncillary?.requiresDeliveryTime
|
ancillaryDeliveryTime: selectedAncillary?.requiresDeliveryTime
|
||||||
? data.deliveryTime
|
? data.deliveryTime
|
||||||
: undefined,
|
: undefined,
|
||||||
|
emailOptOut: !data.optInEmail,
|
||||||
packages: packages,
|
packages: packages,
|
||||||
language: lang,
|
language: lang,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,6 +7,11 @@ const quantitySchemaWithoutRefine = z.object({
|
|||||||
quantityWithCard: z.number().nullable(),
|
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
|
export const quantitySchema = z
|
||||||
.object({})
|
.object({})
|
||||||
.merge(quantitySchemaWithoutRefine)
|
.merge(quantitySchemaWithoutRefine)
|
||||||
@@ -14,7 +19,7 @@ export const quantitySchema = z
|
|||||||
(data) =>
|
(data) =>
|
||||||
(data.quantityWithPoints ?? 0) > 0 || (data.quantityWithCard ?? 0) > 0,
|
(data.quantityWithPoints ?? 0) > 0 || (data.quantityWithCard ?? 0) > 0,
|
||||||
{
|
{
|
||||||
message: "You must select at least one quantity",
|
message: ancillaryError.MIN_QUANTITY_NOT_REACHED,
|
||||||
path: ["quantityWithCard"],
|
path: ["quantityWithCard"],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -23,7 +28,10 @@ export const ancillaryFormSchema = z
|
|||||||
.object({
|
.object({
|
||||||
deliveryTime: z.string(),
|
deliveryTime: z.string(),
|
||||||
optionalText: z.string(),
|
optionalText: z.string(),
|
||||||
termsAndConditions: z.boolean(),
|
termsAndConditions: z
|
||||||
|
.boolean()
|
||||||
|
.refine((value) => value === true, ancillaryError.TERMS_NOT_ACCEPTED),
|
||||||
|
optInEmail: z.boolean(),
|
||||||
paymentMethod: nullableStringValidator,
|
paymentMethod: nullableStringValidator,
|
||||||
})
|
})
|
||||||
.merge(quantitySchemaWithoutRefine)
|
.merge(quantitySchemaWithoutRefine)
|
||||||
@@ -31,7 +39,7 @@ export const ancillaryFormSchema = z
|
|||||||
(data) =>
|
(data) =>
|
||||||
(data.quantityWithPoints ?? 0) > 0 || (data.quantityWithCard ?? 0) > 0,
|
(data.quantityWithPoints ?? 0) > 0 || (data.quantityWithCard ?? 0) > 0,
|
||||||
{
|
{
|
||||||
message: "You must select at least one quantity",
|
message: ancillaryError.MIN_QUANTITY_NOT_REACHED,
|
||||||
path: ["quantityWithCard"],
|
path: ["quantityWithCard"],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -76,6 +76,13 @@ export default function RemoveButton({
|
|||||||
if (!data) {
|
if (!data) {
|
||||||
throw new Error()
|
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({
|
utils.booking.get.invalidate({
|
||||||
refId: refId,
|
refId: refId,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ export default function GuaranteeAncillaryHandler({
|
|||||||
ancillaryDeliveryTime: selectedAncillary.requiresDeliveryTime
|
ancillaryDeliveryTime: selectedAncillary.requiresDeliveryTime
|
||||||
? formData.deliveryTime
|
? formData.deliveryTime
|
||||||
: undefined,
|
: undefined,
|
||||||
|
emailOptOut: !formData.optInEmail,
|
||||||
packages,
|
packages,
|
||||||
language: lang,
|
language: lang,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -8,11 +8,22 @@ import { signupErrors } from "@scandic-hotels/trpc/routers/user/schemas"
|
|||||||
|
|
||||||
import { editProfileErrors } from "@/components/Forms/Edit/Profile/schema"
|
import { editProfileErrors } from "@/components/Forms/Edit/Profile/schema"
|
||||||
import { findMyBookingErrors } from "@/components/HotelReservation/FindMyBooking/schema"
|
import { findMyBookingErrors } from "@/components/HotelReservation/FindMyBooking/schema"
|
||||||
|
import { ancillaryError } from "@/components/HotelReservation/MyStay/Ancillaries/AddAncillaryFlow/schema"
|
||||||
|
|
||||||
import type { IntlShape } from "react-intl"
|
import type { IntlShape } from "react-intl"
|
||||||
|
|
||||||
export function getErrorMessage(intl: IntlShape, errorCode?: string) {
|
export function getErrorMessage(intl: IntlShape, errorCode?: string) {
|
||||||
switch (errorCode) {
|
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:
|
case findMyBookingErrors.BOOKING_NUMBER_INVALID:
|
||||||
return intl.formatMessage({
|
return intl.formatMessage({
|
||||||
id: "error.invalidBookingNumber",
|
id: "error.invalidBookingNumber",
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export const addPackageInput = z.object({
|
|||||||
comment: z.string().optional(),
|
comment: z.string().optional(),
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
|
emailOptOut: z.boolean(),
|
||||||
language: z.nativeEnum(Lang).transform((val) => langToApiLang[val]),
|
language: z.nativeEnum(Lang).transform((val) => langToApiLang[val]),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ export const bookingMutationRouter = router({
|
|||||||
headers,
|
headers,
|
||||||
body: body,
|
body: body,
|
||||||
},
|
},
|
||||||
{ language }
|
{ language, emailOptOut: input.emailOptOut }
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
|
|||||||
Reference in New Issue
Block a user