Skip to content

Commit

Permalink
finish product page
Browse files Browse the repository at this point in the history
  • Loading branch information
jsladerman committed Sep 13, 2024
1 parent 5930e59 commit 01ff073
Show file tree
Hide file tree
Showing 35 changed files with 289 additions and 513 deletions.
71 changes: 48 additions & 23 deletions pages/products/[product].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,37 @@ import { Button } from '@pluralsh/design-system'
import { type GetStaticPaths, type InferGetStaticPropsType } from 'next'
import Link from 'next/link'

import { directusClient } from '@src/apollo-client'
import { FooterVariant } from '@src/components/FooterFull'
import { StandardPageWidth } from '@src/components/layout/LayoutHelpers'
import { BasicPageHero } from '@src/components/PageHeros'
import ProductFeature from '@src/components/ProductFeature'
import { getProductsConfigs } from '@src/data/getProductConfigs'
import {
type ProductFeatureFragment,
ProductPageDocument,
type ProductPageQuery,
type ProductPageQueryVariables,
ProductPageSlugsDocument,
type ProductPageSlugsQuery,
type ProductPageSlugsQueryVariables,
} from '@src/generated/graphqlDirectus'
import { propsWithGlobalSettings } from '@src/utils/getGlobalProps'

import { GradientBG } from '../../src/components/layout/GradientBG'
import { HeaderPad } from '../../src/components/layout/HeaderPad'

