feat: Add tab navigation to hotel page
This commit is contained in:
@@ -108,6 +108,7 @@ html,
|
|||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
scroll-behavior: smooth;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { serverClient } from "@/lib/trpc/server"
|
|||||||
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 TabNavigation from "./TabNavigation"
|
||||||
|
|
||||||
import styles from "./hotelPage.module.css"
|
import styles from "./hotelPage.module.css"
|
||||||
|
|
||||||
@@ -23,18 +24,21 @@ export default async function HotelPage({ lang }: LangParams) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className={styles.pageContainer}>
|
<div className={styles.pageContainer}>
|
||||||
<div className={styles.introContainer}>
|
<TabNavigation />
|
||||||
<IntroSection
|
<main className={styles.mainSection}>
|
||||||
hotelName={attributes.name}
|
<div className={styles.introContainer}>
|
||||||
hotelDescription={attributes.hotelContent.texts.descriptions.short}
|
<IntroSection
|
||||||
location={attributes.location}
|
hotelName={attributes.name}
|
||||||
address={attributes.address}
|
hotelDescription={attributes.hotelContent.texts.descriptions.short}
|
||||||
tripAdvisor={attributes.ratings.tripAdvisor}
|
location={attributes.location}
|
||||||
/>
|
address={attributes.address}
|
||||||
<AmenitiesList detailedFacilities={attributes.detailedFacilities} />
|
tripAdvisor={attributes.ratings.tripAdvisor}
|
||||||
</div>
|
/>
|
||||||
<Rooms rooms={roomCategories} />
|
<AmenitiesList detailedFacilities={attributes.detailedFacilities} />
|
||||||
</main>
|
</div>
|
||||||
|
<Rooms rooms={roomCategories} />
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,10 +30,10 @@ export async function Rooms({ rooms }: RoomsProps) {
|
|||||||
.sort((a, b) => a.sortOrder - b.sortOrder)
|
.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.
|
.slice(0, 3) //TODO: Remove this and render all rooms once we've implemented "show more" logic in SW-203.
|
||||||
return (
|
return (
|
||||||
<SectionContainer>
|
<SectionContainer id="rooms-section">
|
||||||
<SectionHeader
|
<SectionHeader
|
||||||
textTransform="uppercase"
|
textTransform="uppercase"
|
||||||
title={formatMessage({ id: "hotelPages.rooms.title" })}
|
title={formatMessage({ id: "Rooms" })}
|
||||||
subtitle={null}
|
subtitle={null}
|
||||||
/>
|
/>
|
||||||
<Grids.Stackable>
|
<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 {
|
.pageContainer {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pageContainer > * {
|
||||||
|
padding-left: var(--Spacing-x4);
|
||||||
|
padding-right: var(--Spacing-x3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainSection {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: var(--Spacing-x9);
|
gap: var(--Spacing-x9);
|
||||||
padding: var(--Spacing-x5) var(--Spacing-x3) var(--Spacing-x4);
|
padding: var(--Spacing-x5) var(--Spacing-x3) var(--Spacing-x4);
|
||||||
@@ -11,9 +20,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 1367px) {
|
@media screen and (min-width: 1367px) {
|
||||||
.pageContainer {
|
.pageContainer > *:not(nav) {
|
||||||
padding: var(--Spacing-x9) var(--Spacing-x5);
|
padding: var(--Spacing-x9) var(--Spacing-x5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pageContainer > nav {
|
||||||
|
padding-left: var(--Spacing-x5);
|
||||||
|
padding-right: var(--Spacing-x5);
|
||||||
|
}
|
||||||
.introContainer {
|
.introContainer {
|
||||||
display: grid;
|
display: grid;
|
||||||
/* use justify-content: space between once we have the map component*/
|
/* 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() {
|
export default async function Breadcrumbs() {
|
||||||
const breadcrumbs = await serverClient().contentstack.breadcrumbs.get()
|
const breadcrumbs = await serverClient().contentstack.breadcrumbs.get()
|
||||||
if (!breadcrumbs) {
|
if (!breadcrumbs || breadcrumbs.length === 0) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
const homeBreadcrumb = breadcrumbs.shift()
|
const homeBreadcrumb = breadcrumbs.shift()
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { linkVariants } from "./variants"
|
|||||||
import type { LinkProps } from "./link"
|
import type { LinkProps } from "./link"
|
||||||
|
|
||||||
export default function Link({
|
export default function Link({
|
||||||
|
active,
|
||||||
className,
|
className,
|
||||||
color,
|
color,
|
||||||
href,
|
href,
|
||||||
@@ -23,10 +24,12 @@ export default function Link({
|
|||||||
...props
|
...props
|
||||||
}: LinkProps) {
|
}: LinkProps) {
|
||||||
const currentPageSlug = usePathname()
|
const currentPageSlug = usePathname()
|
||||||
let isActive = currentPageSlug === href
|
let isActive = active || currentPageSlug === href
|
||||||
|
|
||||||
if (partialMatch && !isActive) {
|
if (partialMatch && !isActive) {
|
||||||
isActive = currentPageSlug === href
|
isActive = currentPageSlug === href
|
||||||
}
|
}
|
||||||
|
|
||||||
const classNames = linkVariants({
|
const classNames = linkVariants({
|
||||||
active: isActive,
|
active: isActive,
|
||||||
className,
|
className,
|
||||||
|
|||||||
@@ -93,6 +93,17 @@
|
|||||||
background-color: var(--Scandic-Peach-20);
|
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 {
|
.black {
|
||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ export const linkVariants = cva(styles.link, {
|
|||||||
myPageMobileDropdown: styles.myPageMobileDropdown,
|
myPageMobileDropdown: styles.myPageMobileDropdown,
|
||||||
shortcut: styles.shortcut,
|
shortcut: styles.shortcut,
|
||||||
sidebar: styles.sidebar,
|
sidebar: styles.sidebar,
|
||||||
|
tab: styles.tab,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
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",
|
"A photo of the room": "A photo of the room",
|
||||||
|
"Activities": "Activities",
|
||||||
"Add new card": "Add new card",
|
"Add new card": "Add new card",
|
||||||
"Address": "Address",
|
"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.",
|
"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",
|
"Email": "Email",
|
||||||
"There are no transactions to display": "There are no transactions to display",
|
"There are no transactions to display": "There are no transactions to display",
|
||||||
"Explore all levels and benefits": "Explore all levels and benefits",
|
"Explore all levels and benefits": "Explore all levels and benefits",
|
||||||
|
"FAQ": "FAQ",
|
||||||
"Find booking": "Find booking",
|
"Find booking": "Find booking",
|
||||||
"Flexibility": "Flexibility",
|
"Flexibility": "Flexibility",
|
||||||
"From": "From",
|
"From": "From",
|
||||||
"Get inspired": "Get inspired",
|
"Get inspired": "Get inspired",
|
||||||
"Go back to overview": "Go back to overview",
|
"Go back to overview": "Go back to overview",
|
||||||
"hotelPages.rooms.title": "Rooms",
|
|
||||||
"hotelPages.rooms.roomCard.person": "person",
|
"hotelPages.rooms.roomCard.person": "person",
|
||||||
"hotelPages.rooms.roomCard.persons": "persons",
|
"hotelPages.rooms.roomCard.persons": "persons",
|
||||||
"hotelPages.rooms.roomCard.seeRoomDetails": "See room details",
|
"hotelPages.rooms.roomCard.seeRoomDetails": "See room details",
|
||||||
@@ -63,6 +64,7 @@
|
|||||||
"Log in": "Log in",
|
"Log in": "Log in",
|
||||||
"Log in here": "Log in here",
|
"Log in here": "Log in here",
|
||||||
"Log out": "Log out",
|
"Log out": "Log out",
|
||||||
|
"Meetings & Conferences": "Meetings & Conferences",
|
||||||
"Members": "Members",
|
"Members": "Members",
|
||||||
"Membership cards": "Membership cards",
|
"Membership cards": "Membership cards",
|
||||||
"Membership ID": "Membership ID",
|
"Membership ID": "Membership ID",
|
||||||
@@ -85,6 +87,7 @@
|
|||||||
"On your journey": "On your journey",
|
"On your journey": "On your journey",
|
||||||
"Open": "Open",
|
"Open": "Open",
|
||||||
"or": "or",
|
"or": "or",
|
||||||
|
"Overview": "Overview",
|
||||||
"Password": "Password",
|
"Password": "Password",
|
||||||
"Phone": "Phone",
|
"Phone": "Phone",
|
||||||
"Phone is required": "Phone is required",
|
"Phone is required": "Phone is required",
|
||||||
@@ -98,7 +101,9 @@
|
|||||||
"Previous victories": "Previous victories",
|
"Previous victories": "Previous victories",
|
||||||
"Read more": "Read more",
|
"Read more": "Read more",
|
||||||
"Read more about the hotel": "Read more about the hotel",
|
"Read more about the hotel": "Read more about the hotel",
|
||||||
|
"Restaurant & Bar": "Restaurant & Bar",
|
||||||
"Retype new password": "Retype new password",
|
"Retype new password": "Retype new password",
|
||||||
|
"Rooms": "Rooms",
|
||||||
"Save": "Save",
|
"Save": "Save",
|
||||||
"Select a country": "Select a country",
|
"Select a country": "Select a country",
|
||||||
"Select country of residence": "Select country of residence",
|
"Select country of residence": "Select country of residence",
|
||||||
@@ -121,6 +126,7 @@
|
|||||||
"Welcome": "Welcome",
|
"Welcome": "Welcome",
|
||||||
"Visiting address": "Visiting address",
|
"Visiting address": "Visiting address",
|
||||||
"Welcome to": "Welcome to",
|
"Welcome to": "Welcome to",
|
||||||
|
"Wellness & Exercise": "Wellness & Exercise",
|
||||||
"Where should you go next?": "Where should you go next?",
|
"Where should you go next?": "Where should you go next?",
|
||||||
"Which room class suits you the best?": "Which room class suits you the best?",
|
"Which room class suits you the best?": "Which room class suits you the best?",
|
||||||
"Year": "Year",
|
"Year": "Year",
|
||||||
|
|||||||
@@ -51,7 +51,6 @@
|
|||||||
"Highest level": "Högsta nivå",
|
"Highest level": "Högsta nivå",
|
||||||
"How do you want to sleep?": "Hur vill du sova?",
|
"How do you want to sleep?": "Hur vill du sova?",
|
||||||
"How it works": "Hur det fungerar",
|
"How it works": "Hur det fungerar",
|
||||||
"hotelPages.rooms.title": "Rum",
|
|
||||||
"hotelPages.rooms.roomCard.person": "person",
|
"hotelPages.rooms.roomCard.person": "person",
|
||||||
"hotelPages.rooms.roomCard.persons": "personer",
|
"hotelPages.rooms.roomCard.persons": "personer",
|
||||||
"hotelPages.rooms.roomCard.seeRoomDetails": "Se rumsdetaljer",
|
"hotelPages.rooms.roomCard.seeRoomDetails": "Se rumsdetaljer",
|
||||||
@@ -98,6 +97,7 @@
|
|||||||
"Read more": "Läs mer",
|
"Read more": "Läs mer",
|
||||||
"Read more about the hotel": "Läs mer om hotellet",
|
"Read more about the hotel": "Läs mer om hotellet",
|
||||||
"Retype new password": "Upprepa nytt lösenord",
|
"Retype new password": "Upprepa nytt lösenord",
|
||||||
|
"Rooms": "Rum",
|
||||||
"Save": "Spara",
|
"Save": "Spara",
|
||||||
"Select a country": "Välj ett land",
|
"Select a country": "Välj ett land",
|
||||||
"Select country of residence": "Välj bosättningsland",
|
"Select country of residence": "Välj bosättningsland",
|
||||||
|
|||||||
Reference in New Issue
Block a user