feat: Add tab navigation to hotel page
This commit is contained in:
@@ -108,6 +108,7 @@ html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { serverClient } from "@/lib/trpc/server"
|
||||
import AmenitiesList from "./AmenitiesList"
|
||||
import IntroSection from "./IntroSection"
|
||||
import { Rooms } from "./Rooms"
|
||||
import TabNavigation from "./TabNavigation"
|
||||
|
||||
import styles from "./hotelPage.module.css"
|
||||
|
||||
@@ -23,18 +24,21 @@ export default async function HotelPage({ lang }: LangParams) {
|
||||
})
|
||||
|
||||
return (
|
||||
<main className={styles.pageContainer}>
|
||||
<div className={styles.introContainer}>
|
||||
<IntroSection
|
||||
hotelName={attributes.name}
|
||||
hotelDescription={attributes.hotelContent.texts.descriptions.short}
|
||||
location={attributes.location}
|
||||
address={attributes.address}
|
||||
tripAdvisor={attributes.ratings.tripAdvisor}
|
||||
/>
|
||||
<AmenitiesList detailedFacilities={attributes.detailedFacilities} />
|
||||
</div>
|
||||
<Rooms rooms={roomCategories} />
|
||||
</main>
|
||||
<div className={styles.pageContainer}>
|
||||
<TabNavigation />
|
||||
<main className={styles.mainSection}>
|
||||
<div className={styles.introContainer}>
|
||||
<IntroSection
|
||||
hotelName={attributes.name}
|
||||
hotelDescription={attributes.hotelContent.texts.descriptions.short}
|
||||
location={attributes.location}
|
||||
address={attributes.address}
|
||||
tripAdvisor={attributes.ratings.tripAdvisor}
|
||||
/>
|
||||
<AmenitiesList detailedFacilities={attributes.detailedFacilities} />
|
||||
</div>
|
||||
<Rooms rooms={roomCategories} />
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -30,10 +30,10 @@ export async function Rooms({ rooms }: RoomsProps) {
|
||||
.sort((a, b) => a.sortOrder - b.sortOrder)
|
||||
.slice(0, 3) //TODO: Remove this and render all rooms once we've implemented "show more" logic in SW-203.
|
||||
return (
|
||||
<SectionContainer>
|
||||
<SectionContainer id="rooms-section">
|
||||
<SectionHeader
|
||||
textTransform="uppercase"
|
||||
title={formatMessage({ id: "hotelPages.rooms.title" })}
|
||||
title={formatMessage({ id: "Rooms" })}
|
||||
subtitle={null}
|
||||
/>
|
||||
<Grids.Stackable>
|
||||
|
||||
44
components/ContentType/HotelPage/TabNavigation/index.tsx
Normal file
44
components/ContentType/HotelPage/TabNavigation/index.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
"use client"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import useHash from "@/hooks/useHash"
|
||||
|
||||
import { HotelHashValues } from "./types"
|
||||
|
||||
import styles from "./tabNavigation.module.css"
|
||||
|
||||
export default function TabNavigation() {
|
||||
const hash = useHash()
|
||||
const { formatMessage } = useIntl()
|
||||
const hotelTabLinks: { href: HotelHashValues; text: string }[] = [
|
||||
{ href: "#overview", text: "Overview" },
|
||||
{ href: "#rooms-section", text: "Rooms" },
|
||||
{ href: "#restaurant-and-bar", text: "Restaurant & Bar" },
|
||||
{ href: "#meetings-and-conferences", text: "Meetings & Conferences" },
|
||||
{ href: "#wellness-and-exercise", text: "Wellness & Exercise" },
|
||||
{ href: "#activities", text: "Activities" },
|
||||
{ href: "#faq", text: "FAQ" },
|
||||
]
|
||||
return (
|
||||
<nav className={styles.tabsContainer}>
|
||||
{hotelTabLinks.map((link) => {
|
||||
const isActive =
|
||||
hash === link.href || (hash === "" && link.href === "#overview")
|
||||
console.log("isActive", isActive, "hash", hash, "link.href", link.href)
|
||||
return (
|
||||
<Link
|
||||
key={link.href}
|
||||
href={link.href}
|
||||
active={isActive}
|
||||
variant="tab"
|
||||
color="burgundy"
|
||||
textDecoration="none"
|
||||
>
|
||||
{formatMessage({ id: link.text })}
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
.tabsContainer {
|
||||
display: flex;
|
||||
overflow-x: auto;
|
||||
white-space: nowrap;
|
||||
gap: var(--Spacing-x4);
|
||||
background: var(--Base-Surface-Subtle-Normal);
|
||||
justify-content: flex-start;
|
||||
padding-left: var(--Spacing-x4);
|
||||
padding-right: var(--Spacing-x3);
|
||||
border-bottom: 1px solid var(--Base-Border-Subtle);
|
||||
}
|
||||
8
components/ContentType/HotelPage/TabNavigation/types.ts
Normal file
8
components/ContentType/HotelPage/TabNavigation/types.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export type HotelHashValues =
|
||||
| "#overview"
|
||||
| "#rooms-section"
|
||||
| "#restaurant-and-bar"
|
||||
| "#meetings-and-conferences"
|
||||
| "#wellness-and-exercise"
|
||||
| "#activities"
|
||||
| "#faq"
|
||||
@@ -1,4 +1,13 @@
|
||||
.pageContainer {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.pageContainer > * {
|
||||
padding-left: var(--Spacing-x4);
|
||||
padding-right: var(--Spacing-x3);
|
||||
}
|
||||
|
||||
.mainSection {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x9);
|
||||
padding: var(--Spacing-x5) var(--Spacing-x3) var(--Spacing-x4);
|
||||
@@ -11,9 +20,14 @@
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1367px) {
|
||||
.pageContainer {
|
||||
.pageContainer > *:not(nav) {
|
||||
padding: var(--Spacing-x9) var(--Spacing-x5);
|
||||
}
|
||||
|
||||
.pageContainer > nav {
|
||||
padding-left: var(--Spacing-x5);
|
||||
padding-right: var(--Spacing-x5);
|
||||
}
|
||||
.introContainer {
|
||||
display: grid;
|
||||
/* use justify-content: space between once we have the map component*/
|
||||
|
||||
@@ -8,7 +8,7 @@ import styles from "./breadcrumbs.module.css"
|
||||
|
||||
export default async function Breadcrumbs() {
|
||||
const breadcrumbs = await serverClient().contentstack.breadcrumbs.get()
|
||||
if (!breadcrumbs) {
|
||||
if (!breadcrumbs || breadcrumbs.length === 0) {
|
||||
return null
|
||||
}
|
||||
const homeBreadcrumb = breadcrumbs.shift()
|
||||
|
||||
@@ -10,6 +10,7 @@ import { linkVariants } from "./variants"
|
||||
import type { LinkProps } from "./link"
|
||||
|
||||
export default function Link({
|
||||
active,
|
||||
className,
|
||||
color,
|
||||
href,
|
||||
@@ -23,10 +24,12 @@ export default function Link({
|
||||
...props
|
||||
}: LinkProps) {
|
||||
const currentPageSlug = usePathname()
|
||||
let isActive = currentPageSlug === href
|
||||
let isActive = active || currentPageSlug === href
|
||||
|
||||
if (partialMatch && !isActive) {
|
||||
isActive = currentPageSlug === href
|
||||
}
|
||||
|
||||
const classNames = linkVariants({
|
||||
active: isActive,
|
||||
className,
|
||||
|
||||
@@ -93,6 +93,17 @@
|
||||
background-color: var(--Scandic-Peach-20);
|
||||
}
|
||||
|
||||
.tab {
|
||||
font-family: var(--typography-Body-Regular-fontFamily);
|
||||
padding: var(--Spacing-x2) var(--Spacing-x0);
|
||||
color: var(--Base-Text-High-contrast);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.tab.active {
|
||||
border-bottom: 2px solid var(--Scandic-Brand-Burgundy);
|
||||
}
|
||||
|
||||
.black {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ export const linkVariants = cva(styles.link, {
|
||||
myPageMobileDropdown: styles.myPageMobileDropdown,
|
||||
shortcut: styles.shortcut,
|
||||
sidebar: styles.sidebar,
|
||||
tab: styles.tab,
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
||||
22
hooks/useHash.tsx
Normal file
22
hooks/useHash.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
"use client"
|
||||
|
||||
import { useParams } from "next/navigation"
|
||||
import { useEffect, useState } from "react"
|
||||
|
||||
const getHash = () =>
|
||||
typeof window !== "undefined" ? window.location.hash : undefined
|
||||
|
||||
const useHash = () => {
|
||||
const [isClient, setIsClient] = useState(false)
|
||||
const [hash, setHash] = useState(getHash())
|
||||
const params = useParams()
|
||||
|
||||
useEffect(() => {
|
||||
setIsClient(true)
|
||||
setHash(getHash())
|
||||
}, [params])
|
||||
|
||||
return isClient ? hash : null
|
||||
}
|
||||
|
||||
export default useHash
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"A photo of the room": "A photo of the room",
|
||||
"Activities": "Activities",
|
||||
"Add new card": "Add new card",
|
||||
"Address": "Address",
|
||||
"All our beds are from Bliss, allowing you to adjust the firmness for your perfect comfort.": "All our beds are from Bliss, allowing you to adjust the firmness for your perfect comfort.",
|
||||
@@ -44,12 +45,12 @@
|
||||
"Email": "Email",
|
||||
"There are no transactions to display": "There are no transactions to display",
|
||||
"Explore all levels and benefits": "Explore all levels and benefits",
|
||||
"FAQ": "FAQ",
|
||||
"Find booking": "Find booking",
|
||||
"Flexibility": "Flexibility",
|
||||
"From": "From",
|
||||
"Get inspired": "Get inspired",
|
||||
"Go back to overview": "Go back to overview",
|
||||
"hotelPages.rooms.title": "Rooms",
|
||||
"hotelPages.rooms.roomCard.person": "person",
|
||||
"hotelPages.rooms.roomCard.persons": "persons",
|
||||
"hotelPages.rooms.roomCard.seeRoomDetails": "See room details",
|
||||
@@ -63,6 +64,7 @@
|
||||
"Log in": "Log in",
|
||||
"Log in here": "Log in here",
|
||||
"Log out": "Log out",
|
||||
"Meetings & Conferences": "Meetings & Conferences",
|
||||
"Members": "Members",
|
||||
"Membership cards": "Membership cards",
|
||||
"Membership ID": "Membership ID",
|
||||
@@ -85,6 +87,7 @@
|
||||
"On your journey": "On your journey",
|
||||
"Open": "Open",
|
||||
"or": "or",
|
||||
"Overview": "Overview",
|
||||
"Password": "Password",
|
||||
"Phone": "Phone",
|
||||
"Phone is required": "Phone is required",
|
||||
@@ -98,7 +101,9 @@
|
||||
"Previous victories": "Previous victories",
|
||||
"Read more": "Read more",
|
||||
"Read more about the hotel": "Read more about the hotel",
|
||||
"Restaurant & Bar": "Restaurant & Bar",
|
||||
"Retype new password": "Retype new password",
|
||||
"Rooms": "Rooms",
|
||||
"Save": "Save",
|
||||
"Select a country": "Select a country",
|
||||
"Select country of residence": "Select country of residence",
|
||||
@@ -121,6 +126,7 @@
|
||||
"Welcome": "Welcome",
|
||||
"Visiting address": "Visiting address",
|
||||
"Welcome to": "Welcome to",
|
||||
"Wellness & Exercise": "Wellness & Exercise",
|
||||
"Where should you go next?": "Where should you go next?",
|
||||
"Which room class suits you the best?": "Which room class suits you the best?",
|
||||
"Year": "Year",
|
||||
|
||||
@@ -51,7 +51,6 @@
|
||||
"Highest level": "Högsta nivå",
|
||||
"How do you want to sleep?": "Hur vill du sova?",
|
||||
"How it works": "Hur det fungerar",
|
||||
"hotelPages.rooms.title": "Rum",
|
||||
"hotelPages.rooms.roomCard.person": "person",
|
||||
"hotelPages.rooms.roomCard.persons": "personer",
|
||||
"hotelPages.rooms.roomCard.seeRoomDetails": "Se rumsdetaljer",
|
||||
@@ -98,6 +97,7 @@
|
||||
"Read more": "Läs mer",
|
||||
"Read more about the hotel": "Läs mer om hotellet",
|
||||
"Retype new password": "Upprepa nytt lösenord",
|
||||
"Rooms": "Rum",
|
||||
"Save": "Spara",
|
||||
"Select a country": "Välj ett land",
|
||||
"Select country of residence": "Välj bosättningsland",
|
||||
|
||||
Reference in New Issue
Block a user