feat(SW-415): Added Image gallery

This commit is contained in:
Pontus Dreij
2024-10-11 14:45:52 +02:00
parent 79a01ae699
commit f63cecc488
9 changed files with 128 additions and 27 deletions

View File

@@ -4,7 +4,13 @@ import { useIntl } from "react-intl"
import { RateDefinition } from "@/server/routers/hotels/output" import { RateDefinition } from "@/server/routers/hotels/output"
import FlexibilityOption from "@/components/HotelReservation/SelectRate/RoomSelection/FlexibilityOption" import FlexibilityOption from "@/components/HotelReservation/SelectRate/RoomSelection/FlexibilityOption"
import { ChevronRightSmallIcon } from "@/components/Icons" import {
ChevronRightSmallIcon,
GalleryIcon,
ImageIcon,
} from "@/components/Icons"
import Image from "@/components/Image"
import Lightbox from "@/components/Lightbox"
import Button from "@/components/TempDesignSystem/Button" import Button from "@/components/TempDesignSystem/Button"
import Body from "@/components/TempDesignSystem/Text/Body" import Body from "@/components/TempDesignSystem/Text/Body"
import Caption from "@/components/TempDesignSystem/Text/Caption" import Caption from "@/components/TempDesignSystem/Text/Caption"
@@ -53,17 +59,22 @@ export default function RoomCard({
} }
const roomSize = roomCategories.find( const roomSize = roomCategories.find(
(category) => category.name === roomConfiguration.roomType (room) => room.name === roomConfiguration.roomType
)?.roomSize )?.roomSize
const occupancy = roomCategories.find( const occupancy = roomCategories.find(
(category) => category.name === roomConfiguration.roomType (room) => room.name === roomConfiguration.roomType
)?.occupancy.total )?.occupancy.total
const roomDescription = roomCategories.find( const roomDescription = roomCategories.find(
(room) => room.name === roomConfiguration.roomType (room) => room.name === roomConfiguration.roomType
)?.descriptions.short )?.descriptions.short
const images = roomCategories.find(
(room) => room.name === roomConfiguration.roomType
)?.images
const mainImage = images?.[0]
return ( return (
<div className={styles.card}> <div className={styles.card}>
<div className={styles.cardBody}> <div className={styles.cardBody}>
@@ -78,7 +89,10 @@ export default function RoomCard({
)} )}
</Caption> </Caption>
<Caption color="uiTextMediumContrast"> <Caption color="uiTextMediumContrast">
{roomSize?.min}-{roomSize?.max} m² {roomSize?.min === roomSize?.max
? roomSize?.min
: `${roomSize?.min}-${roomSize?.max}`}
m²
</Caption> </Caption>
<Button <Button
intent="text" intent="text"
@@ -103,7 +117,7 @@ export default function RoomCard({
id: "Breakfast selection in next step.", id: "Breakfast selection in next step.",
})} })}
</Caption> </Caption>
<div> <div className={styles.flexibilityOptions}>
<FlexibilityOption <FlexibilityOption
name={intl.formatMessage({ id: "Non-refundable" })} name={intl.formatMessage({ id: "Non-refundable" })}
value="non-refundable" value="non-refundable"
@@ -128,22 +142,41 @@ export default function RoomCard({
</div> </div>
</div> </div>
</div> </div>
{mainImage && (
<div className={styles.imageContainer}> <div className={styles.imageContainer}>
<span className={styles.roomsLeft}> {roomConfiguration.roomsLeft < 5 && (
<Footnote <span className={styles.roomsLeft}>
color="burgundy" <Footnote
textTransform="uppercase" color="burgundy"
>{`${roomConfiguration.roomsLeft} ${intl.formatMessage({ id: "Left" })}`}</Footnote> textTransform="uppercase"
</span> >{`${roomConfiguration.roomsLeft} ${intl.formatMessage({ id: "Left" })}`}</Footnote>
{/* TODO: maybe use the `Image` component instead of the `img` tag. Waiting until we know how to get the image */} </span>
{/* eslint-disable-next-line @next/next/no-img-element */} )}
<img {/*NOTE: images from the test API are hosted on test3.scandichotels.com,
alt={intl.formatMessage({ id: "A photo of the room" })} which can't be accessed unless on Scandic's Wifi or using Citrix. */}
// TODO: Correct image URL <Image
src="https://www.scandichotels.se/imageVault/publishedmedia/xnmqnmz6mz0uhuat0917/scandic-helsinki-hub-room-standard-KR-7.jpg" src={mainImage.imageSizes.small}
/> alt={mainImage.metaData.altText}
</div> width={330}
height={185}
/>
{images && (
<Lightbox
images={images.map((image) => ({
url: image.imageSizes.small,
alt: image.metaData.altText,
title: image.metaData.title,
}))}
dialogTitle={roomConfiguration.roomType}
>
<div className={styles.galleryIcon} id="lightboxTrigger">
<GalleryIcon color="white" />
<Footnote color="white">{images.length}</Footnote>
</div>
</Lightbox>
)}
</div>
)}
</div> </div>
) )
} }

