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

View File

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

View File

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

View File

@@ -1,42 +1,25 @@
import { useState } from "react"
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 Footnote from "@/components/TempDesignSystem/Text/Footnote"
import styles from "./specialRequests.module.css"
export default function SpecialRequests() {
const [isOpen, setIsOpen] = useState(false)
const intl = useIntl()
function toggleRequests() {
setIsOpen((prevVal) => !prevVal)
}
return (
<div className={styles.requests} data-requests-open={isOpen}>
<button className={styles.toggle} onClick={toggleRequests} type="button">
<Footnote
color="uiTextHighContrast"
textTransform="uppercase"
type="label"
className={styles.header}
textAlign="left"
>
<div className={styles.requests}>
<Typography variant="Title/Overline/sm">
<p className={styles.heading}>
{intl.formatMessage({
defaultMessage: "Special requests",
defaultMessage: "Special requests (optional)",
})}
</Footnote>
<MaterialIcon icon="keyboard_arrow_down" className={styles.chevron} />
<Divider className={styles.divider} color="subtle" />
</button>
</p>
</Typography>
<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.
@@ -74,14 +57,13 @@ export default function SpecialRequests() {
},
]}
/> */}
<TextArea
label={intl.formatMessage({
defaultMessage:
"Is there anything else you would like us to know before your arrival?",
})}
name="specialRequest.comment"
/>
</div>
<TextArea
label={intl.formatMessage({
defaultMessage:
"Is there anything else you would like us to know before your arrival?",
})}
name="specialRequest.comment"
/>
</div>
</div>
)

View File

@@ -1,55 +1,14 @@
.requests {
--header-height: 50px;
display: grid;
grid-template-rows: var(--header-height) 0fr;
transition: 0.3s ease-out;
grid-column: 1 / -1;
}
.toggle {
display: grid;
grid-template-areas:
"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;
gap: var(--Space-x2);
}
.header {
grid-area: header;
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);
.heading {
color: var(--Text-Default);
}
.content {
overflow: hidden;
}
.contentWrapper {
padding-top: var(--Spacing-x3);
display: grid;
gap: var(--Spacing-x2);
width: 100%;
}
.requests[data-requests-open="true"] {
grid-template-rows: var(--header-height) 1fr;
gap: var(--Space-x2);
}

View File

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

View File

@@ -12,6 +12,9 @@ import SelectedRoom from "@/components/HotelReservation/EnterDetails/SelectedRoo
import Title from "@/components/TempDesignSystem/Text/Title"
import { useRoomContext } from "@/contexts/Details/Room"
import { getBedTypeInfoText } from "./utils"
import { ChildBedMapEnum } from "@/types/components/bookingWidget/enums"
import { StepEnum } from "@/types/enums/step"
import type { SafeUser } from "@/types/user"
@@ -22,9 +25,23 @@ export default function RoomOne({ user }: { user: SafeUser }) {
breakfastPackages: state.breakfastPackages,
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 =
!room.breakfastIncluded && !!breakfastPackages?.length
return (
<section>
{isMultiroom ? (
@@ -46,12 +63,9 @@ export default function RoomOne({ user }: { user: SafeUser }) {
{room.bedTypes ? (
<Section
header={intl.formatMessage({
defaultMessage: "Select bed",
})}
label={intl.formatMessage({
defaultMessage: "Request bedtype",
})}
header={intl.formatMessage({ defaultMessage: "Select bed" })}
label={intl.formatMessage({ defaultMessage: "Request bedtype" })}
additionalInfo={bedTypeInfoText}
step={StepEnum.selectBed}
>
<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 { useIntl } from "react-intl"
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { useRoomContext } from "@/contexts/Details/Room"
import styles from "./section.module.css"
@@ -15,6 +15,7 @@ export default function Section({
children,
header,
label,
additionalInfo,
step,
disabled,
}: React.PropsWithChildren<SectionProps>) {
@@ -49,21 +50,21 @@ export default function Section({
return (
<div
className={`${styles.accordion} ${disabled ? styles.disabled : ""}`}
className={`${styles.section} ${disabled ? styles.disabled : ""}`}
data-step={step}
>
<header className={styles.header}>
<Footnote
className={styles.title}
asChild
textTransform="uppercase"
type="label"
>
<h2>{header}</h2>
</Footnote>
<Subtitle className={styles.selection} type="two">
{title}
</Subtitle>
<Typography variant="Title/Overline/sm">
<h2 className={styles.heading}>{header}</h2>
</Typography>
<Typography variant="Title/Subtitle/md">
<p className={styles.subheading}>{title}</p>
</Typography>
{additionalInfo ? (
<Typography variant="Body/Paragraph/mdRegular">
<p className={styles.additionalInfo}>{additionalInfo}</p>
</Typography>
) : null}
</header>
<div className={styles.content}>
<div className={styles.contentWrapper}>{children}</div>

View File

@@ -1,26 +1,18 @@
.accordion {
--header-height: 2.4em;
--circle-height: 24px;
gap: var(--Spacing-x3);
width: 100%;
padding-top: var(--Spacing-x3);
.section {
display: grid;
grid-template-areas: "header" "content";
grid-template-rows: var(--header-height) 1fr;
column-gap: var(--Spacing-x-one-and-half);
gap: var(--Space-x3);
width: 100%;
padding-top: var(--Space-x3);
}
.header {
grid-area: header;
.heading,
.subheading {
color: var(--Text-Default);
}
.title {
grid-area: title;
text-align: start;
}
.selection {
grid-area: selection;
.additionalInfo {
color: var(--Text-Secondary);
margin-top: var(--Space-x05);
}
.contentWrapper {
@@ -28,7 +20,6 @@
}
.content {
grid-area: content;
border-bottom: 1px solid var(--Primary-Light-On-Surface-Divider-subtle);
}
@@ -36,10 +27,3 @@
opacity: 0.5;
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": [
{
"type": 0,
"value": "Sådan bruger du"
"value": "Se hvordan"
}
],
"2JgixA": [
@@ -1311,7 +1311,8 @@
"value": "Indtast venligst den kode, der er sendt til "
},
{
"children": [],
"children": [
],
"type": 8,
"value": "maskedContactInfo"
},
@@ -3501,7 +3502,8 @@
"value": "Indtast venligst den kode, der er sendt til "
},
{
"children": [],
"children": [
],
"type": 8,
"value": "maskedContactInfo"
},
@@ -5921,4 +5923,4 @@
"value": "Kort"
}
]
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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