From 20bf89d2067f29895fb392a829d2dec2cd15a3e4 Mon Sep 17 00:00:00 2001 From: Christel Westerberg Date: Thu, 6 Nov 2025 13:40:15 +0000 Subject: [PATCH] Merged in fix/STAY-72-resend-booking-confirmation (pull request #3067) feat(STAY-72): add resend confirmation button and endpoint * feat(STAY-72): add resend confirmation button and endpoint * fix: replace modify buttons with design system button Approved-by: Chuma Mcphoy (We Ahead) Approved-by: Erik Tiekstra --- .../ActionsButton/actionsButton.module.css | 3 + .../Actions/ActionsButton/index.tsx | 33 +++++++++ .../AddToCalendar/AddToCalendarButton.tsx | 26 ++----- .../Actions/AddToCalendar/button.module.css | 19 ----- .../ManageStay/Actions/CancelStay/index.tsx | 9 ++- .../ManageStay/Actions/ChangeDates/index.tsx | 10 +-- .../Actions/CustomerSupport/index.tsx | 11 ++- .../Actions/GuaranteeLateArrival/index.tsx | 9 ++- .../Actions/ResendConfirmationEmail/index.tsx | 73 +++++++++++++++++++ .../ViewAndPrintReceipt/view.module.css | 2 +- .../ManageStay/Actions/actions.module.css | 5 +- .../NotCancelled/ManageStay/Actions/index.tsx | 8 +- .../Actions/NotCancelled/ManageStay/index.tsx | 2 +- packages/trpc/lib/api/endpoints.ts | 3 + packages/trpc/lib/routers/booking/input.ts | 4 + .../lib/routers/booking/mutation/index.ts | 48 ++++++++++++ 16 files changed, 209 insertions(+), 56 deletions(-) create mode 100644 apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/ActionsButton/actionsButton.module.css create mode 100644 apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/ActionsButton/index.tsx delete mode 100644 apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/AddToCalendar/button.module.css create mode 100644 apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/ResendConfirmationEmail/index.tsx diff --git a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/ActionsButton/actionsButton.module.css b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/ActionsButton/actionsButton.module.css new file mode 100644 index 000000000..a9a2ffa20 --- /dev/null +++ b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/ActionsButton/actionsButton.module.css @@ -0,0 +1,3 @@ +.icon { + padding-right: var(--Space-x05); +} diff --git a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/ActionsButton/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/ActionsButton/index.tsx new file mode 100644 index 000000000..a55d139a3 --- /dev/null +++ b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/ActionsButton/index.tsx @@ -0,0 +1,33 @@ +"use client" + +import { Button } from "@scandic-hotels/design-system/Button" +import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon" + +import styles from "./actionsButton.module.css" + +import type { MaterialSymbolProps } from "@scandic-hotels/design-system/Icons/MaterialIcon/MaterialSymbol" + +export default function ActionsButton({ + icon, + text, + onPress, + isDisabled = false, +}: { + icon: MaterialSymbolProps["icon"] + text: string + onPress: () => void + isDisabled?: boolean +}) { + return ( + + ) +} diff --git a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/AddToCalendar/AddToCalendarButton.tsx b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/AddToCalendar/AddToCalendarButton.tsx index 1c9fabe3e..7a0cddc60 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/AddToCalendar/AddToCalendarButton.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/AddToCalendar/AddToCalendarButton.tsx @@ -1,14 +1,10 @@ "use client" -import { Button as ButtonRAC } from "react-aria-components" import { useIntl } from "react-intl" -import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon" -import { Typography } from "@scandic-hotels/design-system/Typography" - import { trackMyStayPageLink } from "@/utils/tracking" -import styles from "./button.module.css" +import ActionsButton from "../ActionsButton" export default function AddToCalendarButton({ disabled, @@ -25,20 +21,14 @@ export default function AddToCalendarButton({ } return ( - - - - - {intl.formatMessage({ - id: "common.addToCalendar", - defaultMessage: "Add to calendar", - })} - - - + /> ) } diff --git a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/AddToCalendar/button.module.css b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/AddToCalendar/button.module.css deleted file mode 100644 index 3715e6b97..000000000 --- a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/AddToCalendar/button.module.css +++ /dev/null @@ -1,19 +0,0 @@ -.button { - align-items: center; - background: none; - border: none; - cursor: pointer; - display: flex; - gap: var(--Space-x1); - padding: var(--Space-x1) 0; - width: 100%; - - &:disabled { - color: var(--Scandic-Grey-40); - } -} - -.text { - color: var(--Text-Interactive-Default); - text-align: left; -} diff --git a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/CancelStay/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/CancelStay/index.tsx index 7aee1ccd5..9681501ed 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/CancelStay/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/CancelStay/index.tsx @@ -5,6 +5,7 @@ import { useIntl } from "react-intl" import Modal from "@/components/HotelReservation/MyStay/Modal" import { trackMyStayPageLink } from "@/utils/tracking" +import ActionsButton from "../ActionsButton" import Alerts from "./Alerts" import Steps from "./Steps" @@ -19,12 +20,14 @@ export default function CancelStay() { return ( - - {intl.formatMessage({ + + /> {({ close }) => ( diff --git a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/ChangeDates/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/ChangeDates/index.tsx index 0beab1680..59e7e3e15 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/ChangeDates/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/ChangeDates/index.tsx @@ -7,6 +7,7 @@ import { useMyStayStore } from "@/stores/my-stay" import Modal from "@/components/HotelReservation/MyStay/Modal" import { trackMyStayPageLink } from "@/utils/tracking" +import ActionsButton from "../ActionsButton" import { dateHasPassed } from "../utils" import Alerts from "./Alerts" import Steps from "./Steps" @@ -40,13 +41,12 @@ export default function ChangeDates() { }) return ( - - {text} - + onPress={trackChangeDates} + text={text} + /> {({ close }) => ( diff --git a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/CustomerSupport/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/CustomerSupport/index.tsx index c7bf652bf..1b53c15f3 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/CustomerSupport/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/CustomerSupport/index.tsx @@ -2,10 +2,11 @@ import { DialogTrigger } from "react-aria-components" import { useIntl } from "react-intl" -import Modal from "@/components/HotelReservation/MyStay/Modal" import CustomerSupportModal from "@/components/HotelReservation/MyStay/ReferenceCard/Actions/CustomerSupportModal" import { trackMyStayPageLink } from "@/utils/tracking" +import ActionsButton from "../ActionsButton" + export default function CustomerSupport() { const intl = useIntl() @@ -15,12 +16,14 @@ export default function CustomerSupport() { return ( - - {intl.formatMessage({ + + /> ) diff --git a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/GuaranteeLateArrival/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/GuaranteeLateArrival/index.tsx index 990e96565..5cce3d3f4 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/GuaranteeLateArrival/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/GuaranteeLateArrival/index.tsx @@ -9,6 +9,7 @@ import { useMyStayStore } from "@/stores/my-stay" import Modal from "@/components/HotelReservation/MyStay/Modal" import { trackMyStayPageLink } from "@/utils/tracking" +import ActionsButton from "../ActionsButton" import { dateHasPassed } from "../utils" import Form from "./Form" @@ -54,9 +55,11 @@ export default function GuaranteeLateArrival() { return ( - - {text} - + {({ close }) => ( diff --git a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/ResendConfirmationEmail/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/ResendConfirmationEmail/index.tsx new file mode 100644 index 000000000..4d6daf14e --- /dev/null +++ b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/ResendConfirmationEmail/index.tsx @@ -0,0 +1,73 @@ +"use client" +import { useIntl } from "react-intl" + +import { logger } from "@scandic-hotels/common/logger" +import { toast } from "@scandic-hotels/design-system/Toast" +import { trpc } from "@scandic-hotels/trpc/client" + +import { useMyStayStore } from "@/stores/my-stay" + +import useLang from "@/hooks/useLang" +import { trackMyStayPageLink } from "@/utils/tracking" + +import ActionsButton from "../ActionsButton" + +type ResendConfirmationEmailProps = { + onClose: () => void +} + +export default function ResendConfirmationEmail({ + onClose, +}: ResendConfirmationEmailProps) { + const intl = useIntl() + const lang = useLang() + + const refId = useMyStayStore((state) => state.refId) + + const resendEmail = trpc.booking.resendConfirmation.useMutation() + + function resendConfirmationEmail() { + trackMyStayPageLink("resend confirmation email") + resendEmail.mutate( + { language: lang, refId }, + { + onSuccess() { + onClose() + toast.success( + intl.formatMessage({ + id: "myStay.manageStay.resendConfirmationEmail.success", + defaultMessage: "Confirmation email was resent successfully", + }) + ) + }, + onError(e) { + onClose() + toast.error( + intl.formatMessage({ + id: "myStay.manageStay.resendConfirmationEmail.error", + defaultMessage: + "There was an error resending the confirmation email", + }) + ) + logger.error("[myStay] Resend confirmation email failed", { + error: e.data, + }) + }, + } + ) + } + + const printMsg = intl.formatMessage({ + id: "myStay.manageStay.resendConfirmationEmail", + defaultMessage: "Resend confirmation email", + }) + + return ( + + ) +} diff --git a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/ViewAndPrintReceipt/view.module.css b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/ViewAndPrintReceipt/view.module.css index bee715a3b..95cc11637 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/ViewAndPrintReceipt/view.module.css +++ b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/ViewAndPrintReceipt/view.module.css @@ -3,5 +3,5 @@ color: var(--Text-Interactive-Default); display: flex; gap: var(--Space-x1); - padding: var(--Space-x1) 0; + padding: var(--Space-x05) 0; } diff --git a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/actions.module.css b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/actions.module.css index 2cf353a24..d66a6408e 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/actions.module.css +++ b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/actions.module.css @@ -1,5 +1,8 @@ .list { - list-style: none; + display: flex; + flex-direction: column; + align-items: start; margin: 0; padding: 0; + gap: var(--Space-x15); } diff --git a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/index.tsx index 0600fd4c7..7408ec0a9 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/index.tsx @@ -3,16 +3,22 @@ import CancelStay from "./CancelStay" import ChangeDates from "./ChangeDates" import CustomerSupport from "./CustomerSupport" import GuaranteeLateArrival from "./GuaranteeLateArrival" +import ResendConfirmationEmail from "./ResendConfirmationEmail" import ViewAndPrintReceipt from "./ViewAndPrintReceipt" import styles from "./actions.module.css" -export default function Actions() { +type ActionsProps = { + onClose: () => void +} + +export default function Actions({ onClose }: ActionsProps) { return (
+ diff --git a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/index.tsx index 92cb635a1..7116165d9 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/index.tsx @@ -54,7 +54,7 @@ export default function ManageStay() {
- +
diff --git a/packages/trpc/lib/api/endpoints.ts b/packages/trpc/lib/api/endpoints.ts index 00aaf8caa..b8a9e680e 100644 --- a/packages/trpc/lib/api/endpoints.ts +++ b/packages/trpc/lib/api/endpoints.ts @@ -78,6 +78,9 @@ export namespace endpoints { export function guarantee(confirmationNumber: string) { return `${bookings}/${confirmationNumber}/guarantee` } + export function confirmNotification(confirmationNumber: string) { + return `${bookings}/${confirmationNumber}/confirmNotification` + } export const enum Stays { future = `${base.path.booking}/${version}/${base.enitity.Stays}/future`, diff --git a/packages/trpc/lib/routers/booking/input.ts b/packages/trpc/lib/routers/booking/input.ts index 7cca42f3e..95906c893 100644 --- a/packages/trpc/lib/routers/booking/input.ts +++ b/packages/trpc/lib/routers/booking/input.ts @@ -22,6 +22,10 @@ export const removePackageInput = z.object({ language: z.nativeEnum(Lang).transform((val) => langToApiLang[val]), }) +export const resendConfirmationInput = z.object({ + language: z.nativeEnum(Lang).transform((val) => langToApiLang[val]), +}) + export const cancelBookingsInput = z.object({ language: z.nativeEnum(Lang), }) diff --git a/packages/trpc/lib/routers/booking/mutation/index.ts b/packages/trpc/lib/routers/booking/mutation/index.ts index 2878db24f..f49899e69 100644 --- a/packages/trpc/lib/routers/booking/mutation/index.ts +++ b/packages/trpc/lib/routers/booking/mutation/index.ts @@ -10,6 +10,7 @@ import { cancelBookingsInput, guaranteeBookingInput, removePackageInput, + resendConfirmationInput, updateBookingInput, } from "../input" import { bookingConfirmationSchema } from "../output" @@ -318,6 +319,53 @@ export const bookingMutationRouter = router({ metricsRemovePackage.success() + return true + }), + resendConfirmation: safeProtectedServiceProcedure + .input(resendConfirmationInput) + .concat(refIdPlugin.toConfirmationNumber) + .use(async ({ ctx, next }) => { + const token = await ctx.getScandicUserToken() + + return next({ + ctx: { + token, + }, + }) + }) + .mutation(async function ({ ctx, input }) { + const { confirmationNumber } = ctx + + const resendConfirmationCounter = createCounter( + "trpc.booking", + "confirmation.resend" + ) + const metricsResendConfirmation = resendConfirmationCounter.init({ + confirmationNumber, + }) + + metricsResendConfirmation.start() + + const token = ctx.token ?? ctx.serviceToken + const headers = { + Authorization: `Bearer ${token}`, + } + + const apiResponse = await api.post( + api.endpoints.v1.Booking.confirmNotification(confirmationNumber), + { + headers, + }, + { language: input.language } + ) + + if (!apiResponse.ok) { + await metricsResendConfirmation.httpError(apiResponse) + return false + } + + metricsResendConfirmation.success() + return true }), })