Merged in feat/SW-1282-list-added-ancillaries (pull request #1416)

Feat/SW-1282 list added ancillaries

* feat(sw-1282): add icon to accordionItem

* feat(sw-1282): list added ancillaries

* Change translation key to already existing

* Remove duplicate key

* Move new files to the new folder structure


Approved-by: Pontus Dreij
This commit is contained in:
Niclas Edenvin
2025-02-27 22:01:11 +00:00
parent 427cc10edc
commit 747201b0f7
22 changed files with 326 additions and 17 deletions
@@ -18,7 +18,7 @@ export default async function AccessibilityAmenity({
return (
<AccordionItem
title={intl.formatMessage({ id: "Accessibility" })}
icon={IconName.Accessibility}
iconName={IconName.Accessibility}
variant="sidepeek"
trackingId="amenities:accessibility"
>
@@ -29,7 +29,7 @@ export default async function BreakfastAmenity({
return (
<AccordionItem
title={intl.formatMessage({ id: "Breakfast" })}
icon={IconName.CoffeeAlt}
iconName={IconName.CoffeeAlt}
variant="sidepeek"
trackingId="amenities:breakfast"
>
@@ -13,7 +13,7 @@ export default async function CheckInAmenity({
return (
<AccordionItem
title={`${intl.formatMessage({ id: "Check-in" })}/${intl.formatMessage({ id: "Check-out" })}`}
icon={IconName.Business}
iconName={IconName.Business}
variant="sidepeek"
trackingId="amenities:check-in"
>
@@ -19,7 +19,7 @@ export default async function ParkingAmenity({
return (
<AccordionItem
title={intl.formatMessage({ id: "Parking" })}
icon={IconName.Parking}
iconName={IconName.Parking}
variant="sidepeek"
trackingId="amenities:parking"
>
@@ -0,0 +1,109 @@
.container {
display: flex;
flex-direction: column;
gap: var(--Spacing-x-half);
padding: 0 var(--Spacing-x2);
}
.header {
display: flex;
flex-direction: column;
justify-content: space-between;
gap: var(--Spacing-x-one-and-half);
margin-top: var(--Spacing-x5);
}
@media (min-width: 768px) {
.container {
gap: var(--Spacing-x3);
}
.header {
align-items: center;
flex-direction: row;
}
}
.deliveryTime {
display: flex;
justify-content: space-between;
gap: var(--Spacing-x1);
}
.ancillaryMobile {
background-color: var(--Base-Background-Primary-Normal);
border-radius: var(--Corner-radius-Medium);
display: flex;
flex-direction: column;
gap: var(--Spacing-x1);
}
.ancillaryDesktop {
display: none;
padding: var(--Spacing-x2);
background-color: var(--Base-Background-Primary-Normal);
border-radius: var(--Corner-radius-Medium);
flex-direction: column;
gap: var(--Spacing-x1);
}
@media (min-width: 768px) {
.ancillaryMobile {
display: none;
}
.ancillaryDesktop {
display: flex;
}
}
.paymentMobileWrapper {
display: flex;
}
.paymentMobile {
display: flex;
gap: var(--Spacing-x2);
align-items: center;
}
.commentMobile {
margin-bottom: var(--Spacing-x3);
}
.footerMobile {
display: flex;
margin-top: var(--Spacing-x4);
}
.specification {
display: flex;
justify-content: space-between;
padding: var(--Spacing-x1) 0;
}
.name {
display: flex;
gap: var(--Spacing-x1);
align-items: center;
}
.payment {
display: flex;
gap: var(--Spacing-x2);
align-items: center;
}
.footer {
display: flex;
justify-content: space-between;
padding: var(--Spacing-x1) 0;
}
.comment {
display: flex;
gap: var(--Spacing-x-one-and-half);
}
.actions {
display: flex;
gap: var(--Spacing-x2);
}
@@ -0,0 +1,166 @@
import { useIntl } from "react-intl"
import { CheckCircleIcon, DeleteIcon, EditIcon } from "@/components/Icons"
import Accordion from "@/components/TempDesignSystem/Accordion"
import AccordionItem from "@/components/TempDesignSystem/Accordion/AccordionItem"
import Button from "@/components/TempDesignSystem/Button"
import Divider from "@/components/TempDesignSystem/Divider"
import Body from "@/components/TempDesignSystem/Text/Body"
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
import styles from "./addedAncillaries.module.css"
import type { AddedAncillariesProps } from "@/types/components/myPages/myStay/ancillaries"
export function AddedAncillaries({
ancillaries,
booking,
}: AddedAncillariesProps) {
const intl = useIntl()
return (
<div className={styles.container}>
<div className={styles.header}>
<Subtitle>{intl.formatMessage({ id: "My Add-on's" })}</Subtitle>
{booking.ancillary?.deliveryTime && (
<div className={styles.deliveryTime}>
<Body color="baseTextHighContrast" textTransform="bold">
{intl.formatMessage({ id: "Delivered at:" })}
</Body>
<Body color="baseTextHighContrast" textTransform="bold">
{booking.ancillary?.deliveryTime}
</Body>
</div>
)}
</div>
{booking.ancillaries.map((ancillary) => {
const ancillaryItem = ancillaries?.find((a) => a.id === ancillary.code)
return (
<>
<Accordion className={styles.ancillaryMobile}>
<AccordionItem
title={ancillaryItem?.title ?? ""}
icon={<CheckCircleIcon color="uiSemanticSuccess" />}
>
<div>
{ancillary.comment && (
<>
<div className={styles.commentMobile}>
<Body textTransform="bold">
{intl.formatMessage({ id: "Other requests" })}:
</Body>
<Body color="uiTextMediumContrast">
{ancillary.comment}
</Body>
</div>
</>
)}
<div className={styles.paymentMobileWrapper}>
<div className={styles.paymentMobile}>
<Body>{intl.formatMessage({ id: "Total" })}</Body>
<Body textTransform="bold">
{`${ancillary.totalPrice} ${ancillary.currency}`}
</Body>
<Divider
variant="vertical"
color="baseSurfaceSubtleNormal"
/>
<Body textTransform="bold">
{`${ancillary.points} ${intl.formatMessage({ id: "Points" })}`}
</Body>
</div>
</div>
</div>
<div className={styles.footerMobile}>
<div className={styles.actions}>
<Button
intent="text"
size="small"
variant="icon"
theme="base"
>
<EditIcon />
{intl.formatMessage({ id: "Modify" })}
</Button>
<Divider
variant="vertical"
color="baseSurfaceSubtleNormal"
/>
<Button
intent="text"
size="small"
variant="icon"
theme="base"
>
<DeleteIcon />
{intl.formatMessage({ id: "Remove" })}
</Button>
</div>
</div>
</AccordionItem>
</Accordion>
<div className={styles.ancillaryDesktop}>
<div className={styles.specification}>
<div className={styles.name}>
<CheckCircleIcon color="uiSemanticSuccess" />
<Body textTransform="bold">{ancillaryItem?.title}</Body>
<Body textTransform="bold">{`X${ancillary.totalUnit}`}</Body>
</div>
<div className={styles.payment}>
<Body>{intl.formatMessage({ id: "Total" })}</Body>
<Body textTransform="bold">
{`${ancillary.totalPrice} ${ancillary.currency}`}
</Body>
<Divider variant="vertical" color="baseSurfaceSubtleNormal" />
<Body textTransform="bold">
{`${ancillary.points} ${intl.formatMessage({ id: "Points" })}`}
</Body>
</div>
</div>
<Divider color="baseSurfaceSubtleHover" />
<div className={styles.footer}>
<div className={styles.comment}>
{ancillary.comment && (
<>
<Body textTransform="bold">
{intl.formatMessage({ id: "Other requests" })}:
</Body>
<Body>{ancillary.comment}</Body>
</>
)}
</div>
<div className={styles.actions}>
<Button
intent="text"
size="small"
variant="icon"
theme="base"
>
<EditIcon />
{intl.formatMessage({ id: "Modify" })}
</Button>
<Divider variant="vertical" color="baseSurfaceSubtleNormal" />
<Button
intent="text"
size="small"
variant="icon"
theme="base"
>
<DeleteIcon />
{intl.formatMessage({ id: "Remove" })}
</Button>
</div>
</div>
</div>
</>
)
})}
</div>
)
}
@@ -9,6 +9,7 @@ import { AncillaryCard } from "@/components/TempDesignSystem/AncillaryCard"
import Title from "@/components/TempDesignSystem/Text/Title"
import AddAncillaryFlowModal from "./AddAncillaryFlow/AddAncillaryFlowModal"
import { AddedAncillaries } from "./AddedAncillaries"
import AncillaryGridModal from "./AncillaryGridModal"
import styles from "./ancillaries.module.css"
@@ -115,6 +116,8 @@ export function Ancillaries({ ancillaries, booking, user }: AncillariesProps) {
</Carousel>
</div>
<AddedAncillaries booking={booking} ancillaries={allAncillaries} />
<AddAncillaryFlowModal
user={user}
isOpen={isModalOpen}
@@ -13,7 +13,7 @@ export default function Accessibility({
return (
<AccordionItem
title={intl.formatMessage({ id: "Accessibility" })}
icon={IconName.Accessibility}
iconName={IconName.Accessibility}
variant="sidepeek"
>
<Body>{elevatorPitchText}</Body>
@@ -12,7 +12,7 @@ export default function CheckinCheckOut({ checkin }: CheckInCheckOutProps) {
return (
<AccordionItem
title={intl.formatMessage({ id: "Check-in/Check-out" })}
icon={IconName.Calendar}
iconName={IconName.Calendar}
variant="sidepeek"
>
<Body textTransform="bold">{intl.formatMessage({ id: "Hours" })}</Body>
@@ -13,7 +13,7 @@ export default function MeetingsAndConferences({
return (
<AccordionItem
title={intl.formatMessage({ id: "Meetings & Conferences" })}
icon={IconName.Business}
iconName={IconName.Business}
variant="sidepeek"
>
<Body>{meetingDescription}</Body>
@@ -15,7 +15,7 @@ export default function Parking({ parking }: ParkingProps) {
return (
<AccordionItem
title={intl.formatMessage({ id: "Parking" })}
icon={IconName.Parking}
iconName={IconName.Parking}
className={styles.parking}
variant="sidepeek"
>
@@ -19,7 +19,7 @@ export default function Restaurant({
},
{ totalRestaurants: 1 }
)}
icon={IconName.Restaurant}
iconName={IconName.Restaurant}
variant="sidepeek"
>
<Body>{restaurantsContentDescriptionMedium}</Body>
@@ -1,4 +1,5 @@
import type { VariantProps } from "class-variance-authority"
import type { ReactNode } from "react"
import type { IconName } from "@/types/components/icon"
import type { accordionItemVariants } from "./variants"
@@ -7,7 +8,8 @@ export interface AccordionItemProps
extends React.HtmlHTMLAttributes<HTMLDetailsElement>,
VariantProps<typeof accordionItemVariants> {
title: string
icon?: IconName
iconName?: IconName
icon?: ReactNode
trackingId?: string
subtitle?: string
}
@@ -17,6 +17,7 @@ import type { AccordionItemProps } from "./accordionItem"
export default function AccordionItem({
children,
icon,
iconName,
title,
theme,
variant,
@@ -27,7 +28,10 @@ export default function AccordionItem({
const contentRef = useRef<HTMLDivElement>(null)
const detailsRef = useRef<HTMLDetailsElement>(null)
const IconComp = getIconByIconName(icon)
const FoundIcon = getIconByIconName(iconName)
const IconComp = icon
? icon
: FoundIcon && <FoundIcon color="baseTextHighcontrast" />
function toggleAccordion() {
const details = detailsRef.current
@@ -56,7 +60,7 @@ export default function AccordionItem({
<li className={accordionItemVariants({ className, variant, theme })}>
<details ref={detailsRef} onToggle={toggleAccordion}>
<summary className={styles.summary}>
{IconComp && <IconComp color="baseTextHighcontrast" />}
{IconComp}
{variant === "sidepeek" ? (
<Subtitle
className={styles.title}