reafactor: decouple SidePeek from URL params
This commit is contained in:
@@ -1,10 +1,8 @@
|
|||||||
import { Lang } from "@/constants/languages"
|
|
||||||
import { amenities } from "@/constants/routes/hotelPageParams"
|
import { amenities } from "@/constants/routes/hotelPageParams"
|
||||||
|
|
||||||
import { mapFacilityToIcon } from "@/components/ContentType/HotelPage/data"
|
import { mapFacilityToIcon } from "@/components/ContentType/HotelPage/data"
|
||||||
import { ChevronRightIcon } from "@/components/Icons"
|
import { ChevronRightIcon } from "@/components/Icons"
|
||||||
import Link from "@/components/TempDesignSystem/Link"
|
import Link from "@/components/TempDesignSystem/Link"
|
||||||
import { generateSidePeekLink } from "@/components/TempDesignSystem/SidePeek/data"
|
|
||||||
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"
|
||||||
import { getIntl } from "@/i18n"
|
import { getIntl } from "@/i18n"
|
||||||
@@ -12,21 +10,18 @@ import { getIntl } from "@/i18n"
|
|||||||
import styles from "./amenitiesList.module.css"
|
import styles from "./amenitiesList.module.css"
|
||||||
|
|
||||||
import { HotelData } from "@/types/hotel"
|
import { HotelData } from "@/types/hotel"
|
||||||
|
import { getLang } from "@/i18n/serverContext"
|
||||||
|
|
||||||
export default async function AmenitiesList({
|
export default async function AmenitiesList({
|
||||||
detailedFacilities,
|
detailedFacilities,
|
||||||
// TODO: remove prop drilling once getLang() is merged
|
|
||||||
lang,
|
|
||||||
}: {
|
}: {
|
||||||
detailedFacilities: HotelData["data"]["attributes"]["detailedFacilities"]
|
detailedFacilities: HotelData["data"]["attributes"]["detailedFacilities"]
|
||||||
lang: Lang
|
|
||||||
}) {
|
}) {
|
||||||
const { formatMessage } = await getIntl()
|
const { formatMessage } = await getIntl()
|
||||||
const sidePeekLink = generateSidePeekLink(amenities[lang])
|
|
||||||
const sortedAmenities = detailedFacilities
|
const sortedAmenities = detailedFacilities
|
||||||
.sort((a, b) => b.sortOrder - a.sortOrder)
|
.sort((a, b) => b.sortOrder - a.sortOrder)
|
||||||
.slice(0, 5)
|
.slice(0, 5)
|
||||||
|
const lang = getLang()
|
||||||
return (
|
return (
|
||||||
<section className={styles.amenitiesContainer}>
|
<section className={styles.amenitiesContainer}>
|
||||||
<Subtitle type="two" color="black">
|
<Subtitle type="two" color="black">
|
||||||
@@ -43,7 +38,12 @@ export default async function AmenitiesList({
|
|||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<Link scroll={false} href={sidePeekLink} color="burgundy" variant="icon">
|
<Link
|
||||||
|
scroll={false}
|
||||||
|
href={`?s=${amenities[lang]}`}
|
||||||
|
color="burgundy"
|
||||||
|
variant="icon"
|
||||||
|
>
|
||||||
{formatMessage({ id: "Show all amenities" })}
|
{formatMessage({ id: "Show all amenities" })}
|
||||||
<ChevronRightIcon />
|
<ChevronRightIcon />
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
import { about, amenities } from "@/constants/routes/hotelPageParams"
|
|
||||||
import { serverClient } from "@/lib/trpc/server"
|
import { serverClient } from "@/lib/trpc/server"
|
||||||
|
|
||||||
import { getLang } from "@/i18n/serverContext"
|
import { getLang } from "@/i18n/serverContext"
|
||||||
import SidePeek from "@/components/TempDesignSystem/SidePeek"
|
|
||||||
import SidePeekItem from "@/components/TempDesignSystem/SidePeek/Item"
|
|
||||||
import { getIntl } from "@/i18n"
|
|
||||||
|
|
||||||
import AmenitiesList from "./AmenitiesList"
|
import AmenitiesList from "./AmenitiesList"
|
||||||
import IntroSection from "./IntroSection"
|
import IntroSection from "./IntroSection"
|
||||||
import { Rooms } from "./Rooms"
|
import { Rooms } from "./Rooms"
|
||||||
|
import SidePeeks from "./SidePeeks"
|
||||||
import TabNavigation from "./TabNavigation"
|
import TabNavigation from "./TabNavigation"
|
||||||
|
|
||||||
import styles from "./hotelPage.module.css"
|
import styles from "./hotelPage.module.css"
|
||||||
@@ -27,8 +24,6 @@ export default async function HotelPage() {
|
|||||||
include: ["RoomCategories"],
|
include: ["RoomCategories"],
|
||||||
})
|
})
|
||||||
|
|
||||||
const { formatMessage } = await getIntl()
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.pageContainer}>
|
<div className={styles.pageContainer}>
|
||||||
<TabNavigation />
|
<TabNavigation />
|
||||||
@@ -41,25 +36,8 @@ export default async function HotelPage() {
|
|||||||
address={attributes.address}
|
address={attributes.address}
|
||||||
tripAdvisor={attributes.ratings.tripAdvisor}
|
tripAdvisor={attributes.ratings.tripAdvisor}
|
||||||
/>
|
/>
|
||||||
<SidePeek>
|
<SidePeeks />
|
||||||
<SidePeekItem
|
<AmenitiesList detailedFacilities={attributes.detailedFacilities} />
|
||||||
contentKey={amenities[lang]}
|
|
||||||
title={formatMessage({ id: "Amenities" })}
|
|
||||||
>
|
|
||||||
{/* TODO: Render amenities as per the design. */}
|
|
||||||
Read more about the amenities here
|
|
||||||
</SidePeekItem>
|
|
||||||
<SidePeekItem
|
|
||||||
contentKey={about[lang]}
|
|
||||||
title={formatMessage({ id: "About the hotel" })}
|
|
||||||
>
|
|
||||||
Some additional information about the hotel
|
|
||||||
</SidePeekItem>
|
|
||||||
</SidePeek>
|
|
||||||
<AmenitiesList
|
|
||||||
detailedFacilities={attributes.detailedFacilities}
|
|
||||||
lang={lang}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<Rooms rooms={roomCategories} />
|
<Rooms rooms={roomCategories} />
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { about } from "@/constants/routes/hotelPageParams"
|
|||||||
import ArrowRight from "@/components/Icons/ArrowRight"
|
import ArrowRight from "@/components/Icons/ArrowRight"
|
||||||
import TripAdvisorIcon from "@/components/Icons/TripAdvisor"
|
import TripAdvisorIcon from "@/components/Icons/TripAdvisor"
|
||||||
import Link from "@/components/TempDesignSystem/Link"
|
import Link from "@/components/TempDesignSystem/Link"
|
||||||
import { generateSidePeekLink } from "@/components/TempDesignSystem/SidePeek/data"
|
|
||||||
import BiroScript from "@/components/TempDesignSystem/Text/BiroScript"
|
import BiroScript from "@/components/TempDesignSystem/Text/BiroScript"
|
||||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||||
import Preamble from "@/components/TempDesignSystem/Text/Preamble"
|
import Preamble from "@/components/TempDesignSystem/Text/Preamble"
|
||||||
@@ -35,7 +34,6 @@ export default async function IntroSection({
|
|||||||
{ id: "Tripadvisor reviews" },
|
{ id: "Tripadvisor reviews" },
|
||||||
{ rating: tripAdvisor.rating, count: tripAdvisor.numberOfReviews }
|
{ rating: tripAdvisor.rating, count: tripAdvisor.numberOfReviews }
|
||||||
)
|
)
|
||||||
const aboutTheHotelLink = generateSidePeekLink(about[lang])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={styles.introSection}>
|
<section className={styles.introSection}>
|
||||||
@@ -66,7 +64,7 @@ export default async function IntroSection({
|
|||||||
color="peach80"
|
color="peach80"
|
||||||
textDecoration="underline"
|
textDecoration="underline"
|
||||||
variant="icon"
|
variant="icon"
|
||||||
href={aboutTheHotelLink}
|
href={`?s=${about[lang]}`}
|
||||||
>
|
>
|
||||||
{formatMessage({ id: "Read more about the hotel" })}
|
{formatMessage({ id: "Read more about the hotel" })}
|
||||||
<ArrowRight color="peach80" />
|
<ArrowRight color="peach80" />
|
||||||
|
|||||||
63
components/ContentType/HotelPage/SidePeeks.tsx
Normal file
63
components/ContentType/HotelPage/SidePeeks.tsx
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { usePathname, useRouter, useSearchParams } from "next/navigation"
|
||||||
|
import { useEffect, useState } from "react"
|
||||||
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
|
import { about, amenities } from "@/constants/routes/hotelPageParams"
|
||||||
|
|
||||||
|
import SidePeek from "@/components/TempDesignSystem/SidePeek"
|
||||||
|
import SidePeekItem from "@/components/TempDesignSystem/SidePeek/Item"
|
||||||
|
import { SidePeekContentKey } from "@/components/TempDesignSystem/SidePeek/types"
|
||||||
|
import useLang from "@/hooks/useLang"
|
||||||
|
|
||||||
|
function SidePeekContainer() {
|
||||||
|
const router = useRouter()
|
||||||
|
const pathname = usePathname()
|
||||||
|
const searchParams = useSearchParams()
|
||||||
|
const [activeSidePeek, setActiveSidePeek] =
|
||||||
|
useState<SidePeekContentKey | null>(() => {
|
||||||
|
const sidePeekParam = searchParams.get("s") as SidePeekContentKey | null
|
||||||
|
return sidePeekParam || null
|
||||||
|
})
|
||||||
|
|
||||||
|
const { formatMessage } = useIntl()
|
||||||
|
const lang = useLang()
|
||||||
|
useEffect(() => {
|
||||||
|
const sidePeekParam = searchParams.get("s") as SidePeekContentKey | null
|
||||||
|
if (sidePeekParam !== activeSidePeek) {
|
||||||
|
setActiveSidePeek(sidePeekParam)
|
||||||
|
}
|
||||||
|
}, [searchParams, activeSidePeek])
|
||||||
|
|
||||||
|
function handleClose(isOpen: boolean) {
|
||||||
|
if (!isOpen) {
|
||||||
|
setActiveSidePeek(null)
|
||||||
|
|
||||||
|
const nextSearchParams = new URLSearchParams(searchParams.toString())
|
||||||
|
nextSearchParams.delete("s")
|
||||||
|
|
||||||
|
router.push(`${pathname}?${nextSearchParams}`, { scroll: false })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SidePeek handleClose={handleClose} activeSidePeek={activeSidePeek}>
|
||||||
|
<SidePeekItem
|
||||||
|
contentKey={amenities[lang]}
|
||||||
|
title={formatMessage({ id: "Amenities" })}
|
||||||
|
>
|
||||||
|
{/* TODO: Render amenities as per the design. */}
|
||||||
|
Read more about the amenities here
|
||||||
|
</SidePeekItem>
|
||||||
|
<SidePeekItem
|
||||||
|
contentKey={about[lang]}
|
||||||
|
title={formatMessage({ id: "About the hotel" })}
|
||||||
|
>
|
||||||
|
Some additional information about the hotel
|
||||||
|
</SidePeekItem>
|
||||||
|
</SidePeek>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SidePeekContainer
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import { SidePeekContentKey } from "./types"
|
|
||||||
|
|
||||||
export const generateSidePeekLink = (key: SidePeekContentKey) => {
|
|
||||||
// what should the parameter be named to make sense in all use cases/languages? single caracter? like`s=`?
|
|
||||||
return `?open=${key}`
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useIsSSR } from "@react-aria/ssr"
|
import { useIsSSR } from "@react-aria/ssr"
|
||||||
import { usePathname, useRouter, useSearchParams } from "next/navigation"
|
import React, { Children, cloneElement } from "react"
|
||||||
import React, { Children, cloneElement, useEffect, useState } from "react"
|
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
@@ -14,39 +13,13 @@ import { SidePeekContentKey } from "@/components/TempDesignSystem/SidePeek/types
|
|||||||
|
|
||||||
import styles from "./sidePeek.module.css"
|
import styles from "./sidePeek.module.css"
|
||||||
|
|
||||||
function SidePeek({ children }: React.PropsWithChildren) {
|
import type { SidePeekProps } from "./sidePeek"
|
||||||
// TODO: to make this more reusable,
|
|
||||||
// should the param logic be moved up/out so this component can be
|
|
||||||
// controlled by e.g. URL segments
|
|
||||||
const router = useRouter()
|
|
||||||
const pathname = usePathname()
|
|
||||||
const searchParams = useSearchParams()
|
|
||||||
const [activeSidePeek, setActiveSidePeek] =
|
|
||||||
useState<SidePeekContentKey | null>(() => {
|
|
||||||
const sidePeekParam = searchParams.get(
|
|
||||||
"open"
|
|
||||||
) as SidePeekContentKey | null
|
|
||||||
return sidePeekParam || null
|
|
||||||
})
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const sidePeekParam = searchParams.get("open") as SidePeekContentKey | null
|
|
||||||
if (sidePeekParam !== activeSidePeek) {
|
|
||||||
setActiveSidePeek(sidePeekParam)
|
|
||||||
}
|
|
||||||
}, [searchParams, activeSidePeek])
|
|
||||||
|
|
||||||
function handleClose(isOpen: boolean) {
|
|
||||||
if (!isOpen) {
|
|
||||||
setActiveSidePeek(null)
|
|
||||||
|
|
||||||
const nextSearchParams = new URLSearchParams(searchParams.toString())
|
|
||||||
nextSearchParams.delete("open")
|
|
||||||
|
|
||||||
router.push(`${pathname}?${nextSearchParams}`, { scroll: false })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
function SidePeek({
|
||||||
|
children,
|
||||||
|
handleClose,
|
||||||
|
activeSidePeek,
|
||||||
|
}: React.PropsWithChildren<SidePeekProps>) {
|
||||||
const sidePeekChildren = Children.map(children, (child) => {
|
const sidePeekChildren = Children.map(children, (child) => {
|
||||||
if (!React.isValidElement(child)) {
|
if (!React.isValidElement(child)) {
|
||||||
return child
|
return child
|
||||||
@@ -77,4 +50,4 @@ function SidePeek({ children }: React.PropsWithChildren) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SidePeek
|
export default SidePeek
|
||||||
|
|||||||
4
components/TempDesignSystem/SidePeek/sidePeek.ts
Normal file
4
components/TempDesignSystem/SidePeek/sidePeek.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export interface SidePeekProps {
|
||||||
|
handleClose: (isOpen: boolean) => void
|
||||||
|
activeSidePeek: string | null
|
||||||
|
}
|
||||||
1712
package-lock.json
generated
1712
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user