fix/SW-2247 enter details fixes

* fix(SW-2247): Removed accordion like special requests block and changed button variant

* fix(SW-2247): Added additional info for bedtype selection

Approved-by: Tobias Johansson
Approved-by: Niclas Edenvin
This commit is contained in:
Erik Tiekstra
2025-04-16 09:49:53 +00:00
parent 700141f45e
commit 7108537cb8
19 changed files with 154 additions and 208 deletions

View File

@@ -1,43 +0,0 @@
import { useIntl } from "react-intl"
import { useEnterDetailsStore } from "@/stores/enter-details"
import Body from "@/components/TempDesignSystem/Text/Body"
import { ChildBedMapEnum } from "@/types/components/bookingWidget/enums"
import type { BedTypeInfoProps } from "@/types/components/hotelReservation/enterDetails/bedType"
export default function BedTypeInfo({ hasMultipleBedTypes }: BedTypeInfoProps) {
const intl = useIntl()
const hasChildWithExtraBed = useEnterDetailsStore((state) =>
state.booking.rooms[0].childrenInRoom?.some(
(child) => Number(child.bed) === ChildBedMapEnum.IN_EXTRA_BED
)
)
const availabilityText = intl.formatMessage({
defaultMessage:
"Your selected bed type will be provided based on availability",
})
const extraBedText = intl.formatMessage({
defaultMessage: "Extra bed will be provided additionally",
})
const combinedStr = `${availabilityText}. ${extraBedText}`
if (hasMultipleBedTypes && hasChildWithExtraBed) {
return <Body>{combinedStr}</Body>
}
if (hasMultipleBedTypes) {
return <Body>{availabilityText}</Body>
}
if (hasChildWithExtraBed) {
return <Body>{extraBedText}</Body>
}
return null
}

View File

