feat(SW-713): add rooms sidepeek on hotelpage
This commit is contained in:
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import { ChevronRightIcon, ImageIcon } from "@/components/Icons"
|
import { ImageIcon } from "@/components/Icons"
|
||||||
import Image from "@/components/Image"
|
import Image from "@/components/Image"
|
||||||
import Button from "@/components/TempDesignSystem/Button"
|
import RoomSidePeek from "@/components/SidePeeks/RoomSidePeek"
|
||||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||||
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||||
|
|
||||||
@@ -12,26 +12,28 @@ import styles from "./roomCard.module.css"
|
|||||||
|
|
||||||
import type { RoomCardProps } from "@/types/components/hotelPage/room"
|
import type { RoomCardProps } from "@/types/components/hotelPage/room"
|
||||||
|
|
||||||
export function RoomCard({
|
export function RoomCard({ room }: RoomCardProps) {
|
||||||
badgeTextTransKey,
|
const { images, name, roomSize, occupancy, id } = room
|
||||||
id,
|
|
||||||
images,
|
|
||||||
subtitle,
|
|
||||||
title,
|
|
||||||
}: RoomCardProps) {
|
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const mainImage = images[0]
|
const mainImage = images[0]
|
||||||
|
|
||||||
|
const size =
|
||||||
|
roomSize?.min === roomSize?.max
|
||||||
|
? `${roomSize.min} m²`
|
||||||
|
: `${roomSize.min} - ${roomSize.max} m²`
|
||||||
|
|
||||||
|
const personLabel = intl.formatMessage(
|
||||||
|
{ id: "hotelPages.rooms.roomCard.persons" },
|
||||||
|
{ totalOccupancy: occupancy.total }
|
||||||
|
)
|
||||||
|
|
||||||
|
const subtitle = `${size} (${personLabel})`
|
||||||
|
|
||||||
function handleImageClick() {
|
function handleImageClick() {
|
||||||
// TODO: Implement opening of a model with carousel
|
// TODO: Implement opening of a model with carousel
|
||||||
console.log("Image clicked: ", id)
|
console.log("Image clicked: ", id)
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleRoomCtaClick() {
|
|
||||||
// TODO: Implement opening side-peek component with room details
|
|
||||||
console.log("Room CTA clicked: ", id)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<article className={styles.roomCard}>
|
<article className={styles.roomCard}>
|
||||||
<button className={styles.imageWrapper} onClick={handleImageClick}>
|
<button className={styles.imageWrapper} onClick={handleImageClick}>
|
||||||
@@ -64,19 +66,11 @@ export function RoomCard({
|
|||||||
color="black"
|
color="black"
|
||||||
className={styles.title}
|
className={styles.title}
|
||||||
>
|
>
|
||||||
{title}
|
{name}
|
||||||
</Subtitle>
|
</Subtitle>
|
||||||
<Body color="grey">{subtitle}</Body>
|
<Body color="grey">{subtitle}</Body>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<RoomSidePeek selectedRoom={room} buttonSize="medium" />
|
||||||
theme="base"
|
|
||||||
variant="icon"
|
|
||||||
intent="text"
|
|
||||||
onClick={handleRoomCtaClick}
|
|
||||||
>
|
|
||||||
{intl.formatMessage({ id: "See room details" })}
|
|
||||||
<ChevronRightIcon />
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -22,27 +22,6 @@ export function Rooms({ rooms }: RoomsProps) {
|
|||||||
|
|
||||||
const scrollRef = useRef<HTMLDivElement>(null)
|
const scrollRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
const mappedRooms = rooms
|
|
||||||
.map((room) => {
|
|
||||||
const size = `${room.roomSize.min} - ${room.roomSize.max} m²`
|
|
||||||
const personLabel =
|
|
||||||
room.occupancy.total === 1
|
|
||||||
? intl.formatMessage({ id: "hotelPages.rooms.roomCard.person" })
|
|
||||||
: intl.formatMessage({ id: "hotelPages.rooms.roomCard.persons" })
|
|
||||||
|
|
||||||
const subtitle = `${size} (${room.occupancy.total} ${personLabel})`
|
|
||||||
|
|
||||||
return {
|
|
||||||
id: room.id,
|
|
||||||
images: room.images,
|
|
||||||
title: room.name,
|
|
||||||
subtitle: subtitle,
|
|
||||||
sortOrder: room.sortOrder,
|
|
||||||
popularChoice: null,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.sort((a, b) => a.sortOrder - b.sortOrder)
|
|
||||||
|
|
||||||
function handleShowMore() {
|
function handleShowMore() {
|
||||||
if (scrollRef.current && allRoomsVisible) {
|
if (scrollRef.current && allRoomsVisible) {
|
||||||
scrollRef.current.scrollIntoView({ behavior: "smooth" })
|
scrollRef.current.scrollIntoView({ behavior: "smooth" })
|
||||||
@@ -64,15 +43,9 @@ export function Rooms({ rooms }: RoomsProps) {
|
|||||||
<Grids.Stackable
|
<Grids.Stackable
|
||||||
className={`${styles.grid} ${allRoomsVisible ? styles.allVisible : ""}`}
|
className={`${styles.grid} ${allRoomsVisible ? styles.allVisible : ""}`}
|
||||||
>
|
>
|
||||||
{mappedRooms.map(({ id, images, title, subtitle, popularChoice }) => (
|
{rooms.map((room) => (
|
||||||
<div key={id}>
|
<div key={room.id}>
|
||||||
<RoomCard
|
<RoomCard room={room} />
|
||||||
id={id}
|
|
||||||
images={images}
|
|
||||||
title={title}
|
|
||||||
subtitle={subtitle}
|
|
||||||
badgeTextTransKey={popularChoice ? "Popular choice" : null}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</Grids.Stackable>
|
</Grids.Stackable>
|
||||||
|
|||||||
@@ -78,10 +78,7 @@ export default function RoomCard({
|
|||||||
: `${roomSize?.min}-${roomSize?.max}`}
|
: `${roomSize?.min}-${roomSize?.max}`}
|
||||||
m²
|
m²
|
||||||
</Caption>
|
</Caption>
|
||||||
<RoomSidePeek
|
<RoomSidePeek selectedRoom={selectedRoom} buttonSize="small" />
|
||||||
selectedRoom={selectedRoom}
|
|
||||||
roomConfiguration={roomConfiguration}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<div className={styles.roomDetails}>
|
<div className={styles.roomDetails}>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import type { RoomSidePeekProps } from "@/types/components/hotelReservation/sele
|
|||||||
|
|
||||||
export default function RoomSidePeek({
|
export default function RoomSidePeek({
|
||||||
selectedRoom,
|
selectedRoom,
|
||||||
roomConfiguration,
|
buttonSize,
|
||||||
}: RoomSidePeekProps) {
|
}: RoomSidePeekProps) {
|
||||||
const [isSidePeekOpen, setIsSidePeekOpen] = useState(false)
|
const [isSidePeekOpen, setIsSidePeekOpen] = useState(false)
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
@@ -31,7 +31,7 @@ export default function RoomSidePeek({
|
|||||||
<Button
|
<Button
|
||||||
intent="text"
|
intent="text"
|
||||||
type="button"
|
type="button"
|
||||||
size="small"
|
size={buttonSize}
|
||||||
theme="base"
|
theme="base"
|
||||||
className={styles.button}
|
className={styles.button}
|
||||||
onClick={() => setIsSidePeekOpen(true)}
|
onClick={() => setIsSidePeekOpen(true)}
|
||||||
@@ -41,7 +41,7 @@ export default function RoomSidePeek({
|
|||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<SidePeek
|
<SidePeek
|
||||||
title={roomConfiguration.roomType}
|
title={selectedRoom?.name ?? ""}
|
||||||
isOpen={isSidePeekOpen}
|
isOpen={isSidePeekOpen}
|
||||||
handleClose={() => setIsSidePeekOpen(false)}
|
handleClose={() => setIsSidePeekOpen(false)}
|
||||||
>
|
>
|
||||||
@@ -51,16 +51,14 @@ export default function RoomSidePeek({
|
|||||||
: `${roomSize?.min} - ${roomSize?.max}`}
|
: `${roomSize?.min} - ${roomSize?.max}`}
|
||||||
m².{" "}
|
m².{" "}
|
||||||
{intl.formatMessage(
|
{intl.formatMessage(
|
||||||
{
|
{ id: "booking.accommodatesUpTo" },
|
||||||
id: "booking.accommodatesUpTo",
|
|
||||||
},
|
|
||||||
{ nrOfGuests: occupancy }
|
{ nrOfGuests: occupancy }
|
||||||
)}
|
)}
|
||||||
</Body>
|
</Body>
|
||||||
|
|
||||||
{images && (
|
{images && (
|
||||||
<div className={styles.imageContainer}>
|
<div className={styles.imageContainer}>
|
||||||
<ImageGallery images={images} title={roomConfiguration.roomType} />
|
<ImageGallery images={images} title={selectedRoom.name} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -397,8 +397,7 @@
|
|||||||
"guaranteeing": "garanti",
|
"guaranteeing": "garanti",
|
||||||
"guest": "gæst",
|
"guest": "gæst",
|
||||||
"guests": "gæster",
|
"guests": "gæster",
|
||||||
"hotelPages.rooms.roomCard.person": "person",
|
"hotelPages.rooms.roomCard.persons": "{totalOccupancy, plural, one {# person} other {# personer}}",
|
||||||
"hotelPages.rooms.roomCard.persons": "personer",
|
|
||||||
"hotelPages.rooms.roomCard.seeRoomDetails": "Se værelsesdetaljer",
|
"hotelPages.rooms.roomCard.seeRoomDetails": "Se værelsesdetaljer",
|
||||||
"km to city center": "km til byens centrum",
|
"km to city center": "km til byens centrum",
|
||||||
"lowercase letter": "lille bogstav",
|
"lowercase letter": "lille bogstav",
|
||||||
|
|||||||
@@ -396,8 +396,7 @@
|
|||||||
"guaranteeing": "garantiert",
|
"guaranteeing": "garantiert",
|
||||||
"guest": "gast",
|
"guest": "gast",
|
||||||
"guests": "gäste",
|
"guests": "gäste",
|
||||||
"hotelPages.rooms.roomCard.person": "person",
|
"hotelPages.rooms.roomCard.persons": "{totalOccupancy, plural, one {# person} other {# personen}}",
|
||||||
"hotelPages.rooms.roomCard.persons": "personen",
|
|
||||||
"hotelPages.rooms.roomCard.seeRoomDetails": "Zimmerdetails ansehen",
|
"hotelPages.rooms.roomCard.seeRoomDetails": "Zimmerdetails ansehen",
|
||||||
"km to city center": "km bis zum Stadtzentrum",
|
"km to city center": "km bis zum Stadtzentrum",
|
||||||
"lowercase letter": "Kleinbuchstabe",
|
"lowercase letter": "Kleinbuchstabe",
|
||||||
|
|||||||
@@ -416,8 +416,7 @@
|
|||||||
"guaranteeing": "guaranteeing",
|
"guaranteeing": "guaranteeing",
|
||||||
"guest": "guest",
|
"guest": "guest",
|
||||||
"guests": "guests",
|
"guests": "guests",
|
||||||
"hotelPages.rooms.roomCard.person": "person",
|
"hotelPages.rooms.roomCard.persons": "{totalOccupancy, plural, one {# person} other {# persons}}",
|
||||||
"hotelPages.rooms.roomCard.persons": "persons",
|
|
||||||
"hotelPages.rooms.roomCard.seeRoomDetails": "See room details",
|
"hotelPages.rooms.roomCard.seeRoomDetails": "See room details",
|
||||||
"km to city center": "km to city center",
|
"km to city center": "km to city center",
|
||||||
"lowercase letter": "lowercase letter",
|
"lowercase letter": "lowercase letter",
|
||||||
|
|||||||
@@ -396,8 +396,7 @@
|
|||||||
"guaranteeing": "varmistetaan",
|
"guaranteeing": "varmistetaan",
|
||||||
"guest": "Vieras",
|
"guest": "Vieras",
|
||||||
"guests": "Vieraita",
|
"guests": "Vieraita",
|
||||||
"hotelPages.rooms.roomCard.person": "henkilö",
|
"hotelPages.rooms.roomCard.persons": "{totalOccupancy, plural, one {# henkilö} other {# Henkilöä}}",
|
||||||
"hotelPages.rooms.roomCard.persons": "Henkilöä",
|
|
||||||
"hotelPages.rooms.roomCard.seeRoomDetails": "Katso huoneen tiedot",
|
"hotelPages.rooms.roomCard.seeRoomDetails": "Katso huoneen tiedot",
|
||||||
"km to city center": "km keskustaan",
|
"km to city center": "km keskustaan",
|
||||||
"lowercase letter": "pien kirjain",
|
"lowercase letter": "pien kirjain",
|
||||||
|
|||||||
@@ -394,8 +394,7 @@
|
|||||||
"guaranteeing": "garantiert",
|
"guaranteeing": "garantiert",
|
||||||
"guest": "gjest",
|
"guest": "gjest",
|
||||||
"guests": "gjester",
|
"guests": "gjester",
|
||||||
"hotelPages.rooms.roomCard.person": "person",
|
"hotelPages.rooms.roomCard.persons": "{totalOccupancy, plural, one {# person} other {# personer}}",
|
||||||
"hotelPages.rooms.roomCard.persons": "personer",
|
|
||||||
"hotelPages.rooms.roomCard.seeRoomDetails": "Se detaljer om rommet",
|
"hotelPages.rooms.roomCard.seeRoomDetails": "Se detaljer om rommet",
|
||||||
"km to city center": "km til sentrum",
|
"km to city center": "km til sentrum",
|
||||||
"lowercase letter": "liten bokstav",
|
"lowercase letter": "liten bokstav",
|
||||||
|
|||||||
@@ -395,8 +395,7 @@
|
|||||||
"guaranteeing": "garanterar",
|
"guaranteeing": "garanterar",
|
||||||
"guest": "gäst",
|
"guest": "gäst",
|
||||||
"guests": "gäster",
|
"guests": "gäster",
|
||||||
"hotelPages.rooms.roomCard.person": "person",
|
"hotelPages.rooms.roomCard.persons": "{totalOccupancy, plural, one {# person} other {# personer}}",
|
||||||
"hotelPages.rooms.roomCard.persons": "personer",
|
|
||||||
"hotelPages.rooms.roomCard.seeRoomDetails": "Se information om rummet",
|
"hotelPages.rooms.roomCard.seeRoomDetails": "Se information om rummet",
|
||||||
"km to city center": "km till stadens centrum",
|
"km to city center": "km till stadens centrum",
|
||||||
"lowercase letter": "liten bokstav",
|
"lowercase letter": "liten bokstav",
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
import type { RoomData } from "@/types/hotel"
|
import type { RoomData } from "@/types/hotel"
|
||||||
|
|
||||||
export interface RoomCardProps {
|
export interface RoomCardProps {
|
||||||
id: string
|
room: RoomData
|
||||||
images: RoomData["images"]
|
|
||||||
title: string
|
|
||||||
subtitle: string
|
|
||||||
badgeTextTransKey: string | null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RoomsProps = {
|
export type RoomsProps = {
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import { RoomConfiguration } from "@/server/routers/hotels/output"
|
|
||||||
|
|
||||||
import { RoomData } from "@/types/hotel"
|
import { RoomData } from "@/types/hotel"
|
||||||
|
|
||||||
export type RoomSidePeekProps = {
|
export type RoomSidePeekProps = {
|
||||||
roomConfiguration: RoomConfiguration
|
|
||||||
selectedRoom?: RoomData
|
selectedRoom?: RoomData
|
||||||
|
buttonSize: "small" | "medium"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user