diff --git a/.eslintrc.js b/.eslintrc.js
index 5e238e6..267cb6e 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -3,4 +3,8 @@ module.exports = {
extends: ['plugin:@next/next/recommended', '@payloadcms'],
ignorePatterns: ['**/payload-types.ts'],
plugins: ['prettier'],
+ rules: {
+ '@typescript-eslint/explicit-function-return-type': 'off',
+ '@typescript-eslint/array-type': 'off',
+ },
}
diff --git a/src/app/(pages)/[slug]/page.tsx b/src/app/(pages)/[slug]/page.tsx
index 2ebbfd8..f5c1304 100644
--- a/src/app/(pages)/[slug]/page.tsx
+++ b/src/app/(pages)/[slug]/page.tsx
@@ -1,19 +1,17 @@
-import { Metadata } from "next";
-import { draftMode } from "next/headers";
+import { Metadata } from 'next'
+import { draftMode } from 'next/headers'
+import { Page } from '../../../payload/payload-types'
+import { fetchDoc } from '../../_api/fetchDoc'
+import HubContentGrid from '../../_blocks/HubContentGrid/page'
+import { Subscribe } from '../../_blocks/Subscribe'
+import SearchBar from '../../_components/SearchBar'
+import { ALL_CONTENT } from '../../_graphql/allContent'
+import { fetcher } from '../../_utilities/fetcher'
+import { generateMeta } from '../../_utilities/generateMeta'
+import styles from './styles.module.css'
-import { Page } from "../../../payload/payload-types";
-import { fetchDoc } from "../../_api/fetchDoc";
-
-import { generateMeta } from "../../_utilities/generateMeta";
-
-import styles from "./styles.module.css";
-
-
-import { Subscribe } from "@/app/_blocks/Subscribe";
-import { ALL_CONTENT } from "@/app/_graphql/allContent";
-import ContentCard from "@/app/_components/ContentCard";
-import { fetcher } from "@/app/_utilities/fetcher";
+import { AllIcon } from '@/app/_icons/icons'
// Payload Cloud caches all files through Cloudflare, so we don't need Next.js to cache them as well
// This means that we can turn off Next.js data caching and instead rely solely on the Cloudflare CDN
@@ -21,25 +19,21 @@ import { fetcher } from "@/app/_utilities/fetcher";
// But we also need to force Next.js to dynamically render this page on each request for preview mode to work
// See https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamic
// If you are not using Payload Cloud then this line can be removed, see `../../../README.md#cache`
-export const dynamic = "force-dynamic";
-
-
-export default async function Page({ params: { slug = "home" } }) {
- const content = await fetcher({ query: ALL_CONTENT });
-
- const content_list = Object.keys(content).flatMap(key =>
- content[key].map(content => ({
- contentType: key,
- content: content,
- })));
+export const dynamic = 'force-dynamic'
+export default async function Page({ params: { slug = 'home' } }) {
+ const articles = await fetcher({ query: ALL_CONTENT })
return (
<>
{/* Head Block*/}
-
-
+
{/* Top Highlight */}
@@ -47,10 +41,10 @@ export default async function Page({ params: { slug = "home" } }) {
HIGHLIGHTS
From nutritionist to product designer: Reinvinting my carrer at 30
- Inside subvisual Date and Readtime{" "}
+ Inside subvisual Date and Readtime{' '}
-
+
Rui Sousa
@@ -59,45 +53,40 @@ export default async function Page({ params: { slug = "home" } }) {
HIGHLIGHTS
- {" "}
+ {' '}
From nutritionist to product designer: Reinvinting my carrer at 30
- Inside subvisual Date and Readtime{" "}
+ Inside subvisual Date and Readtime{' '}
-
+
Rui Sousa
+ {/* Search Bar */}
+
{/* Content Grid */}
-
-
- {content_list.map((article, i) => (
-
-
-
- ))}
-
-
+
+
>
- );
+ )
}
-export async function generateMetadata({ params: { slug = "home" } }): Promise {
- const { isEnabled: isDraftMode } = draftMode();
+export async function generateMetadata({ params: { slug = 'home' } }): Promise {
+ const { isEnabled: isDraftMode } = draftMode()
- let page: Page | null = null;
+ let page: Page | null = null
try {
page = await fetchDoc({
- collection: "pages",
+ collection: 'pages',
slug,
draft: isDraftMode,
- });
+ })
} catch (error) {
// don't throw an error if the fetch fails
// this is so that we can render static fallback pages for the demo
@@ -105,5 +94,5 @@ export async function generateMetadata({ params: { slug = "home" } }): Promise
- This is a Colfax Medium H1.
- This is a Colfax Redular H1.
- This is an Acta H1.
+ This is a Colfax Medium H1.
+ This is a Colfax Redular H1.
+ This is an Acta H1.
- This is a Colfax Medium H2.
- This is a Colfax Regular H2.
- This is an Acta H2.
+ This is a Colfax Medium H2.
+ This is a Colfax Regular H2.
+ This is an Acta H2.
- This is a Colfax Medium H3.
- This is a Colfax Regular H3.
- This is an Acta H3.
+ This is a Colfax Medium H3.
+ This is a Colfax Regular H3.
+ This is an Acta H3.
- This is a Colfax Medium H4.
- This is a Colfax Regular H4.
- This is an Acta H4.
+ This is a Colfax Medium H4.
+ This is a Colfax Regular H4.
+ This is an Acta H4.
- This is a Colfax Medium H5.
- This is a Colfax Regular H5.
- This is an Acta H5.
+ This is a Colfax Medium H5.
+ This is a Colfax Regular H5.
+ This is an Acta H5.
- This is a Colfax Medium H6.
- This is a Colfax Regular H6.
- This is an Acta H6.
+ This is a Colfax Medium H6.
+ This is a Colfax Regular H6.
+ This is an Acta H6.
- );
+ )
}
diff --git a/src/app/_api/fetchGlobals.ts b/src/app/_api/fetchGlobals.ts
index 281d3dc..7c8c5fe 100644
--- a/src/app/_api/fetchGlobals.ts
+++ b/src/app/_api/fetchGlobals.ts
@@ -1,108 +1,108 @@
-import type { Footer, Header, Settings, Social } from "../../payload/payload-types";
-import { FOOTER_QUERY, HEADER_QUERY, SETTINGS_QUERY, SOCIALS_QUERY } from "../_graphql/globals";
-import { GRAPHQL_API_URL } from "./shared";
+import type { Footer, Header, Settings, Social } from '../../payload/payload-types'
+import { FOOTER_QUERY, HEADER_QUERY, SETTINGS_QUERY, SOCIALS_QUERY } from '../_graphql/globals'
+import { GRAPHQL_API_URL } from './shared'
export async function fetchSettings(): Promise {
- if (!GRAPHQL_API_URL) throw new Error("NEXT_PUBLIC_SERVER_URL not found");
+ if (!GRAPHQL_API_URL) throw new Error('NEXT_PUBLIC_SERVER_URL not found')
try {
return await fetch(`${GRAPHQL_API_URL}/api/graphql`, {
- method: "POST",
+ method: 'POST',
headers: {
- "Content-Type": "application/json",
+ 'Content-Type': 'application/json',
},
- cache: "no-store",
+ cache: 'no-store',
body: JSON.stringify({
query: SETTINGS_QUERY,
}),
})
?.then(res => {
- if (!res.ok) throw new Error("Error fetching doc");
- return res.json();
+ if (!res.ok) throw new Error('Error fetching doc')
+ return res.json()
})
?.then(res => {
- if (res?.errors) throw new Error(res?.errors[0]?.message || "Error fetching settings");
- return res.data?.Settings;
- });
+ if (res?.errors) throw new Error(res?.errors[0]?.message || 'Error fetching settings')
+ return res.data?.Settings
+ })
} catch (err: unknown) {
- throw new Error("Error fetching settings")
+ throw new Error('Error fetching settings')
}
}
export async function fetchHeader(): Promise {
- if (!GRAPHQL_API_URL) throw new Error("NEXT_PUBLIC_SERVER_URL not found");
+ if (!GRAPHQL_API_URL) throw new Error('NEXT_PUBLIC_SERVER_URL not found')
try {
return await fetch(`${GRAPHQL_API_URL}/api/graphql`, {
- method: "POST",
+ method: 'POST',
headers: {
- "Content-Type": "application/json",
+ 'Content-Type': 'application/json',
},
- cache: "no-store",
+ cache: 'no-store',
body: JSON.stringify({
query: HEADER_QUERY,
}),
})
?.then(res => {
- if (!res.ok) throw new Error("Error fetching doc");
- return res.json();
+ if (!res.ok) throw new Error('Error fetching doc')
+ return res.json()
})
?.then(res => {
- if (res?.errors) throw new Error(res?.errors[0]?.message || "Error fetching header");
- return res.data?.Header;
- });
+ if (res?.errors) throw new Error(res?.errors[0]?.message || 'Error fetching header')
+ return res.data?.Header
+ })
} catch (err: unknown) {
- throw new Error("Error fetching header");
+ throw new Error('Error fetching header')
}
}
export async function fetchFooter(): Promise {
- if (!GRAPHQL_API_URL) throw new Error("NEXT_PUBLIC_SERVER_URL not found");
+ if (!GRAPHQL_API_URL) throw new Error('NEXT_PUBLIC_SERVER_URL not found')
try {
return await fetch(`${GRAPHQL_API_URL}/api/graphql`, {
- method: "POST",
+ method: 'POST',
headers: {
- "Content-Type": "application/json",
+ 'Content-Type': 'application/json',
},
body: JSON.stringify({
query: FOOTER_QUERY,
}),
})
.then(res => {
- if (!res.ok) throw new Error("Error fetching doc");
- return res.json();
+ if (!res.ok) throw new Error('Error fetching doc')
+ return res.json()
})
?.then(res => {
- if (res?.errors) throw new Error(res?.errors[0]?.message || "Error fetching footer");
- return res.data?.Footer;
- });
+ if (res?.errors) throw new Error(res?.errors[0]?.message || 'Error fetching footer')
+ return res.data?.Footer
+ })
} catch (err: unknown) {
- throw new Error("Error fetching footer");
+ throw new Error('Error fetching footer')
}
}
export async function fetchSocials(): Promise {
- if (!GRAPHQL_API_URL) throw new Error("NEXT_PUBLIC_SERVER_URL not found");
+ if (!GRAPHQL_API_URL) throw new Error('NEXT_PUBLIC_SERVER_URL not found')
try {
return await fetch(`${GRAPHQL_API_URL}/api/graphql`, {
- method: "POST",
+ method: 'POST',
headers: {
- "Content-Type": "application/json",
+ 'Content-Type': 'application/json',
},
- cache: "no-store",
+ cache: 'no-store',
body: JSON.stringify({
query: SOCIALS_QUERY,
}),
})
?.then(res => res.json())
?.then(res => {
- if (res?.errors) throw new Error(res?.errors[0]?.message || "Error fetching socials");
- return res.data?.Social;
- });
+ if (res?.errors) throw new Error(res?.errors[0]?.message || 'Error fetching socials')
+ return res.data?.Social
+ })
} catch (err: unknown) {
- throw new Error("Error fetching socials");
+ throw new Error('Error fetching socials')
}
}
@@ -115,19 +115,19 @@ export const fetchGlobals = async (): Promise<{
// initiate requests in parallel, then wait for them to resolve
// this will eagerly start to the fetch requests at the same time
// see https://nextjs.org/docs/app/building-your-application/data-fetching/fetching
- const settingsData = fetchSettings();
- const headerData = fetchHeader();
- const footerData = fetchFooter();
- const socialsData = fetchSocials();
+ const settingsData = fetchSettings()
+ const headerData = fetchHeader()
+ const footerData = fetchFooter()
+ const socialsData = fetchSocials()
const [settings, header, footer, socials]: [Settings, Header, Footer, Social] = await Promise.all(
[await settingsData, await headerData, await footerData, await socialsData],
- );
+ )
return {
settings,
header,
footer,
socials,
- };
-};
+ }
+}
diff --git a/src/app/_blocks/HubContentGrid/page.tsx b/src/app/_blocks/HubContentGrid/page.tsx
new file mode 100644
index 0000000..96a31d2
--- /dev/null
+++ b/src/app/_blocks/HubContentGrid/page.tsx
@@ -0,0 +1,56 @@
+'use client'
+
+import { useState } from 'react'
+
+import ContentCard from '../../_components/ContentCard'
+import ContentNavBar from '../../_components/ContentNavBar/ContentNavBar'
+import { ContentTypeArrays } from '../../_interfaces/ContentTypeArrays'
+import { filterArticles } from '../../_utilities/filterArticles'
+import styles from './styles.module.css'
+
+const colorMap = {
+ All: 'var(--dark-rock-800)',
+ Blogposts: 'var(--sub-blue-600)',
+ PodcastEpisodes: 'var(--sub-purple-600)',
+ CaseStudies: 'var(--sub-orange-800)',
+ TalksAndRoundtables: 'var(--sub-purple-300)',
+}
+
+interface HubContentGridProps {
+ articles: ContentTypeArrays
+}
+
+export default function HubContentGrid({ articles }: HubContentGridProps) {
+ // todo: fix rendering when there is no content
+
+ const [activeButton, setActiveButton] = useState('All')
+
+ const handleActiveButtonChange = (buttonName: keyof ContentTypeArrays) => {
+ setActiveButton(buttonName)
+ }
+
+ const filteredArticles = filterArticles({
+ articles: articles,
+ filter: activeButton,
+ })
+
+ return (
+ <>
+ {/* ToDo: add conditional management of border-top-right-radius based on dropdown toggle */}
+
+
+
+ {filteredArticles.map((article, i) => (
+
+
+
+ ))}
+
+
+ >
+ )
+}
diff --git a/src/app/_blocks/HubContentGrid/styles.module.css b/src/app/_blocks/HubContentGrid/styles.module.css
new file mode 100644
index 0000000..f9917e1
--- /dev/null
+++ b/src/app/_blocks/HubContentGrid/styles.module.css
@@ -0,0 +1,51 @@
+/* Content grid */
+.contentGridContainer {
+ position: relative;
+ background-color: #FFFFFF;
+ width: max(calc(100% - 32px), calc(100% - 160px));
+ margin: 40px auto;
+ border-bottom-right-radius: 45px;
+ border-bottom-left-radius: 45px;
+ border-top-right-radius: 45px;
+ border: 1px solid var(--dark-rock-800) !important;
+ border-top: none;
+ overflow: hidden;
+ z-index: 3;
+}
+
+.contentGridContainer::after {
+ content: '';
+ position: absolute;
+ border-top: 2px solid var(--dark-rock-800);
+ width: 22.6%;
+ top: 0;
+ right: 0;
+}
+
+.contentGrid {
+ padding: 0;
+ margin: 0 auto;
+ display: grid;
+ grid-template-columns: 1fr;
+
+}
+
+.contentCard {
+ padding: 50px 20px;
+ height: min-content;
+ border: 1px solid var(--dark-rock-800);
+ border-top: none;
+}
+
+@media(min-width: 1024px) {
+ .contentGrid {
+ grid-template-columns: repeat(3, 1fr);
+ }
+
+ .contentGridContainer {
+ border-top: 1px solid;
+ border-radius: 25px;
+ margin: 0 auto;
+ }
+
+}
diff --git a/src/app/_blocks/Subscribe/index.tsx b/src/app/_blocks/Subscribe/index.tsx
index 2c88193..24955ec 100644
--- a/src/app/_blocks/Subscribe/index.tsx
+++ b/src/app/_blocks/Subscribe/index.tsx
@@ -1,4 +1,4 @@
-import styles from "./styles.module.css";
+import styles from './styles.module.css'
export function Subscribe() {
return (
@@ -7,13 +7,15 @@ export function Subscribe() {
- );
+ )
}
diff --git a/src/app/_components/AuthorSummary/index.tsx b/src/app/_components/AuthorSummary/index.tsx
index e8f7e4b..7b11c61 100644
--- a/src/app/_components/AuthorSummary/index.tsx
+++ b/src/app/_components/AuthorSummary/index.tsx
@@ -1,34 +1,27 @@
-import React from "react";
+import React from 'react'
-import { Author } from "../../../payload/payload-types";
-import FeaturedImage from "../FeaturedImage";
-import SocialLinks from "../SocialLinks";
-
-import styles from "./styles.module.css";
+import { Author } from '../../../payload/payload-types'
+import FeaturedImage from '../FeaturedImage'
+import SocialLinks from '../SocialLinks'
+import styles from './styles.module.css'
export default function AuthorSummary({ author }: { author: Author }) {
- const { name, role, bio, linkedIn, gitHub, medium, x, featuredImage } = author;
+ const { name, role, bio, linkedIn, gitHub, medium, x, featuredImage } = author
// TODO: Convert this to an array with names in collection config
- const socials: string[] = [linkedIn, gitHub, medium, x].filter(Boolean);
+ const socials: string[] = [linkedIn, gitHub, medium, x].filter(Boolean)
return (
-
-
- );
+ )
}
diff --git a/src/app/_components/BackButton/index.tsx b/src/app/_components/BackButton/index.tsx
index 25ee069..3116ccf 100644
--- a/src/app/_components/BackButton/index.tsx
+++ b/src/app/_components/BackButton/index.tsx
@@ -1,10 +1,10 @@
// TODO: update href to filter for the same category
import Link from 'next/link'
-import BackArrow from "../../_icons/BackArrow";
+import BackArrow from '../../_icons/BackArrow'
import styles from './styles.module.css'
-export default function BackButton({className}) {
+export default function BackButton({ className }) {
return (
<>
diff --git a/src/app/_components/ContentNavBar/Buttons/index.tsx b/src/app/_components/ContentNavBar/Buttons/index.tsx
new file mode 100644
index 0000000..de19d77
--- /dev/null
+++ b/src/app/_components/ContentNavBar/Buttons/index.tsx
@@ -0,0 +1,132 @@
+import styles from './styles.module.css'
+
+import { AllIcon, BlogpostIcon, CaseStudiesIcon, PodcastIcon, TalksIcon } from '@/app/_icons/icons'
+
+export function AllContent({ fill = 'var(--dark-rock-800)', textColor = 'var(--soft-white-100)' }) {
+ return (
+
+ {/* Change fill based on state? */}
+
+
+
+
+ All
+
+
+ )
+}
+
+export function Blogposts({ fill = 'var(--sub-blue-600)', textColor = 'var(--soft-white-100)' }) {
+ return (
+
+
+
+
+
+ Blogposts
+
+
+ )
+}
+
+export function Podcasts({ fill = 'var(--sub-purple-600)', textColor = 'var(--soft-white-100)' }) {
+ return (
+
+
+
+
+
+ Podcasts
+
+
+ )
+}
+
+export function CaseStudies({
+ fill = 'var(--sub-orange-800)',
+ textColor = 'var(--soft-white-100)',
+}) {
+ return (
+
+
+
+
+
+ Case Studies
+
+
+ )
+}
+
+export function Talks({ fill = 'var(--sub-purple-300)', textColor = 'var(--soft-white-100)' }) {
+ return (
+
+
+
+
+
+ Talks & Roundtables
+
+
+ )
+}
diff --git a/src/app/_components/ContentNavBar/Buttons/styles.module.css b/src/app/_components/ContentNavBar/Buttons/styles.module.css
new file mode 100644
index 0000000..a1f624c
--- /dev/null
+++ b/src/app/_components/ContentNavBar/Buttons/styles.module.css
@@ -0,0 +1,6 @@
+.buttonText {
+ text-anchor: start;
+ dominant-baseline: middle;
+ font-size: 16px;
+ font-weight: bold;
+}
diff --git a/src/app/_components/ContentNavBar/ContentNavBar.tsx b/src/app/_components/ContentNavBar/ContentNavBar.tsx
new file mode 100644
index 0000000..7d51a63
--- /dev/null
+++ b/src/app/_components/ContentNavBar/ContentNavBar.tsx
@@ -0,0 +1,99 @@
+'use client'
+
+import { AllContent, Blogposts, CaseStudies, Podcasts, Talks } from './Buttons'
+import DropdownMenu from './DropdownMenu'
+import styles from './styles.module.css'
+
+import { ContentTypeArrays } from '@/app/_interfaces/ContentTypeArrays'
+
+export default function ContentNavBar({
+ activeButton,
+ onActiveButtonChange,
+}: {
+ activeButton: string
+ onActiveButtonChange: (buttonName: keyof ContentTypeArrays | 'All') => void
+}) {
+ const handleButtonClick = (buttonName: keyof ContentTypeArrays | 'All') => {
+ onActiveButtonChange(buttonName)
+ }
+
+ return (
+ // todo: make whole button clickable?
+
+
+
+
+
handleButtonClick('All')}
+ >
+
+
+
handleButtonClick('Blogposts')}
+ >
+
+
+
handleButtonClick('PodcastEpisodes')}
+ >
+
+
+
handleButtonClick('CaseStudies')} // Use the correct name
+ >
+
+
+
handleButtonClick('TalksAndRoundtables')}
+ >
+
+
+
+ )
+}
diff --git a/src/app/_components/ContentNavBar/DropdownMenu/index.tsx b/src/app/_components/ContentNavBar/DropdownMenu/index.tsx
new file mode 100644
index 0000000..aaddc24
--- /dev/null
+++ b/src/app/_components/ContentNavBar/DropdownMenu/index.tsx
@@ -0,0 +1,91 @@
+import { useState } from 'react'
+
+import {
+ AllIcon,
+ BlogpostIcon,
+ CaseStudiesIcon,
+ ChevronDownIcon,
+ ChevronUpIcon,
+ PodcastIcon,
+ TalksIcon,
+} from '../../../_icons/icons'
+import { ContentTypeArrays } from '../../../_interfaces/ContentTypeArrays'
+import styles from './styles.module.css'
+
+const iconMap = {
+ All: ,
+ Blogposts: ,
+ PodcastEpisodes: ,
+ CaseStudies: ,
+ TalksAndRoundtables: ,
+}
+
+const labelMap = {
+ All: 'All',
+ Blogposts: 'Blogposts',
+ PodcastEpisodes: 'Podcasts',
+ CaseStudies: 'Case Studies',
+ TalksAndRoundtables: 'Talks & Roundtables',
+}
+
+export default function DropdownMenu({
+ activeButton,
+ onActiveButtonChange,
+}: {
+ activeButton: string
+ onActiveButtonChange: () => void
+}) {
+ const [isDropdownActive, setIsDropdownActive] = useState(false)
+ const [buttonLabel, setButtonLabel] = useState('All')
+
+ const handleButtonClick = (buttonName: keyof ContentTypeArrays | 'All') => {
+ onActiveButtonChange(buttonName)
+ setIsDropdownActive(false)
+ setButtonLabel(labelMap[buttonName])
+ }
+
+ return (
+
+
+
+
+
setIsDropdownActive(!isDropdownActive)}
+ >
+ {iconMap[activeButton]} {buttonLabel}
+ {isDropdownActive ? : }
+
+ {isDropdownActive && (
+
+
+
+ handleButtonClick('All')}>{iconMap['All']} All
+ handleButtonClick('Blogposts')}>
+ {iconMap['Blogposts']} Blogposts
+
+ handleButtonClick('PodcastEpisodes')}>
+ {iconMap['PodcastEpisodes']} Podcasts
+
+ handleButtonClick('CaseStudies')}>
+ {iconMap['CaseStudies']} Case Studies
+
+ handleButtonClick('TalksAndRoundtables')}>
+ {iconMap['TalksAndRoundtables']} Talks & Roundtables
+
+
+
+
+ )}
+
+ )
+}
diff --git a/src/app/_components/ContentNavBar/DropdownMenu/styles.module.css b/src/app/_components/ContentNavBar/DropdownMenu/styles.module.css
new file mode 100644
index 0000000..4cd5528
--- /dev/null
+++ b/src/app/_components/ContentNavBar/DropdownMenu/styles.module.css
@@ -0,0 +1,82 @@
+.dropdownContainer {
+ width: 100%;
+ padding: 0;
+ z-index: 5;
+}
+
+.dropdownButton {
+ display: flex;
+ position: relative;
+ align-items: center;
+ width: 208px;
+ margin-left: 20px;
+ margin-top: -40px;
+ padding: 8px 10px;
+ border-radius: 100px;
+ border: none;
+ background-color: var(--soft-white-100);
+ color: var(--dark-rock-800);
+ margin-bottom: 6px;
+ font-weight: bold;
+ gap: 8px;
+ z-index: 5;
+}
+
+.dropdownMenuContainer {
+ position: relative;
+ border-left: 2px solid var(--dark-rock-800);
+ background-color: white;
+ margin-top: -8px;
+ margin-bottom: -40px;
+ padding-top: 6px;
+}
+
+.dropdownMenuContainer::after {
+ content: '';
+ position: absolute;
+ border-top: 2px solid var(--dark-rock-800);
+ border-right: 2px solid var(--dark-rock-800);
+ border-top-right-radius: 45px;
+ width: 24.5%;
+ height: 100%;
+ top: 0;
+ right: 0;
+}
+
+
+.dropdownMenu {
+ width: 208px;
+ margin-left: 20px;
+ background-color: var(--soft-white-100);
+ font-weight: bold;
+ border-radius: 15px;
+}
+
+.dropdownMenu ul {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ gap: 20px;
+
+}
+
+.dropdownMenu li {
+ display: flex;
+ align-items: center;
+ padding: 8px 16px;
+ gap: 8px;
+ margin-bottom: 10px;
+ transition: 0.3s ease;
+}
+
+.dropdownMenu li:hover {
+ background-color: #E4E4E4;
+ transition: 0.3s ease;
+
+}
+
+@media (min-width: 1024px) {
+ .dropdownContainer {
+ display: none;
+ }
+}
diff --git a/src/app/_components/ContentNavBar/styles.module.css b/src/app/_components/ContentNavBar/styles.module.css
new file mode 100644
index 0000000..0f2a380
--- /dev/null
+++ b/src/app/_components/ContentNavBar/styles.module.css
@@ -0,0 +1,52 @@
+/* Content nav */
+.contentNav {
+ display: flex;
+ position: relative;
+ padding: 0;
+ width: var(--content-width);
+ margin: auto auto -48px;
+ flex-wrap: wrap;
+ z-index: 5;
+ font-size: 12px;
+}
+
+.button {
+ background-color: transparent;
+ border: none;
+ padding: 0;
+ width: fit-content;
+ position: relative;
+ display: none;
+}
+
+.activeButton {
+ z-index: 1;
+}
+
+
+.blogpostsButton {
+ margin-left: -120px;
+}
+
+.podcastsButton {
+ margin-left: -120px;
+}
+
+.caseStudiesButton {
+ margin-left: -120px;
+}
+
+.talksButton {
+ margin-left: -80px;
+}
+
+@media (min-width: 1024px) {
+ .contentNav {
+ margin: auto auto -18px;
+ z-index: 0;
+ }
+
+ .button {
+ display: flex;
+ }
+}
diff --git a/src/app/_components/FeaturedImage/index.tsx b/src/app/_components/FeaturedImage/index.tsx
index 2763e72..f3770bf 100644
--- a/src/app/_components/FeaturedImage/index.tsx
+++ b/src/app/_components/FeaturedImage/index.tsx
@@ -1,10 +1,7 @@
+import { className } from 'postcss-selector-parser'
+
import { getImage } from '../../_utilities/getImage'
-import { className } from "postcss-selector-parser";
export default function FeaturedImage({ src, className }) {
- return (
-
-
-
- )
+ return
}
diff --git a/src/app/_components/Header/DropDownIcon/index.tsx b/src/app/_components/Header/DropDownIcon/index.tsx
index c3f3cdb..023407a 100644
--- a/src/app/_components/Header/DropDownIcon/index.tsx
+++ b/src/app/_components/Header/DropDownIcon/index.tsx
@@ -1,10 +1,14 @@
-
-
-export default function DropDownIcon({className}) {
+export default function DropDownIcon({ className }) {
return (
-
-
-
+
+
+
)
}
diff --git a/src/app/_components/Header/Logo/index.tsx b/src/app/_components/Header/Logo/index.tsx
index 27f382e..dc81560 100644
--- a/src/app/_components/Header/Logo/index.tsx
+++ b/src/app/_components/Header/Logo/index.tsx
@@ -1,9 +1,16 @@
export default function Logo() {
return (
-
+
+ fill="#403F4C"
+ />
- );
+ )
}
diff --git a/src/app/_components/Header/index.tsx b/src/app/_components/Header/index.tsx
index 7fe9c22..ca40c41 100644
--- a/src/app/_components/Header/index.tsx
+++ b/src/app/_components/Header/index.tsx
@@ -1,17 +1,17 @@
-import Link from "next/link";
+import Link from 'next/link'
-import { Header } from "../../../payload/payload-types";
-import { fetchHeader } from "../../_api/fetchGlobals";
-import styles from "./styles.module.css";
+import { Header } from '../../../payload/payload-types'
+import { fetchHeader } from '../../_api/fetchGlobals'
+import DropDownIcon from './DropDownIcon'
+import styles from './styles.module.css'
-import DropDownIcon from "./DropDownIcon";
-import Logo from "@/app/_components/Header/Logo";
+import Logo from '@/app/_components/Header/Logo'
export async function Header() {
- let header: Header | null = null;
+ let header: Header | null = null
try {
- header = await fetchHeader();
+ header = await fetchHeader()
} catch (error) {
// When deploying this template on Payload Cloud, this page needs to build before the APIs are live
// So swallow the error here and simply render the header without nav items if one occurs
@@ -19,13 +19,13 @@ export async function Header() {
// console.error(error)
}
- const navItems = header?.navItems || [];
+ const navItems = header?.navItems || []
return (
<>
-
+
{/* TODO: Conditionally format Content Hub to reflect active link?*/}
@@ -35,14 +35,14 @@ export async function Header() {
{link.label}
- );
+ )
})}
-
+
CONTACT US
>
- );
+ )
}
diff --git a/src/app/_components/SearchBar/index.tsx b/src/app/_components/SearchBar/index.tsx
new file mode 100644
index 0000000..a252bb0
--- /dev/null
+++ b/src/app/_components/SearchBar/index.tsx
@@ -0,0 +1,11 @@
+import styles from './styles.module.css'
+
+import { MagnifyingGlass } from '@/app/_icons/icons'
+export default function SearchBar() {
+ return (
+
+
+
+
+ )
+}
diff --git a/src/app/_components/SearchBar/styles.module.css b/src/app/_components/SearchBar/styles.module.css
new file mode 100644
index 0000000..ee9905c
--- /dev/null
+++ b/src/app/_components/SearchBar/styles.module.css
@@ -0,0 +1,32 @@
+/* Search bar */
+
+.searchBar {
+ display: flex;
+ margin: 40px auto;
+ border-radius: 100px;
+ border: 1px solid var(--dark-rock-800);
+ width: 328px;
+ overflow: hidden;
+ padding: 10px 10px 10px 20px;
+ gap: 10px;
+ align-items: center;
+}
+
+.searchInput {
+ display: flex;
+ border: none;
+ width: 100%;
+}
+
+.searchInput:focus,
+.searchInput:active {
+ border: none;
+ outline: none;
+}
+
+@media (min-width: 1024px) {
+ .searchBar {
+ width: 836px;
+ margin: 80px auto;
+ }
+}
diff --git a/src/app/_components/SocialLinks/index.tsx b/src/app/_components/SocialLinks/index.tsx
index bc0f8c4..6cbc7a9 100644
--- a/src/app/_components/SocialLinks/index.tsx
+++ b/src/app/_components/SocialLinks/index.tsx
@@ -1,5 +1,6 @@
import Link from 'next/link'
-import LinkedInIcon from "@/app/_icons/LinkedInIcon";
+
+import LinkedInIcon from '@/app/_icons/LinkedInIcon'
export default function SocialLinks({ socials }: { socials: string[] }) {
return (
diff --git a/src/app/_css/globals.css b/src/app/_css/globals.css
index d539a89..12c0981 100644
--- a/src/app/_css/globals.css
+++ b/src/app/_css/globals.css
@@ -24,6 +24,8 @@
--sub-purple-100: #D4BEFE;
--sub-purple-50: #EFE5FF;
+ --sub-orange-800: #F36312;
+
--dark-rock-800: #403F4C;
--soft-white-100: #F6F6F6;
@@ -33,6 +35,10 @@
--font-weight-500: 500;
--font-weight-800: 800;
+ /* Content Sizing */
+ --content-width: max(calc(100% - 32px), calc(100% - 160px));
+
+
/* Font Sizes */
--size-160: 160px;
--size-113: 113px;
@@ -50,6 +56,7 @@
--size-14: 14px;
--size-12: 12px;
--size-13: 13px;
+ --size-10: 10px;
} /* end of :root */
*, *::before, *::after {
@@ -61,11 +68,13 @@
}
body {
+ max-width: 1728px;
+ margin: 0 auto;
font-size: var(--size-14);
text-rendering: optimizeLegibility;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
- font-family: var(--colfax-regular);
+ font-family: var(--colfax);
}
img, picture, video, canvas, svg {
@@ -145,4 +154,4 @@ h6 {
font-size: var(--size-28);
}
-}
\ No newline at end of file
+}
diff --git a/src/app/_graphql/allContent.ts b/src/app/_graphql/allContent.ts
index 32eb165..cbd22ca 100644
--- a/src/app/_graphql/allContent.ts
+++ b/src/app/_graphql/allContent.ts
@@ -1,9 +1,8 @@
-import { MEDIA_FIELDS } from "@/app/_graphql/media";
-
export const ALL_CONTENT = `
query Content {
- PodcastEpisodes {
+ PodcastEpisodes(limit: 50) {
docs {
+ id
slug
title
summary
@@ -22,8 +21,9 @@ query Content {
}
}
}
- Blogposts {
+ Blogposts(limit: 50) {
docs {
+ id
slug
title
summary
@@ -42,13 +42,15 @@ query Content {
}
}
}
- CaseStudies {
+ CaseStudies(limit: 50) {
docs {
+ id
slug
}
}
- TalksAndRoundtables {
+ TalksAndRoundtables(limit: 50) {
docs {
+ id
slug
}
}
diff --git a/src/app/_icons/BackArrow.tsx b/src/app/_icons/BackArrow.tsx
index 9420f47..5c26fd0 100644
--- a/src/app/_icons/BackArrow.tsx
+++ b/src/app/_icons/BackArrow.tsx
@@ -1,12 +1,22 @@
-import { className } from "postcss-selector-parser";
+import { className } from 'postcss-selector-parser'
-export default function BackArrow({className}) {
+export default function BackArrow({ className }) {
return (
-
+
-
+
-
)
}
diff --git a/src/app/_icons/LinkedInIcon.tsx b/src/app/_icons/LinkedInIcon.tsx
index 5c24d52..13c90b5 100644
--- a/src/app/_icons/LinkedInIcon.tsx
+++ b/src/app/_icons/LinkedInIcon.tsx
@@ -2,7 +2,10 @@ export default function LinkedInIcon() {
return (
-
+
)
}
diff --git a/src/app/_icons/icons.tsx b/src/app/_icons/icons.tsx
new file mode 100644
index 0000000..68d3d1f
--- /dev/null
+++ b/src/app/_icons/icons.tsx
@@ -0,0 +1,452 @@
+export function MagnifyingGlass({ x, y, width = '24', height = '24', stroke = 'black' }) {
+ return (
+
+
+
+ )
+}
+
+export function AllIcon({ x, y, width = '24', height = '24', stroke = 'black' }) {
+ return (
+
+
+
+
+
+
+ )
+}
+
+export function BlogpostIcon({ x, y, width = '24', height = '24', stroke = 'black' }) {
+ return (
+
+
+
+ )
+}
+
+export function PodcastIcon({ x, y, width = '24', height = '24', stroke = 'black' }) {
+ return (
+
+
+
+ )
+}
+
+export function CaseStudiesIcon({ x, y, width = '24', height = '24', stroke = 'black' }) {
+ return (
+
+
+
+ )
+}
+
+export function TalksIcon({ x, y, width = '24', height = '24', fill = 'black' }) {
+ return (
+
+
+
+ )
+}
+
+export function SpectaclesIcon({ x, y }) {
+ return (
+
+
+
+ )
+}
+
+export function ShareIcon({ x, y }) {
+ return (
+
+
+
+ )
+}
+
+export function XIcon({ x, y }) {
+ return (
+
+
+
+ )
+}
+
+export function ChevronUpIcon({ x, y }) {
+ return (
+
+
+
+ )
+}
+
+export function ChevronDownIcon({ x, y }) {
+ return (
+
+
+
+ )
+}
+
+export function RSSIcon({ x, y }) {
+ return (
+
+
+
+ )
+}
+
+export function MuteIcon({ x, y }) {
+ return (
+
+
+
+ )
+}
+
+export function LowerVolume({ x, y }) {
+ return (
+
+
+
+ )
+}
+
+export function RaiseVolumeIcon({ x, y }) {
+ return (
+
+
+
+ )
+}
+
+export function PlayIcon({ x, y }) {
+ return (
+
+
+
+ )
+}
+
+export function MoveFifteenIcon({ x, y }) {
+ return (
+
+
+
+
+ )
+}
+
+export function BackFifteenIcon({ x, y }) {
+ return (
+
+
+
+
+ )
+}
+
+export function HeadphonesIcon({ x, y }) {
+ return (
+
+
+
+ )
+}
+
+export function LinkIcon({ x, y }) {
+ return (
+
+
+
+ )
+}
diff --git a/src/app/_interfaces/ContentTypeArrays.ts b/src/app/_interfaces/ContentTypeArrays.ts
new file mode 100644
index 0000000..28cae6c
--- /dev/null
+++ b/src/app/_interfaces/ContentTypeArrays.ts
@@ -0,0 +1,13 @@
+import type {
+ Blogpost,
+ CaseStudy,
+ PodcastEpisode,
+ TalksAndRoundtable,
+} from '@/payload/payload-types'
+
+export interface ContentTypeArrays {
+ Blogposts: Blogpost[]
+ PodcastEpisodes: PodcastEpisode[]
+ CaseStudies: CaseStudy[]
+ TalksAndRoundtables: TalksAndRoundtable[]
+}
diff --git a/src/app/_utilities/fetcher.ts b/src/app/_utilities/fetcher.ts
index 511bbd1..5a753e9 100644
--- a/src/app/_utilities/fetcher.ts
+++ b/src/app/_utilities/fetcher.ts
@@ -1,7 +1,11 @@
-import { GRAPHQL_API_URL } from "@/app/_api/shared";
+import { GRAPHQL_API_URL } from '../_api/shared'
-export async function fetcher(args: { query: string; variables?: Record }) {
+import type { ContentTypeArrays } from '@/app/_interfaces/ContentTypeArrays'
+export async function fetcher(args: {
+ query: string
+ variables?: Record
+}): Promise {
const { query, variables } = args
try {
@@ -24,6 +28,6 @@ export async function fetcher(args: { query: string; variables?: Record {
+ if (filter === 'All') {
+ const keys = Object.keys(articles) as Array
+
+ return keys.flatMap(articleType =>
+ articles[articleType].map(article => ({
+ contentType: articleType,
+ content: article,
+ })),)
+ }
+
+ return articles[filter]?.map(article => ({
+ contentType: filter,
+ content: article,
+ }))
+}
diff --git a/src/payload/globals/Socials.ts b/src/payload/globals/Socials.ts
index f97bee3..b998c22 100644
--- a/src/payload/globals/Socials.ts
+++ b/src/payload/globals/Socials.ts
@@ -1,16 +1,16 @@
-import type { GlobalConfig } from "payload/types";
+import type { GlobalConfig } from 'payload/types'
-import link from "../fields/link";
+import link from '../fields/link'
export const Socials: GlobalConfig = {
- slug: "socials",
+ slug: 'socials',
access: {
read: () => true,
},
fields: [
{
- name: "socials",
- type: "array",
+ name: 'socials',
+ type: 'array',
fields: [
{
name: 'name',
@@ -25,5 +25,4 @@ export const Socials: GlobalConfig = {
],
},
],
-
-};
+}
diff --git a/src/payload/payload.config.ts b/src/payload/payload.config.ts
index f74a8b4..d4bf114 100644
--- a/src/payload/payload.config.ts
+++ b/src/payload/payload.config.ts
@@ -1,7 +1,6 @@
import { webpackBundler } from '@payloadcms/bundler-webpack'
import { mongooseAdapter } from '@payloadcms/db-mongodb'
import { lexicalEditor } from '@payloadcms/richtext-lexical'
-
import dotenv from 'dotenv'
import path from 'path'
import { buildConfig } from 'payload/config'
diff --git a/tsconfig.json b/tsconfig.json
index 69aaaa8..1784325 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -9,7 +9,7 @@
],
"allowJs": true,
"skipLibCheck": true,
- "strict": false,
+ "strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"incremental": true,