Merged in fix/SW-2406-update-modal-ancillaries (pull request #1911)

fix(SW-2406): update modal for ancillaries

* fix(SW-2406): update modal for ancillaries

* fix(SW-2406): revert translations


Approved-by: Niclas Edenvin
Approved-by: Erik Tiekstra
This commit is contained in:
Bianca Widstam
2025-04-30 08:20:57 +00:00
parent f4fe2e3a4a
commit 7070770581
9 changed files with 98 additions and 95 deletions

View File

@@ -2,7 +2,7 @@
display: flex; display: flex;
gap: var(--Spacing-x4); gap: var(--Spacing-x4);
justify-content: flex-end; justify-content: flex-end;
padding: var(--Space-x15) var(--Space-x15) 0; padding: var(--Space-x2) var(--Space-x15) 0;
} }
.confirmButtons { .confirmButtons {

View File

@@ -26,6 +26,7 @@ export default function ActionButtons({
const { const {
currentStep, currentStep,
prevStep, prevStep,
prevStepMobile,
selectQuantity, selectQuantity,
selectDeliveryTime, selectDeliveryTime,
selectQuantityAndDeliveryTime, selectQuantityAndDeliveryTime,
@@ -33,6 +34,7 @@ export default function ActionButtons({
} = useAddAncillaryStore((state) => ({ } = useAddAncillaryStore((state) => ({
currentStep: state.currentStep, currentStep: state.currentStep,
prevStep: state.prevStep, prevStep: state.prevStep,
prevStepMobile: state.prevStepMobile,
selectQuantity: state.selectQuantity, selectQuantity: state.selectQuantity,
selectDeliveryTime: state.selectDeliveryTime, selectDeliveryTime: state.selectDeliveryTime,
selectQuantityAndDeliveryTime: state.selectQuantityAndDeliveryTime, selectQuantityAndDeliveryTime: state.selectQuantityAndDeliveryTime,
@@ -116,7 +118,7 @@ export default function ActionButtons({
variant="Text" variant="Text"
size="Small" size="Small"
color="Primary" color="Primary"
onPress={prevStep} onPress={isMobile ? prevStepMobile : prevStep}
> >
{intl.formatMessage({ {intl.formatMessage({
defaultMessage: "Back", defaultMessage: "Back",

View File

@@ -1,15 +1,15 @@
.selectContainer { .selectContainer {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: var(--Spacing-x2); gap: var(--Space-x2);
margin-bottom: var(--Spacing-x2); padding: var(--Space-x3);
margin-bottom: var(--Space-x05);
background-color: var(--Base-Background-Primary-Normal);
border-radius: var(--Corner-radius-Medium);
} }
.select { .select {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: var(--Spacing-x2); gap: var(--Space-x1);
padding: var(--Spacing-x2) var(--Spacing-x3);
background-color: var(--Base-Background-Primary-Normal);
border-radius: var(--Corner-radius-Medium);
} }

View File

@@ -1,11 +1,10 @@
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { generateDeliveryOptions } from "@/components/HotelReservation/MyStay/utils/ancillaries" import { generateDeliveryOptions } from "@/components/HotelReservation/MyStay/utils/ancillaries"
import Input from "@/components/TempDesignSystem/Form/Input" import Input from "@/components/TempDesignSystem/Form/Input"
import Select from "@/components/TempDesignSystem/Form/Select" import Select from "@/components/TempDesignSystem/Form/Select"
import Body from "@/components/TempDesignSystem/Text/Body"
import Caption from "@/components/TempDesignSystem/Text/Caption"
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
import styles from "./deliveryDetailsStep.module.css" import styles from "./deliveryDetailsStep.module.css"
@@ -16,11 +15,13 @@ export default function DeliveryMethodStep() {
return ( return (
<div className={styles.selectContainer}> <div className={styles.selectContainer}>
<div className={styles.select}> <div className={styles.select}>
<Subtitle type="two"> <Typography variant="Body/Supporting text (caption)/smBold">
{intl.formatMessage({ <h3>
defaultMessage: "Delivered at:", {intl.formatMessage({
})} defaultMessage: "Delivered at:",
</Subtitle> })}
</h3>
</Typography>
<Select <Select
name="deliveryTime" name="deliveryTime"
label={""} label={""}
@@ -28,13 +29,15 @@ export default function DeliveryMethodStep() {
registerOptions={{ required: true }} registerOptions={{ required: true }}
isNestedInModal isNestedInModal
/> />
<Body> </div>
<Typography variant="Body/Supporting text (caption)/smRegular">
<p>
{intl.formatMessage({ {intl.formatMessage({
defaultMessage: defaultMessage:
"All add-ons are delivered at the same time. Changes to delivery times will affect earlier add-ons.", "All add-ons are delivered at the same time. Changes to delivery times will affect earlier add-ons.",
})} })}
</Body> </p>
</div> </Typography>
<div className={styles.select}> <div className={styles.select}>
<Input <Input
label={intl.formatMessage({ label={intl.formatMessage({
@@ -42,11 +45,13 @@ export default function DeliveryMethodStep() {
})} })}
name="optionalText" name="optionalText"
/> />
<Caption> <Typography variant="Body/Supporting text (caption)/smRegular">
{intl.formatMessage({ <h3>
defaultMessage: "Optional", {intl.formatMessage({
})} defaultMessage: "Optional",
</Caption> })}
</h3>
</Typography>
</div> </div>
</div> </div>
) )

View File

@@ -50,20 +50,12 @@ export default function SelectQuantityStep({ user }: SelectQuantityStepProps) {
const insufficientPoints = currentPoints < pointsCost || currentPoints === 0 const insufficientPoints = currentPoints < pointsCost || currentPoints === 0
const pointsLabel = insufficientPoints
? intl.formatMessage({
defaultMessage: "Insufficient points",
})
: intl.formatMessage({
defaultMessage: "Select quantity",
})
return ( return (
<div className={styles.selectContainer}> <div className={styles.selectContainer}>
{selectedAncillary?.points && user && ( {selectedAncillary?.points && user && (
<div className={styles.select}> <div className={styles.select}>
<Typography variant="Title/Subtitle/md"> <Typography variant="Title/Subtitle/md">
<h2> <h2 className={styles.selectTitle}>
{intl.formatMessage({ {intl.formatMessage({
defaultMessage: "Pay with points", defaultMessage: "Pay with points",
})} })}
@@ -84,18 +76,29 @@ export default function SelectQuantityStep({ user }: SelectQuantityStepProps) {
<p>{currentPoints}</p> <p>{currentPoints}</p>
</Typography> </Typography>
</div> </div>
<Select {insufficientPoints ? (
name="quantityWithPoints" <Typography variant="Body/Supporting text (caption)/smRegular">
label={pointsLabel} <h2 className={styles.insufficientPoints}>
items={pointsQuantityOptions} {intl.formatMessage({
disabled={insufficientPoints} defaultMessage: "Insufficient points",
isNestedInModal })}
/> </h2>
</Typography>
) : (
<Select
name="quantityWithPoints"
label={intl.formatMessage({
defaultMessage: "Select quantity",
})}
items={pointsQuantityOptions}
isNestedInModal
/>
)}
</div> </div>
)} )}
<div className={styles.select}> <div className={styles.select}>
<Typography variant="Title/Subtitle/md"> <Typography variant="Title/Subtitle/md">
<h2> <h2 className={styles.selectTitle}>
{ {
/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */ /* eslint-disable-next-line formatjs/no-literal-string-in-jsx */
" " " "

View File

@@ -2,7 +2,6 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: var(--Space-x025); gap: var(--Space-x025);
margin-bottom: var(--Space-x2);
} }
.select { .select {
@@ -15,6 +14,14 @@
margin-bottom: var(--Spacing-x1); margin-bottom: var(--Spacing-x1);
} }
.selectTitle {
margin-bottom: var(--Space-x1);
}
.insufficientPoints {
color: var(--Text-Tertiary);
}
.totalPointsContainer { .totalPointsContainer {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@@ -73,6 +80,9 @@
display: none; display: none;
} }
@media screen and (min-width: 768px) { @media screen and (min-width: 768px) {
.select {
margin-bottom: 0;
}
.icon { .icon {
display: block; display: block;
} }

View File

@@ -3,6 +3,7 @@
flex-direction: column; flex-direction: column;
max-height: 70dvh; max-height: 70dvh;
width: 100%; width: 100%;
margin-top: var(--Space-x3);
} }
.form { .form {
@@ -19,22 +20,6 @@
.modalScrollable { .modalScrollable {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
margin-bottom: var(--Spacing-x2);
}
.imageContainer {
position: relative;
width: 100%;
aspect-ratio: 16 / 9;
border-radius: var(--Corner-radius-Medium);
overflow: hidden;
margin-top: var(--Spacing-x1);
flex-shrink: 0;
margin-bottom: var(--Spacing-x3);
}
.image {
object-fit: cover;
} }
.price { .price {
@@ -52,12 +37,10 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-between; justify-content: space-between;
position: sticky;
border-radius: var(--Corner-radius-Medium); border-radius: var(--Corner-radius-Medium);
bottom: 0;
z-index: 10;
background: var(--Surface-Primary-OnSurface-Default); background: var(--Surface-Primary-OnSurface-Default);
padding-bottom: var(--Space-x15); padding-bottom: var(--Space-x15);
margin-top: var(--Space-x2);
} }
.description { .description {
@@ -65,15 +48,6 @@
margin: var(--Spacing-x2) 0; margin: var(--Spacing-x2) 0;
} }
.actionButtons {
position: sticky;
bottom: 0;
z-index: 10;
background: var(--UI-Opacity-White-100);
border-top: 1px solid var(--Base-Border-Normal);
padding-bottom: var(--Space-x025);
}
.pointsDivider { .pointsDivider {
display: flex; display: flex;
gap: var(--Space-x2); gap: var(--Space-x2);

View File

@@ -26,7 +26,6 @@ import {
getAncillarySessionData, getAncillarySessionData,
setAncillarySessionData, setAncillarySessionData,
} from "@/components/HotelReservation/MyStay/utils/ancillaries" } from "@/components/HotelReservation/MyStay/utils/ancillaries"
import Image from "@/components/Image"
import LoadingSpinner from "@/components/LoadingSpinner" import LoadingSpinner from "@/components/LoadingSpinner"
import Modal from "@/components/Modal" import Modal from "@/components/Modal"
import Divider from "@/components/TempDesignSystem/Divider" import Divider from "@/components/TempDesignSystem/Divider"
@@ -351,14 +350,6 @@ export default function AddAncillaryFlowModal({
<div className={styles.modalScrollable}> <div className={styles.modalScrollable}>
{selectedAncillary && ( {selectedAncillary && (
<> <>
<div className={styles.imageContainer}>
<Image
className={styles.image}
src={selectedAncillary.imageUrl}
alt={selectedAncillary.title}
fill
/>
</div>
{currentStep !== AncillaryStepEnum.confirmation && ( {currentStep !== AncillaryStepEnum.confirmation && (
<div className={styles.contentContainer}> <div className={styles.contentContainer}>
<div className={styles.price}> <div className={styles.price}>
@@ -409,24 +400,24 @@ export default function AddAncillaryFlowModal({
</> </>
)} )}
<Steps user={user} savedCreditCards={savedCreditCards} /> <Steps user={user} savedCreditCards={savedCreditCards} />
{currentStep === AncillaryStepEnum.selectAncillary ? null : (
<div
className={
currentStep === AncillaryStepEnum.confirmation
? styles.confirmStep
: ""
}
>
<PriceDetails isPriceDetailsOpen={isPriceDetailsOpen} />
<ActionButtons
isPriceDetailsOpen={isPriceDetailsOpen}
togglePriceDetails={togglePriceDetails}
isSubmitting={addAncillary.isPending || isLoading}
/>
</div>
)}
</div> </div>
</form> </form>
{currentStep === AncillaryStepEnum.selectAncillary ? null : (
<div
className={
currentStep === AncillaryStepEnum.confirmation
? styles.confirmStep
: styles.actionButtons
}
>
<PriceDetails isPriceDetailsOpen={isPriceDetailsOpen} />
<ActionButtons
isPriceDetailsOpen={isPriceDetailsOpen}
togglePriceDetails={togglePriceDetails}
isSubmitting={addAncillary.isPending || isLoading}
/>
</div>
)}
</FormProvider> </FormProvider>
</div> </div>
</Modal> </Modal>

View File

@@ -52,6 +52,7 @@ export interface AddAncillaryState {
openModal: () => void openModal: () => void
closeModal: () => void closeModal: () => void
prevStep: () => void prevStep: () => void
prevStepMobile: () => void
breakfastData: BreakfastData | null breakfastData: BreakfastData | null
setBreakfastData: (breakfastData: BreakfastData | null) => void setBreakfastData: (breakfastData: BreakfastData | null) => void
isBreakfast: boolean isBreakfast: boolean
@@ -194,6 +195,23 @@ export const createAddAncillaryStore = (
} }
}) })
), ),
prevStepMobile: () =>
set(
produce((state: AddAncillaryState) => {
if (state.currentStep === AncillaryStepEnum.selectQuantity) {
state.isOpen = false
clearAncillarySessionData()
state.selectedAncillary = null
state.steps = steps
} else {
if (state.currentStep === AncillaryStepEnum.confirmation) {
state.currentStep = AncillaryStepEnum.selectQuantity
} else {
state.currentStep = state.currentStep - 1
}
}
})
),
selectAncillary: (ancillary) => selectAncillary: (ancillary) =>
set( set(
produce((state: AddAncillaryState) => { produce((state: AddAncillaryState) => {