View File

@@ -44,6 +44,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: var(--Spacing-x1); gap: var(--Spacing-x1);
margin-bottom: var(--Spacing-x2);
} }
.name { .name {
@@ -57,6 +58,12 @@
border-radius: var(--Corner-radius-Medium) var(--Corner-radius-Medium) 0 0; border-radius: var(--Corner-radius-Medium) var(--Corner-radius-Medium) 0 0;
} }
.flexibilityOptions {
display: flex;
flex-direction: column;
gap: var(--Spacing-x2);
}
.roomsLeft { .roomsLeft {
position: absolute; position: absolute;
top: 12px; top: 12px;
@@ -65,3 +72,22 @@
padding: var(--Spacing-x-half) var(--Spacing-x1); padding: var(--Spacing-x-half) var(--Spacing-x1);
border-radius: var(--Corner-radius-Small); border-radius: var(--Corner-radius-Small);
} }
.imageContainer {
min-height: 185px;
position: relative;
}
.galleryIcon {
position: absolute;
bottom: 16px;
right: 16px;
height: 24px;
background-color: rgba(64, 57, 55, 0.9);
padding: 0 var(--Spacing-x-half);
border-radius: var(--Corner-radius-Small);
cursor: pointer;
display: flex;
align-items: center;
gap: var(--Spacing-x-quarter);
}

View File

@@ -1,8 +1,5 @@
"use client" "use client"
import { useRouter, useSearchParams } from "next/navigation" import { useRouter, useSearchParams } from "next/navigation"
import { useIntl } from "react-intl"
import Button from "@/components/TempDesignSystem/Button"
import RoomCard from "./RoomCard" import RoomCard from "./RoomCard"
@@ -16,7 +13,6 @@ export default function RoomSelection({
}: RoomSelectionProps) { }: RoomSelectionProps) {
const router = useRouter() const router = useRouter()
const searchParams = useSearchParams() const searchParams = useSearchParams()
const intl = useIntl()
function handleSubmit(e: React.FormEvent<HTMLFormElement>) { function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault() e.preventDefault()
@@ -44,12 +40,12 @@ export default function RoomSelection({
</li> </li>
))} ))}
</ul> </ul>
<div className={styles.summary}> {/* <div className={styles.summary}>
This is summary This is summary
<Button type="submit" size="small" theme="primaryDark"> <Button type="submit" size="small" theme="primaryDark">
{intl.formatMessage({ id: "Choose room" })} {intl.formatMessage({ id: "Choose room" })}
</Button> </Button>
</div> </div> */}
</form> </form>
</div> </div>
) )

View File

