Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

convert product pages to cms #103

Merged
merged 3 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"editor.formatOnSave": false,
"editor.formatOnSave": true,
"editor.codeActionsOnSave": ["source.formatDocument", "source.fixAll.eslint"],
"search.exclude": {
"**/.yarn": true,
Expand Down
5 changes: 4 additions & 1 deletion codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ const config: CodegenConfig = {
schema: `https://directus.plural.sh/graphql${
directusToken ? `?access_token=${directusToken}` : ''
}`,
documents: './src/graph/directus/*.graphql',
documents: [
'./src/graph/directus/**/*.graphql',
'!./src/graph/directus/system/**/*.graphql',
],
plugins: [
'typescript',
'typescript-operations',
Expand Down
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
54 changes: 37 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,36 @@ export const ProductLink = forwardRef(
}
)

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

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

const IconComponent = itemConfig?.icon ? icons[itemConfig.icon] : null

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">
{IconComponent && (
<IconComponent color={theme.colors['icon-primary']} />
)}
</div>
<ResponsiveText
as="p"
textStyles={{ '': 'mBody2Bold' }}
textStyles={{ '': 'aBody2' }}
>
{props.children}
{itemConfig?.title}
</ResponsiveText>

<ArrowRightIcon
className="hover-arrow"
size="16px"
Expand All @@ -89,28 +114,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
10 changes: 1 addition & 9 deletions src/components/NavigationMobile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ import { isEmpty } from 'lodash-es'
import styled, { useTheme } from 'styled-components'
import { useIsomorphicLayoutEffect } from 'usehooks-ts'

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

import useScrollLock from './hooks/useScrollLock'
import { MainLink, ProductMobileLink } from './Navigation'
Expand All @@ -32,13 +31,6 @@ type MobileMenuProps = NavContextValue & {
className?: string
}

type NavData = (
| (NavListFragment & {
subnav?: NavData | null
})
| null
)[]

function NavList({ navData }: { navData?: NavData | null }) {
const theme = useTheme()

Expand Down
Loading
Loading