@@ -13,7 +13,6 @@ import {
import RadioCard from "@/components/TempDesignSystem/Form/RadioCard" import RadioCard from "@/components/TempDesignSystem/Form/RadioCard"
import { useRoomContext } from "@/contexts/Details/Room" import { useRoomContext } from "@/contexts/Details/Room"
import BedTypeInfo from "./BedTypeInfo"
import { bedTypeFormSchema } from "./schema" import { bedTypeFormSchema } from "./schema"
import styles from "./bedOptions.module.css" import styles from "./bedOptions.module.css"
@@ -64,7 +63,6 @@ export default function BedType() {
return ( return (
<FormProvider {...methods}> <FormProvider {...methods}>
<div className={styles.container}> <div className={styles.container}>
<BedTypeInfo hasMultipleBedTypes={bedTypes.length > 1} />
<form className={styles.form} onSubmit={methods.handleSubmit(onSubmit)}> <form className={styles.form} onSubmit={methods.handleSubmit(onSubmit)}>
{bedTypes.map((roomType) => { {bedTypes.map((roomType) => {
const width = const width =

View File

@@ -3,10 +3,11 @@ import { zodResolver } from "@hookform/resolvers/zod"
import { FormProvider, useForm } from "react-hook-form" import { FormProvider, useForm } from "react-hook-form"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { Button } from "@scandic-hotels/design-system/Button"
import { useEnterDetailsStore } from "@/stores/enter-details" import { useEnterDetailsStore } from "@/stores/enter-details"
import SpecialRequests from "@/components/HotelReservation/EnterDetails/Details/SpecialRequests" import SpecialRequests from "@/components/HotelReservation/EnterDetails/Details/SpecialRequests"
import Button from "@/components/TempDesignSystem/Button"
import CountrySelect from "@/components/TempDesignSystem/Form/Country" import CountrySelect from "@/components/TempDesignSystem/Form/Country"
import Input from "@/components/TempDesignSystem/Form/Input" import Input from "@/components/TempDesignSystem/Form/Input"
import Phone from "@/components/TempDesignSystem/Form/Phone" import Phone from "@/components/TempDesignSystem/Form/Phone"
@@ -133,15 +134,15 @@ export default function Details() {
</div> </div>
<footer className={styles.footer}> <footer className={styles.footer}>
<Button <Button
disabled={ isDisabled={
!( !(
methods.formState.isValid || methods.formState.isValid ||
(isPaymentNext && canProceedToPayment) (isPaymentNext && canProceedToPayment)
) )
} }
intent="secondary" variant="Tertiary"
size="small" typography="Body/Paragraph/mdBold"
theme="base" size="Medium"
type="submit" type="submit"
> >
{isPaymentNext {isPaymentNext

View File

@@ -4,10 +4,11 @@ import { useCallback, useState } from "react"
import { FormProvider, useForm } from "react-hook-form" import { FormProvider, useForm } from "react-hook-form"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { Button } from "@scandic-hotels/design-system/Button"
import { useEnterDetailsStore } from "@/stores/enter-details" import { useEnterDetailsStore } from "@/stores/enter-details"
import SpecialRequests from "@/components/HotelReservation/EnterDetails/Details/SpecialRequests" import SpecialRequests from "@/components/HotelReservation/EnterDetails/Details/SpecialRequests"
import Button from "@/components/TempDesignSystem/Button"
import CountrySelect from "@/components/TempDesignSystem/Form/Country" import CountrySelect from "@/components/TempDesignSystem/Form/Country"
import Input from "@/components/TempDesignSystem/Form/Input" import Input from "@/components/TempDesignSystem/Form/Input"
import Phone from "@/components/TempDesignSystem/Form/Phone" import Phone from "@/components/TempDesignSystem/Form/Phone"
@@ -151,15 +152,15 @@ export default function Details({ user }: DetailsProps) {
</div> </div>
<footer className={styles.footer}> <footer className={styles.footer}>
<Button <Button
disabled={ isDisabled={
!( !(
methods.formState.isValid || methods.formState.isValid ||
(isPaymentNext && canProceedToPayment) (isPaymentNext && canProceedToPayment)
) )
} }
intent="secondary" variant="Tertiary"
size="small" typography="Body/Paragraph/mdBold"
theme="base" size="Medium"
type="submit" type="submit"
> >
{isPaymentNext {isPaymentNext

View File

@@ -1,42 +1,25 @@
import { useState } from "react"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon" import { Typography } from "@scandic-hotels/design-system/Typography"
import Divider from "@/components/TempDesignSystem/Divider"
import TextArea from "@/components/TempDesignSystem/Form/TextArea" import TextArea from "@/components/TempDesignSystem/Form/TextArea"
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
import styles from "./specialRequests.module.css" import styles from "./specialRequests.module.css"
export default function SpecialRequests() { export default function SpecialRequests() {
const [isOpen, setIsOpen] = useState(false)
const intl = useIntl() const intl = useIntl()
function toggleRequests() {
setIsOpen((prevVal) => !prevVal)
}
return ( return (
<div className={styles.requests} data-requests-open={isOpen}> <div className={styles.requests}>
<button className={styles.toggle} onClick={toggleRequests} type="button"> <Typography variant="Title/Overline/sm">
<Footnote <p className={styles.heading}>
color="uiTextHighContrast"
textTransform="uppercase"
type="label"
className={styles.header}
textAlign="left"
>
{intl.formatMessage({ {intl.formatMessage({
defaultMessage: "Special requests", defaultMessage: "Special requests (optional)",
})} })}
</Footnote> </p>
<MaterialIcon icon="keyboard_arrow_down" className={styles.chevron} /> </Typography>
<Divider className={styles.divider} color="subtle" />
</button>
<div className={styles.content}> <div className={styles.content}>
<div className={styles.contentWrapper}> {/*
{/*
TODO: Hiding because API is not ready for this yet (https://scandichotels.atlassian.net/browse/SW-1497). Add back in when API is ready. TODO: Hiding because API is not ready for this yet (https://scandichotels.atlassian.net/browse/SW-1497). Add back in when API is ready.
@@ -74,14 +57,13 @@ export default function SpecialRequests() {
}, },
]} ]}
/> */} /> */}
<TextArea <TextArea
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: defaultMessage:
"Is there anything else you would like us to know before your arrival?", "Is there anything else you would like us to know before your arrival?",
})} })}
name="specialRequest.comment" name="specialRequest.comment"
/> />
</div>
</div> </div>
</div> </div>
) )

View File

@@ -1,55 +1,14 @@
.requests { .requests {
--header-height: 50px;
display: grid;
grid-template-rows: var(--header-height) 0fr;
transition: 0.3s ease-out;
grid-column: 1 / -1; grid-column: 1 / -1;
}
.toggle {
display: grid; display: grid;
grid-template-areas: gap: var(--Space-x2);
"header chevron"
"divider divider";
grid-template-columns: 1fr auto;
background-color: transparent;
gap: var(--Spacing-x1);
border: none;
width: 100%;
cursor: pointer;
margin: var(--Spacing-x2) 0;
} }
.header { .heading {
grid-area: header; color: var(--Text-Default);
align-self: flex-start;
}
.chevron {
grid-area: chevron;
}
.divider {
grid-area: divider;
border-top: 1px solid var(--Color-gray-300);
}
.requests[data-requests-open="true"] .chevron {
transform: rotate(180deg);
} }
.content { .content {
overflow: hidden;
}
.contentWrapper {
padding-top: var(--Spacing-x3);
display: grid; display: grid;
gap: var(--Spacing-x2); gap: var(--Space-x2);
width: 100%;
}
.requests[data-requests-open="true"] {
grid-template-rows: var(--header-height) 1fr;
} }

View File

@@ -12,6 +12,9 @@ import SelectedRoom from "@/components/HotelReservation/EnterDetails/SelectedRoo
import Title from "@/components/TempDesignSystem/Text/Title" import Title from "@/components/TempDesignSystem/Text/Title"
import { useRoomContext } from "@/contexts/Details/Room" import { useRoomContext } from "@/contexts/Details/Room"
import { getBedTypeInfoText } from "./utils"
import { ChildBedMapEnum } from "@/types/components/bookingWidget/enums"
import { StepEnum } from "@/types/enums/step" import { StepEnum } from "@/types/enums/step"
export default function Multiroom() { export default function Multiroom() {
@@ -41,6 +44,16 @@ export default function Multiroom() {
isBreakfastStepValid isBreakfastStepValid
) )
const hasChildWithExtraBed = room.childrenInRoom?.some(
(child) => Number(child.bed) === ChildBedMapEnum.IN_EXTRA_BED
)
const bedTypeInfoText = getBedTypeInfoText(
intl,
!!hasChildWithExtraBed,
room.bedTypes.length > 1
)
return ( return (
<section> <section>
<Header> <Header>
@@ -60,12 +73,9 @@ export default function Multiroom() {
{room.bedTypes ? ( {room.bedTypes ? (
<Section <Section
header={intl.formatMessage({ header={intl.formatMessage({ defaultMessage: "Select bed" })}
defaultMessage: "Select bed", label={intl.formatMessage({ defaultMessage: "Request bedtype" })}
})} additionalInfo={bedTypeInfoText}
label={intl.formatMessage({
defaultMessage: "Request bedtype",
})}
step={StepEnum.selectBed} step={StepEnum.selectBed}
disabled={!arePreviousRoomsValid} disabled={!arePreviousRoomsValid}
> >

View File

@@ -12,6 +12,9 @@ import SelectedRoom from "@/components/HotelReservation/EnterDetails/SelectedRoo
import Title from "@/components/TempDesignSystem/Text/Title" import Title from "@/components/TempDesignSystem/Text/Title"
import { useRoomContext } from "@/contexts/Details/Room" import { useRoomContext } from "@/contexts/Details/Room"
import { getBedTypeInfoText } from "./utils"
import { ChildBedMapEnum } from "@/types/components/bookingWidget/enums"
import { StepEnum } from "@/types/enums/step" import { StepEnum } from "@/types/enums/step"
import type { SafeUser } from "@/types/user" import type { SafeUser } from "@/types/user"
@@ -22,9 +25,23 @@ export default function RoomOne({ user }: { user: SafeUser }) {
breakfastPackages: state.breakfastPackages, breakfastPackages: state.breakfastPackages,
isMultiroom: state.rooms.length > 1, isMultiroom: state.rooms.length > 1,
})) }))
const {
room: { bedTypes },
} = useRoomContext()
const hasChildWithExtraBed = room.childrenInRoom?.some(
(child) => Number(child.bed) === ChildBedMapEnum.IN_EXTRA_BED
)
const bedTypeInfoText = getBedTypeInfoText(
intl,
!!hasChildWithExtraBed,
bedTypes.length > 1
)
const showBreakfastStep = const showBreakfastStep =
!room.breakfastIncluded && !!breakfastPackages?.length !room.breakfastIncluded && !!breakfastPackages?.length
return ( return (
<section> <section>
{isMultiroom ? ( {isMultiroom ? (
@@ -46,12 +63,9 @@ export default function RoomOne({ user }: { user: SafeUser }) {
{room.bedTypes ? ( {room.bedTypes ? (
<Section <Section
header={intl.formatMessage({ header={intl.formatMessage({ defaultMessage: "Select bed" })}
defaultMessage: "Select bed", label={intl.formatMessage({ defaultMessage: "Request bedtype" })}
})} additionalInfo={bedTypeInfoText}
label={intl.formatMessage({
defaultMessage: "Request bedtype",
})}
step={StepEnum.selectBed} step={StepEnum.selectBed}
> >
<BedType /> <BedType />

View File

@@ -0,0 +1,25 @@
import type { IntlShape } from "react-intl"
export function getBedTypeInfoText(
intl: IntlShape,
hasChildWithExtraBed: boolean,
hasMultipleBedTypes: boolean
) {
const availabilityText = intl.formatMessage({
defaultMessage: "Subject to availability",
})
const extraBedText = intl.formatMessage({
defaultMessage: "Extra bed will be provided additionally",
})
if (hasMultipleBedTypes && hasChildWithExtraBed) {
return `${availabilityText}. ${extraBedText}`
} else if (hasMultipleBedTypes) {
return availabilityText
} else if (hasChildWithExtraBed) {
return extraBedText
}
return null
}

View File

@@ -2,8 +2,8 @@
import { useEffect, useState } from "react" import { useEffect, useState } from "react"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import Footnote from "@/components/TempDesignSystem/Text/Footnote" import { Typography } from "@scandic-hotels/design-system/Typography"
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
import { useRoomContext } from "@/contexts/Details/Room" import { useRoomContext } from "@/contexts/Details/Room"
import styles from "./section.module.css" import styles from "./section.module.css"
@@ -15,6 +15,7 @@ export default function Section({
children, children,
header, header,
label, label,
additionalInfo,
step, step,
disabled, disabled,
}: React.PropsWithChildren<SectionProps>) { }: React.PropsWithChildren<SectionProps>) {
@@ -49,21 +50,21 @@ export default function Section({
return ( return (
<div <div
className={`${styles.accordion} ${disabled ? styles.disabled : ""}`} className={`${styles.section} ${disabled ? styles.disabled : ""}`}
data-step={step} data-step={step}
> >
<header className={styles.header}> <header className={styles.header}>
<Footnote <Typography variant="Title/Overline/sm">
className={styles.title} <h2 className={styles.heading}>{header}</h2>
asChild </Typography>
textTransform="uppercase" <Typography variant="Title/Subtitle/md">
type="label" <p className={styles.subheading}>{title}</p>
> </Typography>
<h2>{header}</h2> {additionalInfo ? (
</Footnote> <Typography variant="Body/Paragraph/mdRegular">
<Subtitle className={styles.selection} type="two"> <p className={styles.additionalInfo}>{additionalInfo}</p>
{title} </Typography>
</Subtitle> ) : null}
</header> </header>
<div className={styles.content}> <div className={styles.content}>
<div className={styles.contentWrapper}>{children}</div> <div className={styles.contentWrapper}>{children}</div>

View File

@@ -1,26 +1,18 @@
.accordion { .section {
--header-height: 2.4em;
--circle-height: 24px;
gap: var(--Spacing-x3);
width: 100%;
padding-top: var(--Spacing-x3);
display: grid; display: grid;
grid-template-areas: "header" "content"; gap: var(--Space-x3);
grid-template-rows: var(--header-height) 1fr; width: 100%;
column-gap: var(--Spacing-x-one-and-half); padding-top: var(--Space-x3);
} }
.header { .heading,
grid-area: header; .subheading {
color: var(--Text-Default);
} }
.title { .additionalInfo {
grid-area: title; color: var(--Text-Secondary);
text-align: start; margin-top: var(--Space-x05);
}
.selection {
grid-area: selection;
} }
.contentWrapper { .contentWrapper {
@@ -28,7 +20,6 @@
} }
.content { .content {
grid-area: content;
border-bottom: 1px solid var(--Primary-Light-On-Surface-Divider-subtle); border-bottom: 1px solid var(--Primary-Light-On-Surface-Divider-subtle);
} }
@@ -36,10 +27,3 @@
opacity: 0.5; opacity: 0.5;
pointer-events: none; pointer-events: none;
} }
@media screen and (min-width: 768px) {
.accordion {
column-gap: var(--Spacing-x3);
grid-template-areas: "header" "content";
}
}

View File

@@ -356,7 +356,7 @@
"2I23F2": [ "2I23F2": [
{ {
"type": 0, "type": 0,
"value": "Sådan bruger du" "value": "Se hvordan"
} }
], ],
"2JgixA": [ "2JgixA": [
@@ -1311,7 +1311,8 @@
"value": "Indtast venligst den kode, der er sendt til " "value": "Indtast venligst den kode, der er sendt til "
}, },
{ {
"children": [], "children": [
],
"type": 8, "type": 8,
"value": "maskedContactInfo" "value": "maskedContactInfo"
}, },
@@ -3501,7 +3502,8 @@
"value": "Indtast venligst den kode, der er sendt til " "value": "Indtast venligst den kode, der er sendt til "
}, },
{ {
"children": [], "children": [
],
"type": 8, "type": 8,
"value": "maskedContactInfo" "value": "maskedContactInfo"
}, },
@@ -5921,4 +5923,4 @@
"value": "Kort" "value": "Kort"
} }
] ]
} }

View File

@@ -1307,7 +1307,8 @@
"value": "Bitte geben Sie den an " "value": "Bitte geben Sie den an "
}, },
{ {
"children": [], "children": [
],
"type": 8, "type": 8,
"value": "maskedContactInfo" "value": "maskedContactInfo"
}, },
@@ -1322,7 +1323,8 @@
"value": "Mit Ihrer Anmeldung akzeptieren Sie die Allgemeinen Geschäftsbedingungen von Scandic Friends " "value": "Mit Ihrer Anmeldung akzeptieren Sie die Allgemeinen Geschäftsbedingungen von Scandic Friends "
}, },
{ {
"children": [], "children": [
],
"type": 8, "type": 8,
"value": "termsAndConditionsLink" "value": "termsAndConditionsLink"
}, },
@@ -3492,7 +3494,8 @@
"value": "Bitte geben Sie den Code ein, der an " "value": "Bitte geben Sie den Code ein, der an "
}, },
{ {
"children": [], "children": [
],
"type": 8, "type": 8,
"value": "maskedContactInfo" "value": "maskedContactInfo"
}, },
@@ -5912,4 +5915,4 @@
"value": "Karte" "value": "Karte"
} }
] ]
} }

View File

@@ -547,7 +547,8 @@
"value": "Please enter the code sent to " "value": "Please enter the code sent to "
}, },
{ {
"children": [], "children": [
],
"type": 8, "type": 8,
"value": "maskedContactInfo" "value": "maskedContactInfo"
}, },
@@ -1778,7 +1779,8 @@
"value": "Please enter the code sent to " "value": "Please enter the code sent to "
}, },
{ {
"children": [], "children": [
],
"type": 8, "type": 8,
"value": "maskedContactInfo" "value": "maskedContactInfo"
}, },
@@ -4333,7 +4335,8 @@
"value": "Please enter the code sent to " "value": "Please enter the code sent to "
}, },
{ {
"children": [], "children": [
],
"type": 8, "type": 8,
"value": "maskedContactInfo" "value": "maskedContactInfo"
}, },
@@ -7356,4 +7359,4 @@
"value": "Map" "value": "Map"
} }
] ]
} }

