diff --git a/src/app/(pages)/[slug]/page.tsx b/src/app/(pages)/[slug]/page.tsx index b8b4c93..9e6a7e3 100644 --- a/src/app/(pages)/[slug]/page.tsx +++ b/src/app/(pages)/[slug]/page.tsx @@ -2,9 +2,13 @@ import { fetchAllContentByType, fetchGlobals } from "@/app/_utilities/contentFet import HubContentGrid from "@/app/_blocks/HubContentGrid"; import HubHead from "@/app/_blocks/HubHead"; import SearchBar from "@/app/_components/SearchBar"; +import { Header } from "@/app/_components/Header"; +import { Metadata } from "next"; + +export const dynamic = "force-dynamic"; +export const revalidate = 0; + -export const dynamic = 'force-dynamic' -export const revalidate = 0 export default async function Page() { const content = { @@ -20,6 +24,7 @@ export default async function Page() { return (
+
@@ -31,7 +36,25 @@ export default async function Page() {
- - ); } + +export function generateMetadata(): Metadata { + return { + title: 'Styleguide Page', + description: 'A guide to various styles in our application.', + openGraph: { + title: 'Styleguide Open Graph Title', + description: 'This is the Open Graph description for the Styleguide page.', + url: 'https://example.com/styleguide', + images: [ + { + url: 'https://example.com/og-image.jpg', + width: 800, + height: 600, + alt: 'Og Image Alt Text', + }, + ], + }, + }; +} diff --git a/src/app/(pages)/authors/[slug]/page.tsx b/src/app/(pages)/authors/[slug]/page.tsx index 896d079..f94606c 100644 --- a/src/app/(pages)/authors/[slug]/page.tsx +++ b/src/app/(pages)/authors/[slug]/page.tsx @@ -5,11 +5,19 @@ import AuthorSummary from '../../../_components/AuthorSummary' import BackButton from '../../../_components/BackButton' import styles from './styles.module.css' +import { Header } from "../../../_components/Header"; import { fetchContentBySlug } from "@/app/_utilities/contentFetchers"; import { Author } from "@/payload-types"; +import { Metadata } from "next"; export const dynamic = 'force-dynamic' +const headerStyle = { + '--dynamic-background': 'var(--sub-purple-50)', + '--dynamic-color': 'var(--dark-rock-800)', + '--dynamic-width': 'calc(100% - 40px)', +} + export default async function ContributorPage({ params: paramsPromise }) { const { slug } = await paramsPromise const author = await fetchContentBySlug({ slug: slug, type: 'authors' }) @@ -17,6 +25,7 @@ export default async function ContributorPage({ params: paramsPromise }) { return (
+
@@ -27,3 +36,16 @@ export default async function ContributorPage({ params: paramsPromise }) {
) } + +export async function generateMetadata({ params: paramsPromise}): Promise { + const { slug } = await paramsPromise + const author = await fetchContentBySlug({ + slug: slug, + type: "authors", + }) + + return { + // @ts-ignore + title: `Subvisual | ${author.name}` + } +} diff --git a/src/app/(pages)/blogposts/[slug]/page.tsx b/src/app/(pages)/blogposts/[slug]/page.tsx index ffaac58..f334a9b 100644 --- a/src/app/(pages)/blogposts/[slug]/page.tsx +++ b/src/app/(pages)/blogposts/[slug]/page.tsx @@ -13,6 +13,14 @@ import styles from "./styles.module.css"; import { fetchContentBySlug } from "@/app/_utilities/contentFetchers"; import { Blogpost } from "@/payload-types"; +import { Header } from "@/app/_components/Header"; +import { Metadata } from "next"; + +const headerStyle = { + '--dynamic-background': 'var(--sub-purple-400)', + '--dynamic-color': 'var(--soft-white-100)', + '--dynamic-width': 'calc(100% - 40px)', +} export default async function BlogpostPage({ params: paramsPromise }) { const { slug } = await paramsPromise; @@ -23,8 +31,11 @@ export default async function BlogpostPage({ params: paramsPromise }) { depth: 3, }); + + return (
+
@@ -54,3 +65,16 @@ export default async function BlogpostPage({ params: paramsPromise }) {
); } + +export async function generateMetadata({ params: paramsPromise}): Promise { + const { slug } = await paramsPromise + const blogpost = await fetchContentBySlug({ + slug: slug, + type: "blogposts", + }) + + return { + // @ts-ignore + title: `Subvisual | ${blogpost.title}` + } +} diff --git a/src/app/(pages)/layout.tsx b/src/app/(pages)/layout.tsx index c1b9ad6..7ba93e7 100644 --- a/src/app/(pages)/layout.tsx +++ b/src/app/(pages)/layout.tsx @@ -11,11 +11,11 @@ export default async function RootLayout({ children }: { children: React.ReactNo return ( - - + + -
+ {/*
*/} {children}
@@ -25,9 +25,5 @@ export default async function RootLayout({ children }: { children: React.ReactNo export const metadata: Metadata = { metadataBase: new URL(process.env.NEXT_PUBLIC_SERVER_URL || 'https://payloadcms.com'), - twitter: { - card: 'summary_large_image', - creator: '@payloadcms', - }, openGraph: mergeOpenGraph(), } diff --git a/src/app/(pages)/page.tsx b/src/app/(pages)/page.tsx index f9710d2..c853741 100644 --- a/src/app/(pages)/page.tsx +++ b/src/app/(pages)/page.tsx @@ -1,6 +1,13 @@ import PageTemplate from './[slug]/page' +import { Metadata } from "next"; export const dynamic = 'auto' export const revalidate = 10 export default PageTemplate + +export function generateMetadata() : Metadata { + return { + title: 'Subvisual Content Hub Home' + } +} diff --git a/src/app/(pages)/podcasts/[slug]/page.tsx b/src/app/(pages)/podcasts/[slug]/page.tsx index 5150ff8..125b0b0 100644 --- a/src/app/(pages)/podcasts/[slug]/page.tsx +++ b/src/app/(pages)/podcasts/[slug]/page.tsx @@ -5,11 +5,18 @@ import EpisodeContent from '../../../_blocks/EpisodeContent' import EpisodeHead from '../../../_blocks/EpisodeHead' import { RelatedContent } from '../../../_blocks/RelatedContent' import { Subscribe } from '../../../_blocks/Subscribe' - +import { Header } from '@/app/_components/Header' import { fetchContentBySlug } from "@/app/_utilities/contentFetchers"; +import { Metadata } from "next"; export const dynamic = 'force-dynamic' +const headerStyle = { + '--dynamic-background': 'var(--sub-purple-400)', + '--dynamic-color': 'var(--soft-white-100)', + '--dynamic-width': 'calc(100% - 40px)', +} + export default async function PodcastEpisodesPage({params: paramsPromise}) { const { slug } = await paramsPromise @@ -23,8 +30,7 @@ export default async function PodcastEpisodesPage({params: paramsPromise}) { return (
- - +
@@ -32,3 +38,17 @@ export default async function PodcastEpisodesPage({params: paramsPromise}) {
) } + +export async function generateMetadata({ params: paramsPromise}): Promise { + const { slug } = await paramsPromise + const podcast = await fetchContentBySlug({ + slug: slug, + type: "podcasts", + }) + + return { + // @ts-ignore + title: `Subvisual | ${podcast.title}`, + + } +} diff --git a/src/app/(payload)/layout.tsx b/src/app/(payload)/layout.tsx index 767040c..2b62958 100644 --- a/src/app/(payload)/layout.tsx +++ b/src/app/(payload)/layout.tsx @@ -1,21 +1,26 @@ /* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */ /* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */ -import configPromise from '@payload-config' -import '@payloadcms/next/css' -import { RootLayout } from '@payloadcms/next/layouts' -import React from 'react' +import configPromise from "@payload-config"; +import "@payloadcms/next/css"; +import { RootLayout } from "@payloadcms/next/layouts"; +import React from "react"; -import './custom.scss' -import { importMap } from './admin/importMap' +import "./custom.scss"; +import { importMap } from "./admin/importMap"; type Args = { children: React.ReactNode } const Layout = ({ children }: Args) => ( - - {children} - -) + <> + + + + + + {children} + +); -export default Layout +export default Layout; diff --git a/src/app/_blocks/BlogpostContent/index.tsx b/src/app/_blocks/BlogpostContent/index.tsx index 781f056..dffa6b3 100644 --- a/src/app/_blocks/BlogpostContent/index.tsx +++ b/src/app/_blocks/BlogpostContent/index.tsx @@ -8,11 +8,15 @@ export default function BlogpostContent({ blogpost }) { const { summary, featuredImage } = blogpost; return (
-
- {featuredImage && } + {featuredImage && ( +
+
+ )}
{summary}
- +
+ +
); } diff --git a/src/app/_blocks/BlogpostContent/styles.module.css b/src/app/_blocks/BlogpostContent/styles.module.css index 685cd8e..2835055 100644 --- a/src/app/_blocks/BlogpostContent/styles.module.css +++ b/src/app/_blocks/BlogpostContent/styles.module.css @@ -5,7 +5,7 @@ .featuredImage { position: relative; - width: 80%px; + width: 80%; height: 340px; margin: auto; border-radius: 45px; @@ -26,3 +26,38 @@ overflow-wrap: break-word; } +.content h1, +.content h2, +.content h3, +.content h4, +.content h5 { + font-size: var(--size-26); + margin-bottom: 20px; +} + +.content p{ + margin-bottom: 20px; +} + +.content a { + color: var(--sub-purple-800); + font-weight: bold; +} + +.content code { + background-color: var(--soft-white-100); + font-weight: bold; +} + +@media (min-width: 1024px) { + + .content h1, + .content h2, + .content h3, + .content h4, + .content h5 { + font-size: var(--size-28); + } + + +} diff --git a/src/app/_blocks/EpisodeContent/Contributors/index.tsx b/src/app/_blocks/EpisodeContent/Contributors/index.tsx index 3f62281..9b2aa3a 100644 --- a/src/app/_blocks/EpisodeContent/Contributors/index.tsx +++ b/src/app/_blocks/EpisodeContent/Contributors/index.tsx @@ -1,12 +1,15 @@ -import AuthorPill from '@/app/_components/AuthorPill' +import { AuthorPill } from "@/app/_components/AuthorPill"; export default function Contributors({ className, authors }) { return (
-

CONTRIBUTORS

+

CONTRIBUTORS

{authors.map((author, i) => ( - +
+ {/* Author has to be passed as an Array for compatibility reasons */} + +
))}
- ) + ); } diff --git a/src/app/_blocks/EpisodeContent/index.tsx b/src/app/_blocks/EpisodeContent/index.tsx index ba51592..6842ebe 100644 --- a/src/app/_blocks/EpisodeContent/index.tsx +++ b/src/app/_blocks/EpisodeContent/index.tsx @@ -6,7 +6,7 @@ import styles from './styles.module.css' import Contributors from '@/app/_blocks/EpisodeContent/Contributors' import ListenOn from '@/app/_blocks/EpisodeContent/ListenOn' import RSSFeed from '@/app/_blocks/EpisodeContent/RSSFeed' -import AuthorPill from '@/app/_components/AuthorPill' + import Categories from '@/app/_components/Categories' import Share from '@/app/_components/Share' diff --git a/src/app/_blocks/EpisodeHead/styles.module.css b/src/app/_blocks/EpisodeHead/styles.module.css index e1b4109..588cf34 100644 --- a/src/app/_blocks/EpisodeHead/styles.module.css +++ b/src/app/_blocks/EpisodeHead/styles.module.css @@ -10,6 +10,7 @@ display: block; font-family: Colfax, sans-serif; padding: 40px 15px; + color: var(--soft-white-100); /* Ensures consistent color */ } .metadataContainer { @@ -23,7 +24,6 @@ display: flex; flex-direction: column; gap: 10px; - grid-row: 1 / 1; grid-column: 1 / 3; padding: 0 20px 30px; } @@ -47,30 +47,18 @@ align-items: center; } -.featuredImageContainer { - display: flex; - position: relative; - justify-content: center; - align-items: center; - grid-row: 2 / 2; - grid-column: 1 / 1; - padding: 10px; - margin: auto; - width: 120px; - height: 120px; -} - .featuredImage { position: relative; - width: max(120px, 294px); + width: 160px; + margin: auto; aspect-ratio: 1 / 1; border-radius: 10px; } .audioPlayer { + grid-column: 2; + grid-row: 2; align-content: center; - grid-column: 2 / 2; - grid-row: 2 / 2; } @media (min-width: 1080px) { @@ -81,12 +69,11 @@ .backButton { padding-left: 95px; - color: var(--soft-white-100); } .metadataContainer { grid-template-columns: 0.6fr 0.4fr; - grid-template-rows: 0.5fr 0.5fr; + grid-template-rows: repeat(2, 0.5fr); padding-left: 95px; column-gap: 50px; } @@ -96,26 +83,21 @@ font-size: var(--size-14); } - .featuredImageContainer { - position: relative; - margin: auto; - width: 324px; - height: 324px; - grid-column: 2 / 2; - grid-row: 1 / 3; + .featuredImage { + width: 290px; } .audioPlayer { display: flex; align-items: center; - grid-column: 1 / 1; + grid-column: 1; } .audioPlayer input[type="range"] { -webkit-appearance: none; height: 6px; border-radius: 100px; - border: none; width: 100%; + border: none; } } diff --git a/src/app/_blocks/HubContentGrid/index.tsx b/src/app/_blocks/HubContentGrid/index.tsx index c4c2440..64e5823 100644 --- a/src/app/_blocks/HubContentGrid/index.tsx +++ b/src/app/_blocks/HubContentGrid/index.tsx @@ -36,8 +36,10 @@ export default function HubContentGrid({ content }) { const filteredContent = filterContent({ articles: content, filter: activeButton }); + return (
+ {/*{
{JSON.stringify(filteredContent, null, 2)}
}*/} {/* TODO: fix dynamic color*/}
diff --git a/src/app/_blocks/HubHead/Highlights/index.tsx b/src/app/_blocks/HubHead/Highlights/index.tsx index 1f7c730..d1df017 100644 --- a/src/app/_blocks/HubHead/Highlights/index.tsx +++ b/src/app/_blocks/HubHead/Highlights/index.tsx @@ -1,9 +1,10 @@ -import AuthorPill from "../../../_components/AuthorPill"; import CategoryPill from "../../../_components/CategoryPill"; import { SpectaclesIcon } from "../../../_icons/icons"; import { estimateReadTime } from "../../../_utilities/estimateReadTime"; import { formatDateTime } from "../../../_utilities/formatDateTime"; import styles from "./styles.module.css"; +import { AuthorPill } from '@/app/_components/AuthorPill' +import Link from "next/link"; const placeholder = { title: "Placeholder", @@ -26,27 +27,29 @@ export async function Highlights({ content, main }) { className={`${styles.highlights} ${main ? styles.mainHighlight : styles.secondaryHighlight}`} >

HIGHLIGHTS

-
{title}
+ {/* TODO: Adapt to other types of content as well */} + +
{title}
+ {content === placeholder ? (
Please setup the highlights
) : ( <>
- {/* TODO: is this a good approach for multi category items? */} - {categories.length > 2 ? ( - - ) : ( - categories.map((category, i) => ) - )} - - {formatDateTime(publishedAt)} + + + {categories.map((category, i) => )} + + + - + {formatDateTime(publishedAt)} + {estimateReadTime("Placeholder")}
- + ) } diff --git a/src/app/_blocks/HubHead/Highlights/styles.module.css b/src/app/_blocks/HubHead/Highlights/styles.module.css index 144d129..efd09de 100644 --- a/src/app/_blocks/HubHead/Highlights/styles.module.css +++ b/src/app/_blocks/HubHead/Highlights/styles.module.css @@ -7,11 +7,19 @@ padding: 30px 20px; } +.highlights h6 { + text-transform: none; + transition: text-decoration 0.3s ease; +} + +.highlights h6:hover { + text-decoration: underline; +} + .overline { font-size: var(--size-16); font-weight: 400; letter-spacing: 0.15rem; - } .mainHighlight { @@ -19,48 +27,41 @@ color: var(--soft-white-100); } +.mainHighlight a { + color: inherit; /* Keeps anchor color consistent with parent */ +} + .secondaryHighlight { display: none; } +/* Metadata container */ .metadataContainer { display: flex; + flex-direction: column; gap: 30px; - align-items: center; } .readTime { display: flex; - flex-direction: row; - gap: 10px; align-items: center; + gap: 10px; } +.readTime svg { + margin-left: 20px; +} -/* Author pill*/ - -.authorPill { +.categories { display: flex; gap: 10px; - align-items: center; - background-color: var(--soft-white-100); - width: fit-content; - border-radius: 100px; - padding: 5px 15px 5px 5px; - color: var(--dark-rock-800); - font-size: var(--size-12); - font-family: var(--colfax); -} - -.authorImage { - width: 34px; - height: 34px; - border-radius: 50%; } +/* Responsive styles for larger screens */ @media (min-width: 1024px) { - .mainHighlight { + .mainHighlight, + .secondaryHighlight { grid-column: 2; } @@ -70,6 +71,5 @@ gap: 20px; border-radius: 45px; border: 1px solid var(--dark-rock-800); - grid-column: 2; } } diff --git a/src/app/_blocks/Subscribe/index.tsx b/src/app/_blocks/Subscribe/index.tsx index d094598..858bd8a 100644 --- a/src/app/_blocks/Subscribe/index.tsx +++ b/src/app/_blocks/Subscribe/index.tsx @@ -8,7 +8,7 @@ export function Subscribe() {
{'subscribe
diff --git a/src/app/_components/AuthorPill/index.tsx b/src/app/_components/AuthorPill/index.tsx index f35fa29..8928622 100644 --- a/src/app/_components/AuthorPill/index.tsx +++ b/src/app/_components/AuthorPill/index.tsx @@ -1,49 +1,63 @@ -import styles from "./styles.module.css"; - +import styles from './styles.module.css'; import FeaturedImage from "@/app/_components/FeaturedImage"; +import React from "react"; import { LinkedInIcon, TwitterIcon } from "@/app/_icons/socialIcons"; -import { getImage } from "@/app/_utilities/getImage"; -import { Author } from "@/payload-types"; -import { fetchMediaByID } from "@/app/_utilities/contentFetchers"; - -export default async function AuthorPill({ large = false, author }) { - - const { name, featuredImage, linkedIn, x } = author; +import Link from "next/link"; - const test = await fetchMediaByID("66ed88055946c64a204c9357"); +export function AuthorPill({ authors }) { - /* unsure if this will be useful, as all names have same length in figma + if (authors.length === 1) { -const dynamicVars = { - '--dynamic-font-weight': large ? 'bold' : 'normal', - '--dynamic-width': large ? '100%' : 'fit-content', -} -*/ - - return ( - <> -
-
- {featuredImage && } + const author = Array.isArray(authors) ? authors[0] : authors + const { name, slug, featuredImage } = author; + return ( + +
+
+ {featuredImage && } +
+ {name} +
+ + ); + } + + if (authors.length === 2) { + const authorOne = authors[0]; + const authorTwo = authors[1]; + + return ( +
+
+
- {name} +
+ +
+ {`${authorOne.name.split(" ")[0]} & ${authorTwo.name.split(" ")[0]}`}
- - {large && ( -
- {linkedIn && ( - - - - )} - {x && ( - - - - )} + ); + + } + + if (authors.length > 2) { + const authorOne = authors[0]; + const authorTwo = authors[1]; + const authorThree = authors[2]; + return ( +
+
+ +
+
+
- )} - - ); +
+ +
+ Various authors +
+ ); + } } diff --git a/src/app/_components/AuthorPill/styles.module.css b/src/app/_components/AuthorPill/styles.module.css index cfb44ed..7d05e7c 100644 --- a/src/app/_components/AuthorPill/styles.module.css +++ b/src/app/_components/AuthorPill/styles.module.css @@ -1,5 +1,22 @@ -.authorPill { +/* General Styles */ +.authorImage { + position: relative; + width: 34px; + height: 34px; + border-radius: 50%; + transition: 0.5s ease; +} + +.socials { display: flex; + gap: 16px; +} + +/* Author Pills */ +.authorPill, +.twoAuthorPill { + display: flex; + position: relative; gap: 10px; align-items: center; background-color: var(--soft-white-100); @@ -13,23 +30,25 @@ transition: 0.5s ease; } -.authorImage { - position: relative; - width: 34px; - height: 34px; - border-radius: 50%; - transition: 0.5s ease; -} - -.authorPill:hover { +.authorPill:hover, +.twoAuthorPill:hover { color: #C1C1C1; } -.authorPill:hover .authorImage { +.authorPill:hover .authorImage, +.twoAuthorPill:hover .authorImage { opacity: 0.5; } -.socials { - display: flex; - gap: 16px; +/* Multiple Authors */ +.authorTwo, +.authorThree { + margin-left: -20px; +} + +@media (min-width: 1024px) { + .authorImage { + position: relative; + } + } diff --git a/src/app/_components/AuthorSummary/index.tsx b/src/app/_components/AuthorSummary/index.tsx index 9e0f6e6..227304a 100644 --- a/src/app/_components/AuthorSummary/index.tsx +++ b/src/app/_components/AuthorSummary/index.tsx @@ -1,20 +1,24 @@ -import React from 'react' +import React from "react"; -import FeaturedImage from '../FeaturedImage' -import SocialLinks from '../SocialLinks' -import styles from './styles.module.css' +import FeaturedImage from "../FeaturedImage"; +import SocialLinks from "../SocialLinks"; +import styles from "./styles.module.css"; import { Author } from "@/payload-types"; export default function AuthorSummary({ 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 = [linkedIn, gitHub, medium, x].filter(Boolean) + const socials = [linkedIn, gitHub, medium, x].filter(Boolean); return (
- {featuredImage && } + {featuredImage && +
+ +
+ }
{name}

{role}

@@ -23,5 +27,5 @@ export default function AuthorSummary({ author }) {
{bio}
- ) + ); } diff --git a/src/app/_components/AuthorSummary/styles.module.css b/src/app/_components/AuthorSummary/styles.module.css index dccc7af..577420c 100644 --- a/src/app/_components/AuthorSummary/styles.module.css +++ b/src/app/_components/AuthorSummary/styles.module.css @@ -14,6 +14,7 @@ } .featuredImage { + position: relative; width: 70px; height: 70px; border-radius: 50%; diff --git a/src/app/_components/Authors/AuthorPills/index.tsx b/src/app/_components/Authors/AuthorPills/index.tsx new file mode 100644 index 0000000..e4fa3b4 --- /dev/null +++ b/src/app/_components/Authors/AuthorPills/index.tsx @@ -0,0 +1,72 @@ +import styles from "@/app/_components/Authors/AuthorPills/styles.module.css"; +import FeaturedImage from "@/app/_components/FeaturedImage"; +import React from "react"; +import { LinkedInIcon, TwitterIcon } from "@/app/_icons/socialIcons"; +import Link from "next/link"; + +export function AuthorPill({ large = false, author }) { + const { name, featuredImage, linkedIn, x } = author; + return ( + +
+
+ {featuredImage && } +
+ {name} +
+ + {large && ( +
+ {linkedIn && ( + + + + )} + {x && ( + + + + )} +
+ )} + + ); +} + +export function TwoAuthorPill({ authors }) { + + const authorOne = authors[0]; + const authorTwo = authors[1]; + + return ( +
+
+ +
+
+ +
+ {`${authorOne.name.split(" ")[0]} & ${authorTwo.name.split(" ")[0]}`} +
+ ); +} + +export function VariousAuthorsPill({ authors }) { + const authorOne = authors[0]; + const authorTwo = authors[1]; + const authorThree = authors[2]; + return ( +
+
+ +
+
+ +
+
+ +
+ Various authors +
+ ); +} diff --git a/src/app/_components/Authors/AuthorPills/styles.module.css b/src/app/_components/Authors/AuthorPills/styles.module.css new file mode 100644 index 0000000..bdfa7c7 --- /dev/null +++ b/src/app/_components/Authors/AuthorPills/styles.module.css @@ -0,0 +1,46 @@ +/* General Styles */ +.authorImage { + position: relative; + width: 34px; + height: 34px; + border-radius: 50%; + transition: 0.5s ease; +} + +.socials { + display: flex; + gap: 16px; +} + +/* Author Pills */ +.authorPill, +.twoAuthorPill { + display: flex; + gap: 10px; + align-items: center; + background-color: var(--soft-white-100); + width: fit-content; + font-weight: bold; + border-radius: 100px; + padding: 5px 15px 5px 5px; + color: var(--dark-rock-800); + font-size: var(--size-12); + font-family: var(--colfax); + transition: 0.5s ease; +} + +.authorPill:hover, +.twoAuthorPill:hover { + color: #C1C1C1; +} + +.authorPill:hover .authorImage, +.twoAuthorPill:hover .authorImage { + opacity: 0.5; +} + +/* Multiple Authors */ +.authorTwo, +.authorThree { + margin-left: -20px; +} diff --git a/src/app/_components/Authors/index.tsx b/src/app/_components/Authors/index.tsx index b5bf02b..2e2da5b 100644 --- a/src/app/_components/Authors/index.tsx +++ b/src/app/_components/Authors/index.tsx @@ -1,15 +1,23 @@ -import React from 'react' +import React from "react"; +import styles from "./AuthorPills/styles.module.css"; -import AuthorPill from '@/app/_components/AuthorPill' +import FeaturedImage from "@/app/_components/FeaturedImage"; +import { LinkedInIcon, TwitterIcon } from "@/app/_icons/socialIcons"; +import Image from "next/image"; +import { TwoAuthorPill } from "@/app/_components/Authors/AuthorPills"; +import { VariousAuthorsPill } from "@/app/_components/Authors/AuthorPills"; +import { AuthorPill } from "@/app/_components/Authors/AuthorPills"; export default function Authors({ authors }) { + + return (
-
- {authors.map((author, i) => ( - - ))} -
+ {authors.length === 1 && } + {authors.length === 2 && } + {authors.length > 2 && }
- ) + + + ); } diff --git a/src/app/_components/ContentCard/index.tsx b/src/app/_components/ContentCard/index.tsx index 1abd4dd..b1dae61 100644 --- a/src/app/_components/ContentCard/index.tsx +++ b/src/app/_components/ContentCard/index.tsx @@ -1,4 +1,4 @@ -import Link from 'next/link' +import Link from "next/link"; import { Blogpost, @@ -6,70 +6,77 @@ import { Category, Podcast, TalksAndRoundtable, -} from '@/payload-types' -import { HeadphonesIcon, SpectaclesIcon } from '../../_icons/icons' -import { estimateReadTime } from '../../_utilities/estimateReadTime' -import { formatDateTime } from '../../_utilities/formatDateTime' -import { toKebabCase } from '../../_utilities/toKebabCase' -import ArchiveButton from '../ArchiveButton' -import Authors from '../Authors' -import CategoryPill from '../CategoryPill' -import FeaturedImage from '../FeaturedImage' -import styles from './styles.module.css' +} from "@/payload-types"; +import { HeadphonesIcon, SpectaclesIcon } from "../../_icons/icons"; +import { estimateReadTime } from "../../_utilities/estimateReadTime"; +import { formatDateTime } from "../../_utilities/formatDateTime"; +import { toKebabCase } from "../../_utilities/toKebabCase"; +import ArchiveButton from "../ArchiveButton"; + +import CategoryPill from "../CategoryPill"; +import FeaturedImage from "../FeaturedImage"; +import styles from "./styles.module.css"; +import Authors from "@/app/_components/Authors"; +import { AuthorPill } from "@/app/_components/AuthorPill"; interface ContentSummaryProps { - contentType: string - content: Blogpost | Podcast | CaseStudy | TalksAndRoundtable // TODO: Extend to CaseStudy and TalksAndRoundTables once consistency is assured + contentType: string; + content: Blogpost | Podcast | CaseStudy | TalksAndRoundtable; // TODO: Extend to CaseStudy and TalksAndRoundTables once consistency is assured } const archiveMap = { - Blogposts: 'blogposts', - PodcastEpisodes: 'podcast-episodes', - CaseStudies: 'case-studies', - TalksAndRoundtables: 'talks-and-roundtables', -} + Blogposts: "blogposts", + PodcastEpisodes: "podcast-episodes", + CaseStudies: "case-studies", + TalksAndRoundtables: "talks-and-roundtables", +}; export default function ContentCard({ contentType, content }: ContentSummaryProps) { - const { slug, title, summary, featuredImage, categories, publishedAt, authors } = content + const { slug, title, summary, featuredImage, categories, publishedAt, authors } = content; // todo: convert to a collection item property - const readTime = estimateReadTime('herpaderpa') + const readTime = estimateReadTime("herpaderpa"); + return (
- {/*
{JSON.stringify(content,null, 2)}
*/} - -
+ +
+ + {featuredImage && (
- {/* @ts-ignore */} - {featuredImage && } -
- {/**/} + {/* @ts-ignore */} + +
+ )} + {/**/} +
{title}
+

{summary}

+ - {Array.isArray(categories) && categories.length > 0 - ? categories.map((category: Category, i) => ) - : null} + {Array.isArray(categories) && categories.length > 0 + ? categories.map((category: Category, i) => ) + : null} -
- {formatDateTime(publishedAt)} - {contentType === 'PodcastEpisodes' ? ( - - 1h +
+ {formatDateTime(publishedAt)} + {contentType === "PodcastEpisodes" ? ( + + 1h - ) : ( - - - {readTime} + ) : ( + + + {readTime} - )} -
- - {/**/} + )}
- + +
+
- ) + ); } diff --git a/src/app/_components/ContentCard/styles.module.css b/src/app/_components/ContentCard/styles.module.css index a5732b7..44db90d 100644 --- a/src/app/_components/ContentCard/styles.module.css +++ b/src/app/_components/ContentCard/styles.module.css @@ -10,6 +10,15 @@ gap: 20px; } +.contentMetaContainer h6 { + text-decoration: none; + transition: text-decoration 0.3s ease; +} + +.contentMetaContainer h6:hover { + text-decoration: underline; +} + .dateAndDuration { display: flex; gap: 20px; diff --git a/src/app/_components/Footer/index.tsx b/src/app/_components/Footer/index.tsx index 575eee1..06a8279 100644 --- a/src/app/_components/Footer/index.tsx +++ b/src/app/_components/Footer/index.tsx @@ -1,12 +1,15 @@ -import Link from 'next/link' -import styles from './styles.module.css' +import Link from "next/link"; +import styles from "./styles.module.css"; import { fetchGlobals } from "@/app/_utilities/contentFetchers"; export async function Footer() { - const footer = await fetchGlobals('footer') + const footer = await fetchGlobals("footer"); + // @ts-ignore + const navItems = footer?.navItems || []; // @ts-ignore - const navItems = footer?.navItems || [] + const socials = await fetchGlobals("socials").then(res => res.navItems); + return (
@@ -19,20 +22,26 @@ export async function Footer() { {link.label} - ) + ); })}
{/* TODO: Update fetchGlobals to include socials and then update here. */}

We're Social

- {['Md', 'Md', 'Md', 'Md', 'Md', 'Md', 'Md', 'Md'].map((item, i) => { - return {item} - })} + { + socials.map((item) => ( + + + {item.link.label} + + + )) + }
@@ -46,5 +55,5 @@ export async function Footer() {
- ) + ); } diff --git a/src/app/_components/Footer/styles.module.css b/src/app/_components/Footer/styles.module.css index 7668fa0..37f4e0a 100644 --- a/src/app/_components/Footer/styles.module.css +++ b/src/app/_components/Footer/styles.module.css @@ -3,6 +3,10 @@ justify-content: center; } +.container span { + margin-right: 20px; +} + .footer { display: grid; grid-template-columns: 1fr; diff --git a/src/app/_components/Header/DropDownIcon/index.tsx b/src/app/_components/Header/DropDownIcon/index.tsx index 023407a..2ecc4cd 100644 --- a/src/app/_components/Header/DropDownIcon/index.tsx +++ b/src/app/_components/Header/DropDownIcon/index.tsx @@ -1,14 +1,13 @@ -export default function DropDownIcon({ className }) { +export default function DropDownIcon() { return ( - - + + ) } diff --git a/src/app/_components/Header/Logo/index.tsx b/src/app/_components/Header/Logo/index.tsx index dc81560..f23751c 100644 --- a/src/app/_components/Header/Logo/index.tsx +++ b/src/app/_components/Header/Logo/index.tsx @@ -9,7 +9,7 @@ export default function Logo() { > ) diff --git a/src/app/_components/Header/index.tsx b/src/app/_components/Header/index.tsx index 7718cd6..4ab765b 100644 --- a/src/app/_components/Header/index.tsx +++ b/src/app/_components/Header/index.tsx @@ -1,19 +1,26 @@ -import Link from 'next/link' -import DropDownIcon from './DropDownIcon' -import styles from './styles.module.css' -import Logo from '@/app/_components/Header/Logo' +import Link from "next/link"; +import DropDownIcon from "./DropDownIcon"; +import styles from "./styles.module.css"; +import Logo from "@/app/_components/Header/Logo"; import { fetchGlobals } from "@/app/_utilities/contentFetchers"; -export async function Header() { +const defaultStyle = { + '--dynamic-background': "transparent", + '--dynamic-color': "var(--dark-rock-800)", + '--dynamic-width': '100%' +}; - const header = await fetchGlobals('header') +export async function Header({ style = defaultStyle }) { + + const header = await fetchGlobals("header"); // @ts-ignore - const navItems = header?.navItems || [] + const navItems = header?.navItems || []; return ( <> -
+ {/* @ts-ignore */} +
@@ -25,14 +32,16 @@ export async function Header() { {link.label} - ) + ); })} - + CONTACT US - +
+ +
- ) + ); } diff --git a/src/app/_components/Header/styles.module.css b/src/app/_components/Header/styles.module.css index 9a45be2..72a1fcd 100644 --- a/src/app/_components/Header/styles.module.css +++ b/src/app/_components/Header/styles.module.css @@ -1,5 +1,5 @@ .contactUsPill { - border: 1px solid var(--dark-rock-800); + border: 1px solid; border-radius: 33px; width: 161px; height: 66px; @@ -15,13 +15,19 @@ } .container { - font-family: Colfax; + font-family: var(--colfax); display: flex; align-items: center; gap: 27px; - padding: 20px; + padding: 60px 20px 20px 20px; font-size: 20px; justify-content: space-between; + background-color: var(--dynamic-background); + color: var(--dynamic-color); +} + +.container a { + color: var(--dynamic-color); } .navbar { @@ -36,6 +42,14 @@ } @media (min-width: 1080px) { + + .container { + width: var(--dynamic-width); + padding: 20px 40px 20px 80px; + margin: auto; + } + + .navbar { display: flex; } diff --git a/src/app/_components/PostSummary/index.tsx b/src/app/_components/PostSummary/index.tsx index 1758acc..81cdb47 100644 --- a/src/app/_components/PostSummary/index.tsx +++ b/src/app/_components/PostSummary/index.tsx @@ -2,8 +2,10 @@ import { Blogpost } from "@/payload-types"; import { estimateReadTime } from "../../_utilities/estimateReadTime"; import { formatDateTime } from "../../_utilities/formatDateTime"; import ArchiveButton from "../ArchiveButton"; -import AuthorPill from "../AuthorPill"; + import styles from "./styles.module.css"; +import Authors from '@/app/_components/Authors' +import { AuthorPill } from "@/app/_components/AuthorPill"; export default function PostSummary({ post }) { const { title, publishedAt, content, authors } = post; @@ -23,7 +25,7 @@ export default function PostSummary({ post }) { {/* Author info */}

WRITTEN BY

- + Social Links
diff --git a/src/app/_utilities/mergeOpenGraph.ts b/src/app/_utilities/mergeOpenGraph.ts index 810436c..6f27f2f 100644 --- a/src/app/_utilities/mergeOpenGraph.ts +++ b/src/app/_utilities/mergeOpenGraph.ts @@ -2,12 +2,12 @@ import type { Metadata } from 'next' const defaultOpenGraph: Metadata['openGraph'] = { type: 'website', - siteName: 'Payload Website Template', - title: 'Payload Website Template', - description: 'An open-source website built with Payload and Next.js.', + siteName: 'Subvisual Content Hub', + title: 'Subvisual Content Hub', + description: 'The place where you can find and peruse all content produced by Subvisual.', images: [ { - url: 'https://payloadcms.com/images/og-image.jpg', + url: 'https://raw.githubusercontent.com/subvisual/content-sub/refs/heads/main/public/media/subhub-opengraph-card.png', }, ], } diff --git a/src/collections/Blogposts/Blogposts.ts b/src/collections/Blogposts/index.ts similarity index 100% rename from src/collections/Blogposts/Blogposts.ts rename to src/collections/Blogposts/index.ts diff --git a/src/collections/Globals/Socials/config.ts b/src/collections/Globals/Socials/config.ts new file mode 100644 index 0000000..29bb0d8 --- /dev/null +++ b/src/collections/Globals/Socials/config.ts @@ -0,0 +1,25 @@ +import type { GlobalConfig } from "payload"; +import { link } from "@/fields/link"; +import { revalidateSocials } from "@/collections/Globals/Socials/hooks"; + +export const Socials: GlobalConfig = { + slug: "socials", + access: { + read: () => true, + }, + fields: [ + { + name: "navItems", + type: "array", + fields: [ + link({ + appearances: false, + }), + ], + maxRows: 10, + }, + ], + hooks: { + afterChange: [revalidateSocials], + }, +}; diff --git a/src/collections/Globals/Socials/hooks.ts b/src/collections/Globals/Socials/hooks.ts new file mode 100644 index 0000000..f0b5f8e --- /dev/null +++ b/src/collections/Globals/Socials/hooks.ts @@ -0,0 +1,11 @@ +import type { GlobalAfterChangeHook } from 'payload' + +import { revalidateTag } from 'next/cache' + +export const revalidateSocials: GlobalAfterChangeHook = ({ doc, req: { payload } }) => { + payload.logger.info(`Revalidating socials`) + + revalidateTag('global_socials') + + return doc +} diff --git a/src/collections/Media.ts b/src/collections/Media.ts deleted file mode 100644 index d78b8a6..0000000 --- a/src/collections/Media.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { CollectionConfig } from 'payload' - -import { - FixedToolbarFeature, - InlineToolbarFeature, - lexicalEditor, -} from '@payloadcms/richtext-lexical' -import path from 'path' -import { fileURLToPath } from 'url' - -import { anyone } from '../access/anyone' -import { authenticated } from '../access/authenticated' - -const filename = fileURLToPath(import.meta.url) -const dirname = path.dirname(filename) - -export const Media: CollectionConfig = { - slug: 'media', - access: { - create: authenticated, - delete: authenticated, - read: anyone, - update: authenticated, - }, - fields: [ - { - name: 'alt', - type: 'text', - required: true, - }, - { - name: 'caption', - type: 'richText', - editor: lexicalEditor({ - features: ({ rootFeatures }) => { - return [...rootFeatures, FixedToolbarFeature(), InlineToolbarFeature()] - }, - }), - }, - ], - upload: { - // Upload to the public/media directory in Next.js making them publicly accessible even outside of Payload - staticDir: path.resolve(dirname, '../../public/media'), - }, -} diff --git a/src/collections/Media/Media.ts b/src/collections/Media/index.ts similarity index 100% rename from src/collections/Media/Media.ts rename to src/collections/Media/index.ts diff --git a/src/collections/Podcasts/Podcasts.ts b/src/collections/Podcasts/index.ts similarity index 100% rename from src/collections/Podcasts/Podcasts.ts rename to src/collections/Podcasts/index.ts diff --git a/src/payload-types.ts b/src/payload-types.ts index 9b33092..71f49a3 100644 --- a/src/payload-types.ts +++ b/src/payload-types.ts @@ -35,6 +35,7 @@ export interface Config { globals: { header: Header; footer: Footer; + socials: Social; 'homepage-settings': HomepageSetting; }; locale: null; @@ -883,6 +884,30 @@ export interface Footer { updatedAt?: string | null; createdAt?: string | null; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "socials". + */ +export interface Social { + id: string; + navItems?: + | { + link: { + type?: ('reference' | 'custom') | null; + newTab?: boolean | null; + reference?: { + relationTo: 'pages'; + value: string | Page; + } | null; + url?: string | null; + label: string; + }; + id?: string | null; + }[] + | null; + updatedAt?: string | null; + createdAt?: string | null; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "homepage-settings". diff --git a/src/payload.config.ts b/src/payload.config.ts index 98f4879..6234b56 100644 --- a/src/payload.config.ts +++ b/src/payload.config.ts @@ -35,14 +35,15 @@ import { Page, Post } from 'src/payload-types' import { searchFields } from '@/search/fieldOverrides' import { beforeSyncWithSearch } from '@/search/beforeSync' -import { Blogposts } from "@/collections/Blogposts/Blogposts"; +import { Blogposts } from "@/collections/Blogposts"; import { Authors } from "@/collections/Authors"; import { CaseStudies } from "@/collections/CaseStudies"; -import { Podcasts } from "@/collections/Podcasts/Podcasts"; +import { Podcasts } from "@/collections/Podcasts"; import { TalksAndRoundtables } from "@/collections/TalksAndRoundtables"; import { HomePageSettings } from "@/Globals/HubHighlights/config"; import { cloudStoragePlugin } from "@payloadcms/plugin-cloud-storage"; import { testAdapt } from "@/collections/Media/storageAdapter"; +import { Socials } from "@/collections/Globals/Socials/config"; const filename = fileURLToPath(import.meta.url) const dirname = path.dirname(filename) @@ -141,7 +142,7 @@ export default buildConfig({ path: '/seed', }, ], - globals: [Header, Footer, HomePageSettings], + globals: [Header, Footer, Socials, HomePageSettings], plugins: [ redirectsPlugin({ collections: ['pages', 'posts'],