export default function Product({
slug,
productInfo,
}: InferGetStaticPropsType<typeof getStaticProps>) {
const productConfig = getProductsConfigs()[slug]

return (
<HeaderPad
as={GradientBG}
position="top middle"
image="/images/products/product-background.png"
>
<BasicPageHero
heading={productConfig.title}
description={productConfig.description}
heading={productInfo.page_title}
description={productInfo.page_subtitle}
ctas={
<Button
large
Expand All @@ -39,11 +46,11 @@ export default function Product({
}
/>
<StandardPageWidth className="mb-xxxxxlarge max:mb-xxxxxxlarge">
{productConfig.features.map((item, index) => (
{productInfo.features?.map((feature, i) => (
<ProductFeature
key={index}
inverse={index % 2 !== 0}
feature={item}
key={i}
invert={i % 2 !== 0}
feature={feature as ProductFeatureFragment}
/>
))}
</StandardPageWidth>
Expand All @@ -52,18 +59,20 @@ export default function Product({
}

export const getStaticPaths: GetStaticPaths = async () => {
const products = Object.keys(getProductsConfigs())
const { data, error } = await directusClient.query<
ProductPageSlugsQuery,
ProductPageSlugsQueryVariables
>({
query: ProductPageSlugsDocument,
})

if (process.env.NODE_ENV === 'development') {
return {
paths: [],
fallback: 'blocking',
}
if (error) {
console.error('GraphQL query error in static:', error)
}

return {
paths: products.map((product) => ({
params: { product },
paths: data.product_pages.map((page) => ({
params: { product: page.slug },
})),
fallback: 'blocking',
}
Expand All @@ -75,15 +84,31 @@ export const getStaticProps = async (context) => {
? context?.params?.product
: null

if (!slug || !getProductsConfigs()[slug]) {
if (!slug) {
return { notFound: true }
}

const { data, error } = await directusClient.query<
ProductPageQuery,
ProductPageQueryVariables
>({
query: ProductPageDocument,
variables: { slug },
})

if (error) {
console.error('GraphQL query error in static: ', error)
}
const product = data.product_pages?.[0] || null

if (!product) {
return { notFound: true }
}

return propsWithGlobalSettings({
metaTitle: 'How Plural works',
metaDescription:
'Plural is an open-source, unified, application deployment platform that stands up a Kubernetes cluster and selected applications in the cloud provider of your choice.',
metaTitle: product?.page_title ?? '',
metaDescription: product?.page_subtitle ?? '',
footerVariant: FooterVariant.kitchenSink,
slug,
productInfo: product,
})
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
5 changes: 5 additions & 0 deletions src/apollo-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ const directusLink = ApolloLink.from([directusRetryLink, directusHttpLink])
export const directusClient = new ApolloClient({
link: directusLink,
cache: new InMemoryCache(),
defaultOptions: {
query: {
fetchPolicy: 'network-only',
},
},
})

export default client
49 changes: 32 additions & 17 deletions src/components/Navigation.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import { type ComponentProps, forwardRef } from 'react'
import { type ComponentProps, forwardRef, useContext } from 'react'

import { ArrowRightIcon, useNavigationContext } from '@pluralsh/design-system'

import * as designSystemIcons from '@pluralsh/design-system/dist/icons'
import styled, { useTheme } from 'styled-components'

import { getProductsConfigs } from '@src/data/getProductConfigs'
import * as productNavIcons from '@src/components/menu/ProductNavIcons'

import { mqs } from '../breakpoints'

import { GlobalPropsContext } from './PrimaryPage'
import { ResponsiveText } from './Typography'

const icons = { ...productNavIcons, ...designSystemIcons }

export const MainLink = forwardRef(
(props: ComponentProps<typeof MainLinkBase>, ref) => {
const { Link } = useNavigationContext()
Expand All @@ -27,8 +31,13 @@ export const ProductLink = forwardRef(
(props: ComponentProps<typeof MainLinkBase>, ref) => {
const { Link } = useNavigationContext()
const theme = useTheme()
const globalProps = useContext(GlobalPropsContext)

const itemConfig = globalProps?.siteSettings.main_nav.product.subnav.find(
(item) => item.id === props.id
)?.link

const itemConfig = getProductsConfigs()[props.id || '']
const IconComponent = itemConfig?.icon ? icons[itemConfig.icon] : null

return (
<MainLinkBase
Expand All @@ -37,7 +46,9 @@ export const ProductLink = forwardRef(
{...props}
>
<div className="h-[40px] w-[40px] rounded-medium border border-grey-750 bg-fill-two p-[10px]">
{itemConfig?.navIcon}
{IconComponent && (
<IconComponent color={theme.colors['icon-primary']} />
)}
</div>
<div>
<ResponsiveText
Expand All @@ -51,7 +62,7 @@ export const ProductLink = forwardRef(
textStyles={{ '': 'mBody2' }}
style={{ color: theme.colors['text-light'] }}
>
{itemConfig?.navDescription}
{itemConfig?.description}
</ResponsiveText>
</div>
<ArrowRightIcon
Expand All @@ -64,22 +75,31 @@ export const ProductLink = forwardRef(
}
)

export const SolutionLink = forwardRef(
export const ProductMobileLink = forwardRef(
(props: ComponentProps<typeof MainLinkBase>, ref) => {
const { Link } = useNavigationContext()
const globalProps = useContext(GlobalPropsContext)

const itemConfig = globalProps?.siteSettings.main_nav.product.subnav.find(
(item) => item.id === props.id
)?.link

return (
<MainLinkBase
ref={ref}
as={Link}
{...props}
>
<div className="h-[25px] w-[25px] min-w-[25px] rounded-medium border border-grey-750 bg-fill-two p-xxsmall">
{itemConfig?.icon}
</div>
<ResponsiveText
as="p"
textStyles={{ '': 'mBody2Bold' }}
textStyles={{ '': 'aBody2' }}
>
{props.children}
{itemConfig?.title}
</ResponsiveText>

<ArrowRightIcon
className="hover-arrow"
size="16px"
Expand All @@ -89,28 +109,23 @@ export const SolutionLink = forwardRef(
)
}
)
export const ProductMobileLink = forwardRef(

export const SolutionLink = forwardRef(
(props: ComponentProps<typeof MainLinkBase>, ref) => {
const { Link } = useNavigationContext()

const itemConfig = getProductsConfigs()[props.id || '']

return (
<MainLinkBase
ref={ref}
as={Link}
{...props}
>
<div className="h-[25px] w-[25px] min-w-[25px] rounded-medium border border-grey-750 bg-fill-two p-xxsmall">
{itemConfig?.navIcon}
</div>
<ResponsiveText
as="p"
textStyles={{ '': 'aBody2' }}
textStyles={{ '': 'mBody2Bold' }}
>
{itemConfig?.title}
{props.children}
</ResponsiveText>

<ArrowRightIcon
className="hover-arrow"
size="16px"
Expand Down
22 changes: 2 additions & 20 deletions src/components/NavigationDesktop.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { useMemo } from 'react'

import { ColorModeProvider } from '@pluralsh/design-system'

import { type NavData, useNavData } from '@src/contexts/NavDataContext'
import { useNavData } from '@src/contexts/NavDataContext'

import { TopNavMenu } from './menu/TopNavMenu'
import { MainLink } from './Navigation'
Expand All @@ -15,30 +13,14 @@ export function NavItemLink({ navItem }: { navItem?: any }) {
)
}

function flattenNavData(navData: NavData): NavData {
const ret = navData?.flatMap((navItem) => {
if (navItem?.flatten && navItem.subnav) {
return flattenNavData(navItem.subnav)
}

return navItem
})

return ret
}

export function NavigationDesktop({ logoRef }) {
const navData = useNavData()
const flatNav = useMemo(() => flattenNavData(navData), [navData])
const logoLeft = logoRef?.current?.getBoundingClientRect()?.left

return (
<ColorModeProvider mode="dark">
<div className="hidden gap-xsmall lg:flex">
{flatNav?.map((navItem) => {
if (navItem?.mobile_only) {
return null
}
{navData?.map((navItem) => {
if (navItem?.subnav) {
const kind =
navItem.link?.title === 'Product'
Expand Down
38 changes: 22 additions & 16 deletions src/components/PrimaryPage.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { type ReactNode } from 'react'
import { type ReactNode, createContext } from 'react'

import { type NextRouter, useRouter } from 'next/router'

import { type GlobalPageProps } from '@pages/_app'
import { PAGE_TITLE_PREFIX, PAGE_TITLE_SUFFIX, ROOT_TITLE } from '@src/consts'
import { NavDataProvider } from '@src/contexts/NavDataContext'
import { NavDataProvider, type NavList } from '@src/contexts/NavDataContext'

import { type GlobalProps } from '../utils/getGlobalProps'

Expand All @@ -31,6 +31,10 @@ function selectOgImage(router: NextRouter) {
return 'og_image.png'
}

export const GlobalPropsContext = createContext<GlobalProps | undefined>(
undefined
)

export default function PrimaryPage({
pageProps,
globalProps,
Expand All @@ -54,23 +58,25 @@ export default function PrimaryPage({
...(ogImage ? { ogImage } : {}),
}

const navData = siteSettings?.main_nav?.subnav || []
const navData = (Object.values(siteSettings?.main_nav) as NavList[]) || []

return (
<NavDataProvider value={navData}>
<PagePropsContext.Provider value={pageProps}>
<HtmlHead {...headProps} />
<PageHeader
showHeaderBG={pageProps.showHeaderBG}
promoBanner={{
content: siteSettings?.promo_banner_content,
url: siteSettings?.promo_banner_url,
}}
/>
{children}
<ExternalScripts />
<FullFooter variant={pageProps.footerVariant} />
</PagePropsContext.Provider>
<GlobalPropsContext.Provider value={globalProps}>
<PagePropsContext.Provider value={pageProps}>
<HtmlHead {...headProps} />
<PageHeader
showHeaderBG={pageProps.showHeaderBG}
promoBanner={{
content: siteSettings?.promo_banner_content,
url: siteSettings?.promo_banner_url,
}}
/>
{children}
<ExternalScripts />
<FullFooter variant={pageProps.footerVariant} />
</PagePropsContext.Provider>
</GlobalPropsContext.Provider>
</NavDataProvider>
)
}
Loading

0 comments on commit 01ff073

Please sign in to comment.