diff --git a/apps/partner-sas/app/[lang]/layout.tsx b/apps/partner-sas/app/[lang]/layout.tsx index 90d51e1c7..888a27ff8 100644 --- a/apps/partner-sas/app/[lang]/layout.tsx +++ b/apps/partner-sas/app/[lang]/layout.tsx @@ -61,6 +61,7 @@ export default async function RootLayout(props: RootLayoutProps) { const bookingFlowConfig: BookingFlowConfig = { bookingCodeEnabled: false, + enterDetailsMembershipIdInputLocation: "join-card", variant: "partner-sas", routes: { myStay: routeToScandicWeb(myStay), diff --git a/apps/scandic-web/constants/bookingFlowConfig.ts b/apps/scandic-web/constants/bookingFlowConfig.ts index 87db81d09..05c081347 100644 --- a/apps/scandic-web/constants/bookingFlowConfig.ts +++ b/apps/scandic-web/constants/bookingFlowConfig.ts @@ -7,6 +7,7 @@ import type { BookingFlowConfig } from "@scandic-hotels/booking-flow/BookingFlow export const bookingFlowConfig: BookingFlowConfig = { bookingCodeEnabled: true, + enterDetailsMembershipIdInputLocation: "form", variant: "scandic", routes: { myStay, diff --git a/packages/booking-flow/lib/bookingFlowConfig/bookingFlowConfig.tsx b/packages/booking-flow/lib/bookingFlowConfig/bookingFlowConfig.tsx index 05f94abb6..9df8097cf 100644 --- a/packages/booking-flow/lib/bookingFlowConfig/bookingFlowConfig.tsx +++ b/packages/booking-flow/lib/bookingFlowConfig/bookingFlowConfig.tsx @@ -10,6 +10,7 @@ import type { BookingFlowVariant } from "./bookingFlowVariants" export type BookingFlowConfig = { bookingCodeEnabled: boolean + enterDetailsMembershipIdInputLocation: "form" | "join-card" variant: BookingFlowVariant routes: { myStay: LangRoute diff --git a/packages/booking-flow/lib/components/EnterDetails/Details/Multiroom/PartnerSASJoinScandicFriendsCard/index.tsx b/packages/booking-flow/lib/components/EnterDetails/Details/Multiroom/PartnerSASJoinScandicFriendsCard/index.tsx new file mode 100644 index 000000000..e906e2d19 --- /dev/null +++ b/packages/booking-flow/lib/components/EnterDetails/Details/Multiroom/PartnerSASJoinScandicFriendsCard/index.tsx @@ -0,0 +1,127 @@ +"use client" +import { useWatch } from "react-hook-form" +import { useIntl } from "react-intl" + +import { CurrencyEnum } from "@scandic-hotels/common/constants/currency" +import { membershipTermsAndConditions } from "@scandic-hotels/common/constants/routes/membershipTermsAndConditions" +import { formatPrice } from "@scandic-hotels/common/utils/numberFormatting" +import Footnote from "@scandic-hotels/design-system/Footnote" +import Checkbox from "@scandic-hotels/design-system/Form/Checkbox" +import Link from "@scandic-hotels/design-system/Link" +import { Typography } from "@scandic-hotels/design-system/Typography" +import { trpc } from "@scandic-hotels/trpc/client" + +import { useRoomContext } from "../../../../../contexts/EnterDetails/RoomContext" +import useLang from "../../../../../hooks/useLang" +import { MembershipNumberInput } from "../../RoomOne/Signup/MembershipNumberInput" + +import styles from "./partnerSASJoinScandicFriendsCard.module.css" + +type Props = { + name?: string + updateDetailsStore?: () => void +} +export function PartnerSASJoinScandicFriendsCard({ + name = "join", + updateDetailsStore, +}: Props) { + const lang = useLang() + const intl = useIntl() + const { data: euroBonusProfile } = + trpc.partner.sas.getEuroBonusProfile.useQuery() + + const { + room, + roomNr, + actions: { updateJoin }, + } = useRoomContext() + + const joinValue = useWatch({ name: "join" }) + + function onChange(event: { target: { value: boolean } }) { + updateJoin(event.target.value) + } + + if (euroBonusProfile && euroBonusProfile.linkStatus !== "UNLINKED") { + return null + } + + if (!("member" in room.roomRate) || !room.roomRate.member) { + return null + } + + return ( +
+ +

+ + {intl.formatMessage({ + defaultMessage: "Get the member room price", + })} + {/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */} + {`: `} + + + {intl.formatMessage( + { + defaultMessage: "{amount} for room {roomNr}", + }, + { + amount: formatPrice( + intl, + room.roomRate.member.localPrice.pricePerStay ?? 0, + room.roomRate.member.localPrice.currency ?? + CurrencyEnum.Unknown + ), + roomNr, + } + )} + +

+
+
+ + +
+ {intl.formatMessage({ + defaultMessage: "Join Scandic Friends before check-in", + })} +
+
+
+ + +
+ +
+ + {intl.formatMessage( + { + defaultMessage: + "By joining you accept the Terms and Conditions. The Scandic Friends Membership is valid until further notice, but can at any time be terminated by contacting Scandic Customer Service.", + }, + { + termsAndConditionsLink: (str) => ( + + {str} + + ), + } + )} + +
+
+ ) +} diff --git a/packages/booking-flow/lib/components/EnterDetails/Details/Multiroom/PartnerSASJoinScandicFriendsCard/partnerSASJoinScandicFriendsCard.module.css b/packages/booking-flow/lib/components/EnterDetails/Details/Multiroom/PartnerSASJoinScandicFriendsCard/partnerSASJoinScandicFriendsCard.module.css new file mode 100644 index 000000000..b5dbbd3b1 --- /dev/null +++ b/packages/booking-flow/lib/components/EnterDetails/Details/Multiroom/PartnerSASJoinScandicFriendsCard/partnerSASJoinScandicFriendsCard.module.css @@ -0,0 +1,51 @@ +.cardContainer { + align-self: flex-start; + background-color: var(--Surface-Primary-Hover-Accent); + border-radius: var(--Corner-radius-lg); + display: grid; + gap: var(--Space-x2); + padding: var(--Space-x2); + grid-template-areas: + "price" + "checkbox" + "terms"; + width: min(100%, 696px); +} + +.priceContainer { + grid-area: price; + margin-bottom: var(--Space-x1); +} + +.price { + color: var(--Text-Accent-Primary); +} + +.checkBox { + align-self: center; + grid-area: checkbox; + display: flex; + flex-direction: column; + gap: var(--Space-x2); +} + +.terms { + grid-area: terms; +} + +@media screen and (min-width: 768px) { + .cardContainer { + grid-template-columns: 1fr 1fr; + grid-template-rows: auto auto; + gap: var(--Space-x3); + grid-template-areas: + "price checkbox" + "terms terms"; + } + + .priceContainer { + margin-bottom: 0; + display: flex; + flex-direction: column; + } +} diff --git a/packages/booking-flow/lib/components/EnterDetails/Details/Multiroom/index.tsx b/packages/booking-flow/lib/components/EnterDetails/Details/Multiroom/index.tsx index e9e42f942..90184c1e9 100644 --- a/packages/booking-flow/lib/components/EnterDetails/Details/Multiroom/index.tsx +++ b/packages/booking-flow/lib/components/EnterDetails/Details/Multiroom/index.tsx @@ -22,6 +22,7 @@ import { getErrorMessage } from "../../../BookingFlowInput/errors" import MemberPriceModal from "../MemberPriceModal" import { SpecialRequests } from "../SpecialRequests" import JoinScandicFriendsCard from "./JoinScandicFriendsCard" +import { PartnerSASJoinScandicFriendsCard } from "./PartnerSASJoinScandicFriendsCard" import { getMultiroomDetailsSchema } from "./schema" import styles from "./details.module.css" @@ -161,6 +162,10 @@ export default function Details() { const guestIsGoingToJoin = methods.watch("join") const guestIsMember = methods.watch("membershipNo") + const showMembershipIdInput = + config.enterDetailsMembershipIdInputLocation === "form" && + !guestIsGoingToJoin + return (
- {guestIsMember ? null : } + {guestIsMember ? null : ( + + )}
- {guestIsGoingToJoin ? null : ( + {showMembershipIdInput ? ( - )} + ) : null}
@@ -263,3 +270,26 @@ export default function Details() { ) } + +function JoinScandicCard({ + updateDetailsStore, +}: { + updateDetailsStore: () => void +}) { + const config = useBookingFlowConfig() + + switch (config.enterDetailsMembershipIdInputLocation) { + case "form": + return + case "join-card": + return ( + + ) + default: + const _exhaustiveCheck: never = + config.enterDetailsMembershipIdInputLocation + return null + } +} diff --git a/packages/booking-flow/lib/components/EnterDetails/Details/RoomOne/PartnerSASJoinScandicFriendsCard/index.tsx b/packages/booking-flow/lib/components/EnterDetails/Details/RoomOne/PartnerSASJoinScandicFriendsCard/index.tsx index 793b2b007..7adec7779 100644 --- a/packages/booking-flow/lib/components/EnterDetails/Details/RoomOne/PartnerSASJoinScandicFriendsCard/index.tsx +++ b/packages/booking-flow/lib/components/EnterDetails/Details/RoomOne/PartnerSASJoinScandicFriendsCard/index.tsx @@ -1,26 +1,30 @@ "use client" +import { useWatch } from "react-hook-form" import { useIntl } from "react-intl" import { CurrencyEnum } from "@scandic-hotels/common/constants/currency" import { membershipTermsAndConditions } from "@scandic-hotels/common/constants/routes/membershipTermsAndConditions" import { formatPrice } from "@scandic-hotels/common/utils/numberFormatting" -import { Button } from "@scandic-hotels/design-system/Button" import Footnote from "@scandic-hotels/design-system/Footnote" import Checkbox from "@scandic-hotels/design-system/Form/Checkbox" import Link from "@scandic-hotels/design-system/Link" -import { toast } from "@scandic-hotels/design-system/Toast" import { Typography } from "@scandic-hotels/design-system/Typography" import { trpc } from "@scandic-hotels/trpc/client" import { useRoomContext } from "../../../../../contexts/EnterDetails/RoomContext" import useLang from "../../../../../hooks/useLang" +import { MembershipNumberInput } from "../Signup/MembershipNumberInput" import styles from "./partnerSASJoinScandicFriendsCard.module.css" type Props = { name?: string + updateDetailsStore?: () => void } -export function PartnerSASJoinScandicFriendsCard({ name = "join" }: Props) { +export function PartnerSASJoinScandicFriendsCard({ + name = "join", + updateDetailsStore, +}: Props) { const lang = useLang() const intl = useIntl() const { data: euroBonusProfile } = @@ -31,11 +35,13 @@ export function PartnerSASJoinScandicFriendsCard({ name = "join" }: Props) { actions: { updateJoin }, } = useRoomContext() + const joinValue = useWatch({ name: "join" }) + function onChange(event: { target: { value: boolean } }) { updateJoin(event.target.value) } - if (!euroBonusProfile || euroBonusProfile.linkStatus !== "UNLINKED") { + if (euroBonusProfile && euroBonusProfile.linkStatus !== "UNLINKED") { return null } @@ -48,14 +54,9 @@ export function PartnerSASJoinScandicFriendsCard({ name = "join" }: Props) {

- {/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */} - <> - NOT IMPLEMENTED -
- {intl.formatMessage({ - defaultMessage: "Get the Scandic Friends room price", - })} - + {intl.formatMessage({ + defaultMessage: "Get the member room price", + })} {/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */} {`: `}
@@ -68,32 +69,26 @@ export function PartnerSASJoinScandicFriendsCard({ name = "join" }: Props) {

- - -
- {intl.formatMessage({ - defaultMessage: "Join Scandic Friends now", - })} -
-
-
+
+ + +
+ {intl.formatMessage({ + defaultMessage: "Join Scandic Friends now", + })} +
+
+
- + +
diff --git a/packages/booking-flow/lib/components/EnterDetails/Details/RoomOne/PartnerSASJoinScandicFriendsCard/partnerSASJoinScandicFriendsCard.module.css b/packages/booking-flow/lib/components/EnterDetails/Details/RoomOne/PartnerSASJoinScandicFriendsCard/partnerSASJoinScandicFriendsCard.module.css index cddbc0d1a..b5dbbd3b1 100644 --- a/packages/booking-flow/lib/components/EnterDetails/Details/RoomOne/PartnerSASJoinScandicFriendsCard/partnerSASJoinScandicFriendsCard.module.css +++ b/packages/booking-flow/lib/components/EnterDetails/Details/RoomOne/PartnerSASJoinScandicFriendsCard/partnerSASJoinScandicFriendsCard.module.css @@ -1,15 +1,14 @@ .cardContainer { align-self: flex-start; - background-color: orchid; - color: white; + background-color: var(--Surface-Primary-Hover-Accent); border-radius: var(--Corner-radius-lg); display: grid; gap: var(--Space-x2); padding: var(--Space-x2); grid-template-areas: - "price login" - "checkbox checkbox" - "terms terms"; + "price" + "checkbox" + "terms"; width: min(100%, 696px); } @@ -22,15 +21,12 @@ color: var(--Text-Accent-Primary); } -.login { - grid-area: login; - align-self: center; - justify-self: end; -} - .checkBox { align-self: center; grid-area: checkbox; + display: flex; + flex-direction: column; + gap: var(--Space-x2); } .terms { @@ -39,12 +35,12 @@ @media screen and (min-width: 768px) { .cardContainer { - grid-template-columns: 1fr auto auto; + grid-template-columns: 1fr 1fr; grid-template-rows: auto auto; gap: var(--Space-x3); grid-template-areas: - "price checkbox login" - "terms terms terms"; + "price checkbox" + "terms terms"; } .priceContainer { diff --git a/packages/booking-flow/lib/components/EnterDetails/Details/RoomOne/Signup/MembershipNumberInput.tsx b/packages/booking-flow/lib/components/EnterDetails/Details/RoomOne/Signup/MembershipNumberInput.tsx new file mode 100644 index 000000000..696b66388 --- /dev/null +++ b/packages/booking-flow/lib/components/EnterDetails/Details/RoomOne/Signup/MembershipNumberInput.tsx @@ -0,0 +1,35 @@ +import { useIntl } from "react-intl" + +import BookingFlowInput from "../../../../BookingFlowInput" + +import type { RegisterOptions } from "react-hook-form" + +export function MembershipNumberInput({ + registerOptions, + label, + className, + disabled, +}: { + registerOptions?: RegisterOptions + label?: string + className?: string + disabled?: boolean +}) { + const intl = useIntl() + + return ( + + ) +} diff --git a/packages/booking-flow/lib/components/EnterDetails/Details/RoomOne/Signup/index.tsx b/packages/booking-flow/lib/components/EnterDetails/Details/RoomOne/Signup/index.tsx index 7796645f4..e910f3f6c 100644 --- a/packages/booking-flow/lib/components/EnterDetails/Details/RoomOne/Signup/index.tsx +++ b/packages/booking-flow/lib/components/EnterDetails/Details/RoomOne/Signup/index.tsx @@ -15,6 +15,7 @@ import { useBookingFlowConfig } from "../../../../../bookingFlowConfig/bookingFl import useLang from "../../../../../hooks/useLang" import BookingFlowInput from "../../../../BookingFlowInput" import { getErrorMessage } from "../../../../BookingFlowInput/errors" +import { MembershipNumberInput } from "./MembershipNumberInput" import styles from "./signup.module.css" @@ -41,50 +42,46 @@ export default function Signup({ setIsJoinChecked(joinValue) }, [joinValue]) - return isJoinChecked ? ( -
- -
-
- - - {intl.formatMessage({ - defaultMessage: "Birth date", - })} - - -
- + +
+
+ + + {intl.formatMessage({ + defaultMessage: "Birth date", + })} + + +
+ +
-
- ) : ( - - ) + ) + + if (config.enterDetailsMembershipIdInputLocation === "join-card") return null + + return } diff --git a/packages/booking-flow/lib/components/EnterDetails/Details/RoomOne/index.tsx b/packages/booking-flow/lib/components/EnterDetails/Details/RoomOne/index.tsx index 161dbdb3b..c5301a955 100644 --- a/packages/booking-flow/lib/components/EnterDetails/Details/RoomOne/index.tsx +++ b/packages/booking-flow/lib/components/EnterDetails/Details/RoomOne/index.tsx @@ -149,6 +149,9 @@ export default function Details({ user }: DetailsProps) { setValue("phoneNumberCC", countryCode.toLowerCase()) } }, [countryCode, setValue]) + + const showJoinCard = memberRate && !user + return ( - {config.variant === "scandic" && ( - <>{user || !memberRate ? null : } - )} - - {config.variant === "partner-sas" && ( - <> - {user || !memberRate ? null : } - - )} + {showJoinCard ? ( + + ) : null}
) } + +function JoinScandicCard({ + updateDetailsStore, +}: { + updateDetailsStore: () => void +}) { + const config = useBookingFlowConfig() + + switch (config.enterDetailsMembershipIdInputLocation) { + case "form": + return + case "join-card": + return ( + + ) + default: + const _exhaustiveCheck: never = + config.enterDetailsMembershipIdInputLocation + return null + } +}