Merged in feat/SW-2612-mystay-breakfast-buffet-u (pull request #2059)

Feat: SW-2612 Updated breakfast ancillary UI and Optimised code

* Feat: SW-2612 Updated breakfast ancillary UI and Optimised code

* feat: SW-2612 Updated UI as per figma

* feat: SW-2612 Optimised code

* feat: SW-2612 Optimised code


Approved-by: Tobias Johansson
This commit is contained in:
Hrishikesh Vaipurkar
2025-05-16 08:32:54 +00:00
parent d489bc7aed
commit fa7d94093e
9 changed files with 113 additions and 38 deletions

View File

@@ -2,12 +2,12 @@
display: flex; display: flex;
gap: var(--Spacing-x4); gap: var(--Spacing-x4);
justify-content: flex-end; justify-content: flex-end;
padding: var(--Space-x2) var(--Space-x15) 0; padding: var(--Space-x2) var(--Space-x15) 0 0;
} }
.confirmButtons { .confirmButtons {
display: flex; display: flex;
padding: 0 var(--Space-x15); padding-left: var(--Space-x15);
justify-content: space-between; justify-content: space-between;
align-items: baseline; align-items: baseline;
} }

View File

@@ -25,6 +25,7 @@ export default function ActionButtons({
}: ActionButtonsProps) { }: ActionButtonsProps) {
const { const {
currentStep, currentStep,
isBreakfast,
prevStep, prevStep,
prevStepMobile, prevStepMobile,
selectQuantity, selectQuantity,
@@ -33,6 +34,7 @@ export default function ActionButtons({
selectedAncillary, selectedAncillary,
} = useAddAncillaryStore((state) => ({ } = useAddAncillaryStore((state) => ({
currentStep: state.currentStep, currentStep: state.currentStep,
isBreakfast: state.isBreakfast,
prevStep: state.prevStep, prevStep: state.prevStep,
prevStepMobile: state.prevStepMobile, prevStepMobile: state.prevStepMobile,
selectQuantity: state.selectQuantity, selectQuantity: state.selectQuantity,
@@ -96,19 +98,13 @@ export default function ActionButtons({
{intl.formatMessage({ {intl.formatMessage({
defaultMessage: "Price details", defaultMessage: "Price details",
})} })}
{isPriceDetailsOpen ? ( <MaterialIcon
<MaterialIcon icon={
icon="keyboard_arrow_up" isPriceDetailsOpen ? "keyboard_arrow_up" : "keyboard_arrow_down"
size={20} }
color="Icon/Interactive/Accent" size={20}
/> color="Icon/Interactive/Accent"
) : ( />
<MaterialIcon
icon="keyboard_arrow_down"
size={20}
color="Icon/Interactive/Accent"
/>
)}
</Button> </Button>
)} )}
<div className={styles.buttons}> <div className={styles.buttons}>
@@ -140,7 +136,7 @@ export default function ActionButtons({
<Button <Button
type="button" type="button"
typography="Body/Supporting text (caption)/smBold" typography="Body/Supporting text (caption)/smBold"
variant="Secondary" variant={isBreakfast ? "Primary" : "Secondary"}
size="Small" size="Small"
isDisabled={isSubmitting} isDisabled={isSubmitting}
onPress={handleNextStep} onPress={handleNextStep}

View File

@@ -1,3 +1,4 @@
import { Fragment } from "react"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { Typography } from "@scandic-hotels/design-system/Typography" import { Typography } from "@scandic-hotels/design-system/Typography"
@@ -44,7 +45,7 @@ export default function PriceSummary({
<Divider color="subtle" /> <Divider color="subtle" />
{items.map((item) => ( {items.map((item) => (
<> <Fragment key={item.title}>
{!!item.quantityWithCard && ( {!!item.quantityWithCard && (
<PriceRow <PriceRow
title={item.title} title={item.title}
@@ -68,7 +69,7 @@ export default function PriceSummary({
/> />
)} )}
<Divider color="subtle" /> <Divider color="subtle" />
</> </Fragment>
))} ))}
<div className={styles.column}> <div className={styles.column}>

View File

@@ -37,7 +37,10 @@ export default function PriceDetails({
const quantityWithPoints = useWatch({ name: "quantityWithPoints" }) const quantityWithPoints = useWatch({ name: "quantityWithPoints" })
const quantityWithCard = useWatch({ name: "quantityWithCard" }) const quantityWithCard = useWatch({ name: "quantityWithCard" })
if (!selectedAncillary || currentStep !== AncillaryStepEnum.confirmation) { if (
!selectedAncillary ||
(currentStep !== AncillaryStepEnum.confirmation && !isBreakfast)
) {
return null return null
} }
@@ -147,8 +150,69 @@ export default function PriceDetails({
</p> </p>
</Typography> </Typography>
)} )}
{isBreakfast && breakfastData ? (
<Typography variant="Body/Paragraph/mdBold">
<p className={styles.hideOnDesktop}>
{intl.formatMessage(
{
defaultMessage:
"{totalNights, plural, one {# night} other {# nights}}",
},
{
totalNights: breakfastData.nrOfNights,
}
) +
/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */
" / " +
intl.formatMessage(
{
defaultMessage:
"{value, plural, one {# guest} other {# guests}}",
},
{
value:
breakfastData.nrOfAdults +
breakfastData.nrOfPayingChildren +
breakfastData.nrOfFreeChildren,
}
)}
</p>
</Typography>
) : null}
</div> </div>
<div className={styles.totalPriceValue}> <div className={styles.totalPriceValue}>
{isBreakfast && breakfastData ? (
<>
<Typography variant="Body/Paragraph/mdBold">
<p className={styles.showOnDesktop}>
{intl.formatMessage(
{
defaultMessage:
"{totalNights, plural, one {# night} other {# nights}}",
},
{
totalNights: breakfastData.nrOfNights,
}
) +
/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */
" / " +
intl.formatMessage(
{
defaultMessage:
"{value, plural, one {# guest} other {# guests}}",
},
{
value:
breakfastData.nrOfAdults +
breakfastData.nrOfPayingChildren +
breakfastData.nrOfFreeChildren,
}
)}
</p>
</Typography>
<Divider variant="vertical" color="subtle" />
</>
) : null}
{hasTotalPrice && ( {hasTotalPrice && (
<Typography variant="Body/Paragraph/mdBold"> <Typography variant="Body/Paragraph/mdBold">
<p> <p>
@@ -185,7 +249,7 @@ export default function PriceDetails({
</div> </div>
</div> </div>
<Divider color="subtle" /> <Divider color="subtle" />
{isPriceDetailsOpen && ( {isPriceDetailsOpen && currentStep === AncillaryStepEnum.confirmation && (
<PriceSummary <PriceSummary
totalPrice={totalPrice} totalPrice={totalPrice}
totalPoints={totalPoints} totalPoints={totalPoints}

View File

@@ -1,16 +1,20 @@
.totalPrice { .totalPrice {
display: flex; display: flex;
align-items: baseline; align-items: center;
justify-content: space-between; justify-content: space-between;
gap: var(--Spacing-x1); gap: var(--Spacing-x1);
padding: var(--Spacing-x-one-and-half); padding: var(--Spacing-x-one-and-half);
background-color: var(--Base-Surface-Secondary-light-Normal); background-color: var(--Base-Surface-Secondary-light-Normal);
border-radius: var(--Corner-radius-md); border-radius: var(--Corner-radius-md);
} }
.showOnDesktop {
display: none;
}
.totalPriceInclVAT { .totalPriceInclVAT {
display: flex; display: flex;
gap: var(--Space-x15); gap: var(--Space-x15);
flex-wrap: wrap;
} }
.totalPriceValue { .totalPriceValue {
@@ -22,3 +26,15 @@
.vatText { .vatText {
color: var(--Text-Tertiary); color: var(--Text-Tertiary);
} }
@media screen and (min-width: 768px) {
.showOnDesktop {
display: block;
}
.hideOnDesktop {
display: none;
}
.totalPrice {
align-items: baseline;
}
}

View File

@@ -126,10 +126,6 @@ function BreakfastInfo() {
const intl = useIntl() const intl = useIntl()
const breakfastData = useAddAncillaryStore((state) => state.breakfastData) const breakfastData = useAddAncillaryStore((state) => state.breakfastData)
const { setValue } = useFormContext()
setValue("quantityWithCard", 1)
if (!breakfastData) { if (!breakfastData) {
return intl.formatMessage({ return intl.formatMessage({
defaultMessage: "Can not show breakfast prices.", defaultMessage: "Can not show breakfast prices.",

View File

@@ -91,7 +91,8 @@ export default function AddAncillaryFlowModal({
const formMethods = useForm<AncillaryFormData>({ const formMethods = useForm<AncillaryFormData>({
defaultValues: { defaultValues: {
quantityWithPoints: null, quantityWithPoints: null,
quantityWithCard: !user || hasInsufficientPoints ? 1 : null, quantityWithCard:
!user || hasInsufficientPoints || isBreakfast ? 1 : null,
deliveryTime: defaultDeliveryTime, deliveryTime: defaultDeliveryTime,
optionalText: "", optionalText: "",
termsAndConditions: false, termsAndConditions: false,
@@ -357,17 +358,17 @@ export default function AddAncillaryFlowModal({
<div className={styles.contentContainer}> <div className={styles.contentContainer}>
<div className={styles.price}> <div className={styles.price}>
<Typography variant="Body/Paragraph/mdBold"> <Typography variant="Body/Paragraph/mdBold">
<p> {isBreakfast ? (
{isBreakfast ? ( <BreakfastPriceList />
<BreakfastPriceList /> ) : (
) : ( <p>
formatPrice( {formatPrice(
intl, intl,
selectedAncillary.price.total, selectedAncillary.price.total,
selectedAncillary.price.currency selectedAncillary.price.currency
) )}
)} </p>
</p> )}
</Typography> </Typography>
{selectedAncillary.points && ( {selectedAncillary.points && (
<div className={styles.pointsDivider}> <div className={styles.pointsDivider}>
@@ -406,7 +407,8 @@ export default function AddAncillaryFlowModal({
{currentStep === AncillaryStepEnum.selectAncillary ? null : ( {currentStep === AncillaryStepEnum.selectAncillary ? null : (
<div <div
className={ className={
currentStep === AncillaryStepEnum.confirmation currentStep === AncillaryStepEnum.confirmation ||
isBreakfast
? styles.confirmStep ? styles.confirmStep
: "" : ""
} }

View File

@@ -2,7 +2,6 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: var(--Spacing-x-half); gap: var(--Spacing-x-half);
padding: 0 var(--Spacing-x2);
} }
.header { .header {

View File

@@ -1,3 +1,4 @@
import { Fragment } from "react"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon" import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
@@ -71,7 +72,7 @@ export function AddedAncillaries({
} X${ancillary.totalUnit}` } X${ancillary.totalUnit}`
return ( return (
<> <Fragment key={ancillary.code}>
<Accordion className={styles.ancillaryMobile}> <Accordion className={styles.ancillaryMobile}>
<AccordionItem <AccordionItem
title={ancillaryTitle} title={ancillaryTitle}
@@ -209,7 +210,7 @@ export function AddedAncillaries({
) : null} ) : null}
</div> </div>
</div> </div>
</> </Fragment>
) )
})} })}
</div> </div>