@@ -0,0 +1,36 @@
import { iconVariants } from "./variants"
import type { IconProps } from "@/types/components/icon"
export default function GalleryIcon({ className, color, ...props }: IconProps) {
const classNames = iconVariants({ className, color })
return (
<svg
className={classNames}
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
{...props}
>
<mask
id="mask0_69_3274"
style={{ maskType: "alpha" }}
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="24"
height="24"
>
<rect width="24" height="24" fill="#D9D9D9" />
</mask>
<g mask="url(#mask0_5887_18142)">
<path
d="M9.15 13.9H18.65L15.3725 9.625L13.1875 12.475L11.715 10.575L9.15 13.9ZM8.2 17.7C7.6775 17.7 7.23021 17.514 6.85813 17.1419C6.48604 16.7698 6.3 16.3225 6.3 15.8V4.4C6.3 3.8775 6.48604 3.43021 6.85813 3.05813C7.23021 2.68604 7.6775 2.5 8.2 2.5H19.6C20.1225 2.5 20.5698 2.68604 20.9419 3.05813C21.314 3.43021 21.5 3.8775 21.5 4.4V15.8C21.5 16.3225 21.314 16.7698 20.9419 17.1419C20.5698 17.514 20.1225 17.7 19.6 17.7H8.2ZM8.2 15.8H19.6V4.4H8.2V15.8ZM4.4 21.5C3.8775 21.5 3.43021 21.314 3.05813 20.9419C2.68604 20.5698 2.5 20.1225 2.5 19.6V6.3H4.4V19.6H17.7V21.5H4.4Z"
fill="white"
/>
</g>
</svg>
)
}

View File

@@ -32,6 +32,7 @@ import {
EyeHideIcon, EyeHideIcon,
EyeShowIcon, EyeShowIcon,
FitnessIcon, FitnessIcon,
GalleryIcon,
GiftIcon, GiftIcon,
GlobeIcon, GlobeIcon,
HouseIcon, HouseIcon,
@@ -124,6 +125,8 @@ export function getIconByIconName(icon?: IconName): FC<IconProps> | null {
return FacebookIcon return FacebookIcon
case IconName.Fitness: case IconName.Fitness:
return FitnessIcon return FitnessIcon
case IconName.Gallery:
return GalleryIcon
case IconName.Gift: case IconName.Gift:
return GiftIcon return GiftIcon
case IconName.Globe: case IconName.Globe:

View File

@@ -31,6 +31,7 @@ export { default as ErrorCircleIcon } from "./ErrorCircle"
export { default as EyeHideIcon } from "./EyeHide" export { default as EyeHideIcon } from "./EyeHide"
export { default as EyeShowIcon } from "./EyeShow" export { default as EyeShowIcon } from "./EyeShow"
export { default as FitnessIcon } from "./Fitness" export { default as FitnessIcon } from "./Fitness"
export { default as GalleryIcon } from "./Gallery"
export { default as GiftIcon } from "./Gift" export { default as GiftIcon } from "./Gift"
export { default as GlobeIcon } from "./Globe" export { default as GlobeIcon } from "./Globe"
export { default as HeartIcon } from "./Heart" export { default as HeartIcon } from "./Heart"

View File

@@ -66,3 +66,7 @@
.uiTextPlaceholder { .uiTextPlaceholder {
color: var(--UI-Text-Placeholder); color: var(--UI-Text-Placeholder);
} }
.white {
color: var(--Main-Grey-White);
}

View File

@@ -11,6 +11,7 @@ const config = {
peach50: styles.peach50, peach50: styles.peach50,
uiTextMediumContrast: styles.uiTextMediumContrast, uiTextMediumContrast: styles.uiTextMediumContrast,
uiTextPlaceholder: styles.uiTextPlaceholder, uiTextPlaceholder: styles.uiTextPlaceholder,
white: styles.white,
}, },
textAlign: { textAlign: {
center: styles.center, center: styles.center,

View File

@@ -36,6 +36,7 @@ export enum IconName {
EyeShow = "EyeShow", EyeShow = "EyeShow",
Facebook = "Facebook", Facebook = "Facebook",
Fitness = "Fitness", Fitness = "Fitness",
Gallery = "Gallery",
Gift = "Gift", Gift = "Gift",
Globe = "Globe", Globe = "Globe",
House = "House", House = "House",