Skip to content

Commit

Permalink
cmsify footer
Browse files Browse the repository at this point in the history
  • Loading branch information
jsladerman committed Dec 4, 2024
1 parent a9aaf56 commit 7b8f562
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 129 deletions.
5 changes: 4 additions & 1 deletion src/components/FooterFull.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import styled from 'styled-components'

import { mqs } from '@src/breakpoints'
import { type FooterFragment } from '@src/generated/graphqlDirectus'

import { BasicFooter, MinAltFooter } from './BasicFooter'
import { FooterNav } from './FooterNav'
Expand All @@ -21,11 +22,13 @@ export enum HeaderVariant {
}

export function FullFooter({
footerData,
className,
variant = FooterVariant.withNav,
}: {
className?: string
variant?: FooterVariant
footerData: Nullable<FooterFragment>
}) {
if (variant === FooterVariant.none) {
return null
Expand Down Expand Up @@ -55,7 +58,7 @@ export function FullFooter({
role="contentinfo"
className="relative z-20 flex flex-col gap-y-xxxlarge pt-xxxxlarge"
>
{showNav && <FooterNav />}
{showNav && <FooterNav footerData={footerData} />}
<BasicFooter />
</div>
</StickyFooterSC>
Expand Down
204 changes: 80 additions & 124 deletions src/components/FooterNav.tsx
Original file line number Diff line number Diff line change
@@ -1,115 +1,16 @@
import { type ComponentProps, type ReactNode } from 'react'

import Link from 'next/link'

import styled from 'styled-components'
import { type ReadonlyDeep } from 'type-fest'

import { mqs } from '@src/breakpoints'
import { DISCORD_LINK } from '@src/consts'
import {
type FooterFragment,
type FooterLinkFragment,
} from '@src/generated/graphqlDirectus'

import { FullPageWidth } from './layout/LayoutHelpers'
import { NewsletterSignupForm } from './NewsletterSignupForm'

type NavItemT = {
heading: ReactNode
links: ComponentProps<typeof Link>[]
}

const navItems = [
{
heading: 'Product',
links: [
{
children: 'Login',
href: 'https://app.plural.sh/login',
target: '_blank',
},
{
children: 'GitHub',
href: 'https://github.com/pluralsh/plural',
target: '_blank',
},
{
children: 'Pricing',
href: '/pricing',
},
{
children: 'Support',
href: '/contact',
},
],
},
{
heading: 'Company',
links: [
{
children: 'About',
href: '/about',
},
{
children: 'Careers',
href: '/careers',
},
{
children: 'In the news',
href: 'https://www.plural.sh/blog',
target: '_blank',
},
],
},
{
heading: 'Resources',
links: [
{
children: 'Docs',
href: 'https://docs.plural.sh',
target: '_blank',
},
{
children: 'Blog',
href: 'https://www.plural.sh/blog',
target: '_blank',
},
{
children: 'Releases',
href: 'https://github.com/pluralsh/plural/releases',
target: '_blank',
},
// {
// children: 'Security & Trust Portal',
// href: 'https://github.com/pluralsh/plural/releases',
// target: '_blank',
// },
] as const,
},
{
heading: 'Contact',
links: [
{
children: 'Discord',
href: DISCORD_LINK,
target: '_blank',
},
{
children: 'Twitter',
href: 'https://www.twitter.com/plural_sh',
target: '_blank',
},
{
children: 'LinkedIn',
href: 'https://www.linkedin.com/company/pluralsh',
target: '_blank',
},
{
children: 'Email',
href: '/contact',
target: '_blank',
},
],
},
] as const satisfies ReadonlyDeep<NavItemT[]>

const NavSections = styled.ul(({ theme }) => ({
...theme.partials.reset.list,
display: 'grid',
Expand Down Expand Up @@ -149,6 +50,7 @@ const NavItems = styled.ul(({ theme }) => ({
flexDirection: 'column',
gap: theme.spacing.xsmall,
}))

const NavItem = styled.li(({ theme }) => ({
...theme.partials.reset.li,
margin: 0,
Expand All @@ -166,29 +68,83 @@ export const FooterNavLink = styled(Link)(({ theme }) => ({
},
}))

export const FooterNav = styled(({ ...props }: ComponentProps<'div'>) => (
<div {...props}>
<FullPageWidth>
<NavSections>
{navItems.map((navItem) => (
<NavSection key={navItem.heading}>
<FooterHeading>{navItem.heading}</FooterHeading>
<NavItems>
{navItem.links.map((link) => (
<NavItem key={`${link.children}${link.href}`}>
<FooterNavLink {...link} />
</NavItem>
))}
</NavItems>
export function FooterNav({
footerData,
}: {
footerData: Nullable<FooterFragment>
}) {
if (!footerData) return null

return (
<FooterNavWrapSC>
<FullPageWidth>
<NavSections>
<FooterColumn
title={footerData.column_1_title}
links={sanitizeFooterLinks(footerData.column_1_links)}
/>
<FooterColumn
title={footerData.column_2_title}
links={sanitizeFooterLinks(footerData.column_2_links)}
/>
<FooterColumn
title={footerData.column_3_title}
links={sanitizeFooterLinks(footerData.column_3_links)}
/>
<FooterColumn
title={footerData.column_4_title}
links={sanitizeFooterLinks(footerData.column_4_links)}
/>
<NavSection className="newsletter">
<NewsletterSignupForm />
</NavSection>
</NavSections>
</FullPageWidth>
</FooterNavWrapSC>
)
}

const sanitizeFooterLinks = (
links: Nullable<
Nullable<{ footer_links_id?: Nullable<FooterLinkFragment> }>[]
>
) =>
links
?.filter((link): link is { footer_links_id: FooterLinkFragment } => !!link)
.map((link) => ({
name: link.footer_links_id?.name ?? '',
url: link.footer_links_id?.url ?? '',
})) ?? []

function FooterColumn({
title,
links,
}: {
title: Nullable<string>
links: { name: string; url: string }[]
}) {
if (!title) return null

return (
<NavSection>
<FooterHeading>{title}</FooterHeading>
<NavItems>
{links?.map((link) => (
<NavItem key={link?.url}>
<FooterNavLink
href={link?.url || ''}
target={link?.url.charAt(0) === '/' ? '_self' : '_blank'}
>
{link?.name}
</FooterNavLink>
</NavItem>
))}
<NavSection className="newsletter">
<NewsletterSignupForm />
</NavSection>
</NavSections>
</FullPageWidth>
</div>
))(({ theme }) => {
</NavItems>
</NavSection>
)
}

const FooterNavWrapSC = styled.div(({ theme }) => {
const outlineOffset = -4

return {
Expand Down
5 changes: 4 additions & 1 deletion src/components/PrimaryPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ export default function PrimaryPage({
/>
{children}
<ExternalScripts />
<FullFooter variant={pageProps.footerVariant} />
<FullFooter
variant={pageProps.footerVariant}
footerData={globalProps.footerData}
/>
</PagePropsContext.Provider>
</GlobalPropsContext.Provider>
</NavDataProvider>
Expand Down
48 changes: 45 additions & 3 deletions src/generated/graphqlDirectus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12639,12 +12639,16 @@ export type OurImpactComponentFragment = { __typename?: 'our_impact', top_left_m

export type QuoteCarouselComponentFragment = { __typename?: 'quote_carousel', title?: string | null, quotes?: { __typename?: 'quote_lists', slug: string, items?: Array<{ __typename?: 'quote_lists_items', item?: { __typename?: 'quotes', id: string, quote?: string | null, author_text?: string | null } | null } | null> | null } | null };

export type GlobalDataFragment = { __typename?: 'Query', site_settings?: { __typename?: 'site_settings', og_description?: string | null, og_image?: { __typename?: 'directus_files', id: string, title?: string | null, description?: string | null, tags?: any | null, filename_disk?: string | null, filename_download: string, metadata?: any | null, type?: string | null, filesize?: any | null } | null } | null, solutions_pages: Array<{ __typename?: 'solutions_pages', id: string, slug: string, category?: string | null, dropdown_icon?: string | null, dropdown_title?: string | null }>, product_pages: Array<{ __typename?: 'product_pages', id: string, slug: string, dropdown_icon?: string | null, dropdown_title?: string | null, dropdown_description?: string | null }>, resource_pages: Array<{ __typename?: 'resource_pages', id: string, external: boolean, slug: string, url?: string | null, dropdown_title?: string | null }> };
export type GlobalDataFragment = { __typename?: 'Query', site_settings?: { __typename?: 'site_settings', og_description?: string | null, og_image?: { __typename?: 'directus_files', id: string, title?: string | null, description?: string | null, tags?: any | null, filename_disk?: string | null, filename_download: string, metadata?: any | null, type?: string | null, filesize?: any | null } | null } | null, solutions_pages: Array<{ __typename?: 'solutions_pages', id: string, slug: string, category?: string | null, dropdown_icon?: string | null, dropdown_title?: string | null }>, product_pages: Array<{ __typename?: 'product_pages', id: string, slug: string, dropdown_icon?: string | null, dropdown_title?: string | null, dropdown_description?: string | null }>, resource_pages: Array<{ __typename?: 'resource_pages', id: string, external: boolean, slug: string, url?: string | null, dropdown_title?: string | null }>, footer?: { __typename?: 'footer', column_1_title?: string | null, column_2_title?: string | null, column_3_title?: string | null, column_4_title?: string | null, column_1_links?: Array<{ __typename?: 'footer_footer_links', footer_links_id?: { __typename?: 'footer_links', name: string, url: string } | null } | null> | null, column_2_links?: Array<{ __typename?: 'footer_footer_links_1', footer_links_id?: { __typename?: 'footer_links', name: string, url: string } | null } | null> | null, column_3_links?: Array<{ __typename?: 'footer_footer_links_2', footer_links_id?: { __typename?: 'footer_links', name: string, url: string } | null } | null> | null, column_4_links?: Array<{ __typename?: 'footer_footer_links_3', footer_links_id?: { __typename?: 'footer_links', name: string, url: string } | null } | null> | null } | null };

export type FooterFragment = { __typename?: 'footer', column_1_title?: string | null, column_2_title?: string | null, column_3_title?: string | null, column_4_title?: string | null, column_1_links?: Array<{ __typename?: 'footer_footer_links', footer_links_id?: { __typename?: 'footer_links', name: string, url: string } | null } | null> | null, column_2_links?: Array<{ __typename?: 'footer_footer_links_1', footer_links_id?: { __typename?: 'footer_links', name: string, url: string } | null } | null> | null, column_3_links?: Array<{ __typename?: 'footer_footer_links_2', footer_links_id?: { __typename?: 'footer_links', name: string, url: string } | null } | null> | null, column_4_links?: Array<{ __typename?: 'footer_footer_links_3', footer_links_id?: { __typename?: 'footer_links', name: string, url: string } | null } | null> | null };

export type FooterLinkFragment = { __typename?: 'footer_links', name: string, url: string };

export type GetGlobalDataQueryVariables = Exact<{ [key: string]: never; }>;


export type GetGlobalDataQuery = { __typename?: 'Query', site_settings?: { __typename?: 'site_settings', og_description?: string | null, og_image?: { __typename?: 'directus_files', id: string, title?: string | null, description?: string | null, tags?: any | null, filename_disk?: string | null, filename_download: string, metadata?: any | null, type?: string | null, filesize?: any | null } | null } | null, solutions_pages: Array<{ __typename?: 'solutions_pages', id: string, slug: string, category?: string | null, dropdown_icon?: string | null, dropdown_title?: string | null }>, product_pages: Array<{ __typename?: 'product_pages', id: string, slug: string, dropdown_icon?: string | null, dropdown_title?: string | null, dropdown_description?: string | null }>, resource_pages: Array<{ __typename?: 'resource_pages', id: string, external: boolean, slug: string, url?: string | null, dropdown_title?: string | null }> };
export type GetGlobalDataQuery = { __typename?: 'Query', site_settings?: { __typename?: 'site_settings', og_description?: string | null, og_image?: { __typename?: 'directus_files', id: string, title?: string | null, description?: string | null, tags?: any | null, filename_disk?: string | null, filename_download: string, metadata?: any | null, type?: string | null, filesize?: any | null } | null } | null, solutions_pages: Array<{ __typename?: 'solutions_pages', id: string, slug: string, category?: string | null, dropdown_icon?: string | null, dropdown_title?: string | null }>, product_pages: Array<{ __typename?: 'product_pages', id: string, slug: string, dropdown_icon?: string | null, dropdown_title?: string | null, dropdown_description?: string | null }>, resource_pages: Array<{ __typename?: 'resource_pages', id: string, external: boolean, slug: string, url?: string | null, dropdown_title?: string | null }>, footer?: { __typename?: 'footer', column_1_title?: string | null, column_2_title?: string | null, column_3_title?: string | null, column_4_title?: string | null, column_1_links?: Array<{ __typename?: 'footer_footer_links', footer_links_id?: { __typename?: 'footer_links', name: string, url: string } | null } | null> | null, column_2_links?: Array<{ __typename?: 'footer_footer_links_1', footer_links_id?: { __typename?: 'footer_links', name: string, url: string } | null } | null> | null, column_3_links?: Array<{ __typename?: 'footer_footer_links_2', footer_links_id?: { __typename?: 'footer_links', name: string, url: string } | null } | null> | null, column_4_links?: Array<{ __typename?: 'footer_footer_links_3', footer_links_id?: { __typename?: 'footer_links', name: string, url: string } | null } | null> | null } | null };

export type MinJobListingFragment = { __typename?: 'job_listings', id: string, slug: string, job_title?: string | null, department?: string | null, tags?: any | null, location?: string | null, status?: string | null };

Expand Down Expand Up @@ -13104,6 +13108,40 @@ export const ResourcePageTinyFragmentDoc = gql`
dropdown_title
}
`;
export const FooterLinkFragmentDoc = gql`
fragment FooterLink on footer_links {
name
url
}
`;
export const FooterFragmentDoc = gql`
fragment Footer on footer {
column_1_title
column_1_links {
footer_links_id {
...FooterLink
}
}
column_2_title
column_2_links {
footer_links_id {
...FooterLink
}
}
column_3_title
column_3_links {
footer_links_id {
...FooterLink
}
}
column_4_title
column_4_links {
footer_links_id {
...FooterLink
}
}
}
${FooterLinkFragmentDoc}`;
export const GlobalDataFragmentDoc = gql`
fragment GlobalData on Query {
site_settings {
Expand All @@ -13118,11 +13156,15 @@ export const GlobalDataFragmentDoc = gql`
resource_pages(filter: {status: {_neq: "hidden"}}) {
...ResourcePageTiny
}
footer {
...Footer
}
}
${SiteSettingsFragmentDoc}
${SolutionPageTinyFragmentDoc}
${ProductPageTinyFragmentDoc}
${ResourcePageTinyFragmentDoc}`;
${ResourcePageTinyFragmentDoc}
${FooterFragmentDoc}`;
export const MinJobListingFragmentDoc = gql`
fragment MinJobListing on job_listings {
id
Expand Down
35 changes: 35 additions & 0 deletions src/graph/directus/globalData.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,41 @@ fragment GlobalData on Query {
resource_pages(filter: { status: { _neq: "hidden" } }) {
...ResourcePageTiny
}
footer {
...Footer
}
}

fragment Footer on footer {
column_1_title
column_1_links {
footer_links_id {
...FooterLink
}
}
column_2_title
column_2_links {
footer_links_id {
...FooterLink
}
}
column_3_title
column_3_links {
footer_links_id {
...FooterLink
}
}
column_4_title
column_4_links {
footer_links_id {
...FooterLink
}
}
}

fragment FooterLink on footer_links {
name
url
}

query GetGlobalData {
Expand Down
Loading

0 comments on commit 7b8f562

Please sign in to comment.