View File

@@ -356,7 +356,7 @@
"2I23F2": [ "2I23F2": [
{ {
"type": 0, "type": 0,
"value": "Kuinka käyttää" "value": "Hyödynnä näin"
} }
], ],
"2JgixA": [ "2JgixA": [
@@ -1315,7 +1315,8 @@
"value": "Kirjoita osoitteeseen " "value": "Kirjoita osoitteeseen "
}, },
{ {
"children": [], "children": [
],
"type": 8, "type": 8,
"value": "maskedContactInfo" "value": "maskedContactInfo"
}, },
@@ -3513,7 +3514,8 @@
"value": "Kirjoita osoitteeseen " "value": "Kirjoita osoitteeseen "
}, },
{ {
"children": [], "children": [
],
"type": 8, "type": 8,
"value": "maskedContactInfo" "value": "maskedContactInfo"
}, },
@@ -5933,4 +5935,4 @@
"value": "Kartta" "value": "Kartta"
} }
] ]
} }

View File

@@ -356,7 +356,7 @@
"2I23F2": [ "2I23F2": [
{ {
"type": 0, "type": 0,
"value": "Hvordan bruke" "value": "Les hvordan"
} }
], ],
"2JgixA": [ "2JgixA": [
@@ -1307,7 +1307,8 @@
"value": "Vennligst skriv inn koden som ble sendt til " "value": "Vennligst skriv inn koden som ble sendt til "
}, },
{ {
"children": [], "children": [
],
"type": 8, "type": 8,
"value": "maskedContactInfo" "value": "maskedContactInfo"
}, },
@@ -5921,4 +5922,4 @@
"value": "Kart" "value": "Kart"
} }
] ]
} }

View File

@@ -17,4 +17,4 @@
"value": " m²" "value": " m²"
} }
] ]
} }

View File

@@ -1307,7 +1307,8 @@
"value": "Vänligen ange koden som skickats till " "value": "Vänligen ange koden som skickats till "
}, },
{ {
"children": [], "children": [
],
"type": 8, "type": 8,
"value": "maskedContactInfo" "value": "maskedContactInfo"
}, },
@@ -3497,7 +3498,8 @@
"value": "Vänligen ange den kod som skickats till " "value": "Vänligen ange den kod som skickats till "
}, },
{ {
"children": [], "children": [
],
"type": 8, "type": 8,
"value": "maskedContactInfo" "value": "maskedContactInfo"
}, },
@@ -5925,4 +5927,4 @@
"value": "Karta" "value": "Karta"
} }
] ]
} }

View File

@@ -3,6 +3,7 @@ import type { StepEnum } from "@/types/enums/step"
export interface SectionProps { export interface SectionProps {
header: string header: string
label: string label: string
additionalInfo?: string | null
step: StepEnum step: StepEnum
disabled?: boolean disabled?: boolean
} }