Skip to content

Commit 89b40a2

Browse files
authored
Feat: Post Categories (#435)
* add guides * eslint: allow default exports * adjust guest author socials structure * build interecept guide route * styles * fetch guide specific fields * add guide routes * adjust blog post styling * revalidate docs on related post chnage * rename /guide to /guides * remove intercepted guides route * blog archive style adjustments * blog single style adjustments * useVideo on mobile * bump payload * consolidate posts with category/slug routing * archive page style adjustments * disable dynamic params for archive/post routes * build out archive navigation * bump packages * adjust video cmponent styling * cleanup build errors * revalidation * adjust category slug references * adjust revalidation * query posts by category/slug instead of only slug * filter only guides in docs related resources * revert changes to page transition provider * revalidate on post deletes, category changes * skip relatedDocs revalidation if no relatedDocs * handle potential null values in archive and posts
1 parent f05ee7d commit 89b40a2

File tree

42 files changed

+1667
-539
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1667
-539
lines changed

eslint.config.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,12 @@ export const rootEslintConfig = [
4444
},
4545
rules: {
4646
'payload/no-jsx-import-statements': 'warn',
47+
restrictDefaultExports: 'allow',
4748
},
4849
},
4950
]
5051

51-
export default [
52+
const config = [
5253
...rootEslintConfig,
5354
{
5455
languageOptions: {
@@ -60,3 +61,5 @@ export default [
6061
},
6162
},
6263
]
64+
65+
export default config

package.json

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,29 +27,29 @@
2727
"migrate-posts": "payload run ./src/migrate-posts-to-lexical.ts"
2828
},
2929
"dependencies": {
30-
"@docsearch/react": "^3.6.0",
30+
"@docsearch/react": "^3.9.0",
3131
"@faceless-ui/collapsibles": "^2.0.0-beta.0",
3232
"@faceless-ui/css-grid": "1.3.0-rc.0",
3333
"@faceless-ui/modal": "3.0.0-beta.2",
3434
"@faceless-ui/mouse-info": "2.0.0-beta.0",
3535
"@faceless-ui/scroll-info": "2.0.0-beta.0",
3636
"@faceless-ui/slider": "2.0.0-beta.0",
3737
"@faceless-ui/window-info": "3.0.0-beta.0",
38-
"@payloadcms/db-mongodb": "3.20.0",
39-
"@payloadcms/email-nodemailer": "3.20.0",
40-
"@payloadcms/live-preview-react": "3.20.0",
41-
"@payloadcms/next": "3.20.0",
42-
"@payloadcms/plugin-form-builder": "3.20.0",
43-
"@payloadcms/plugin-nested-docs": "3.20.0",
44-
"@payloadcms/plugin-redirects": "3.20.0",
45-
"@payloadcms/plugin-seo": "3.20.0",
46-
"@payloadcms/richtext-lexical": "3.20.0",
47-
"@payloadcms/storage-vercel-blob": "3.20.0",
48-
"@payloadcms/ui": "3.20.0",
38+
"@payloadcms/db-mongodb": "3.24.0",
39+
"@payloadcms/email-nodemailer": "3.24.0",
40+
"@payloadcms/live-preview-react": "3.24.0",
41+
"@payloadcms/next": "3.24.0",
42+
"@payloadcms/plugin-form-builder": "3.24.0",
43+
"@payloadcms/plugin-nested-docs": "3.24.0",
44+
"@payloadcms/plugin-redirects": "3.24.0",
45+
"@payloadcms/plugin-seo": "3.24.0",
46+
"@payloadcms/richtext-lexical": "3.24.0",
47+
"@payloadcms/storage-vercel-blob": "3.24.0",
48+
"@payloadcms/ui": "3.24.0",
4949
"@radix-ui/react-accordion": "^1.2.1",
5050
"@radix-ui/react-portal": "^1.1.2",
5151
"@radix-ui/react-tabs": "^1.1.1",
52-
"@stripe/react-stripe-js": "^1.16.5",
52+
"@stripe/react-stripe-js": "^3.1.1",
5353
"@stripe/stripe-js": "^1.47.0",
5454
"algoliasearch": "^4.23.3",
5555
"cheerio": "^1.0.0-rc.12",
@@ -65,14 +65,14 @@
6565
"next-sitemap": "^4.0.6",
6666
"node-cron": "^3.0.3",
6767
"nodemailer-sendgrid": "^1.0.3",
68-
"payload": "3.20.0",
68+
"payload": "3.24.0",
6969
"prism-react-renderer": "^2.3.1",
7070
"qs-esm": "7.0.2",
7171
"react": "19.0.0",
7272
"react-cookie": "^4.1.1",
7373
"react-dom": "19.0.0",
7474
"react-facebook-pixel": "^1.0.4",
75-
"react-instantsearch": "^7.11.4",
75+
"react-instantsearch": "^7.15.3",
7676
"react-markdown": "^9.0.1",
7777
"react-select": "5.9.0",
7878
"react-transition-group": "^4.4.5",

pnpm-lock.yaml

Lines changed: 245 additions & 197 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/app/(frontend)/(pages)/blog/[slug]/page.tsx renamed to src/app/(frontend)/(pages)/[category]/[slug]/page.tsx

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,34 @@ import type { Metadata } from 'next'
22

33
import { PayloadRedirects } from '@components/PayloadRedirects/index.js'
44
import { RefreshRouteOnSave } from '@components/RefreshRouterOnSave/index.js'
5+
import BreadcrumbsBar from '@components/Hero/BreadcrumbsBar/index.js'
56
import { fetchBlogPost, fetchPosts } from '@data'
67
import { mergeOpenGraph } from '@root/seo/mergeOpenGraph.js'
78
import { unstable_cache } from 'next/cache'
89
import { draftMode } from 'next/headers.js'
910
import React from 'react'
1011

11-
import { BlogPost } from './BlogPost/index.js'
12+
import { Post } from '@components/Post/index.js'
1213

13-
const getPost = (slug, draft?) =>
14-
draft ? fetchBlogPost(slug) : unstable_cache(fetchBlogPost, ['blogPost', `post-${slug}`])(slug)
14+
const getPost = async (slug, category, draft?) =>
15+
draft
16+
? await fetchBlogPost(slug, category)
17+
: await unstable_cache(fetchBlogPost, ['blogPost', `post-${slug}`])(slug, category)
1518

16-
const Post = async ({
19+
const PostPage = async ({
1720
params,
1821
}: {
1922
params: Promise<{
23+
category: string
2024
slug: any
2125
}>
2226
}) => {
2327
const { isEnabled: draft } = await draftMode()
24-
const { slug } = await params
28+
const { category, slug } = await params
2529

26-
const blogPost = await getPost(slug, draft)
30+
const blogPost = await getPost(slug, category, draft)
2731

28-
const url = `/blog/${slug}`
32+
const url = `/${category}/${slug}`
2933

3034
if (!blogPost) {
3135
return <PayloadRedirects url={url} />
@@ -35,31 +39,42 @@ const Post = async ({
3539
<>
3640
<PayloadRedirects disableNotFound url={url} />
3741
<RefreshRouteOnSave />
38-
<BlogPost {...blogPost} />
42+
<BreadcrumbsBar breadcrumbs={[]} hero={{ type: 'default' }} />
43+
<Post {...blogPost} />
3944
</>
4045
)
4146
}
4247

43-
export default Post
48+
export default PostPage
4449

4550
export async function generateStaticParams() {
46-
const getPosts = unstable_cache(fetchPosts, ['blogPosts'])
51+
const getPosts = unstable_cache(fetchPosts, ['allPosts'])
4752
const posts = await getPosts()
4853

49-
return posts.map(({ slug }) => ({
50-
slug,
51-
}))
54+
return posts
55+
.map(({ slug, category }) => {
56+
if (!category || typeof category === 'string' || !category.slug) {
57+
return null
58+
}
59+
60+
return {
61+
category: category.slug,
62+
slug,
63+
}
64+
})
65+
.filter(Boolean)
5266
}
5367

5468
export async function generateMetadata({
5569
params,
5670
}: {
5771
params: Promise<{
58-
slug: any
72+
category: string
73+
slug: string
5974
}>
6075
}): Promise<Metadata> {
6176
const { isEnabled: draft } = await draftMode()
62-
const { slug } = await params
77+
const { category, slug } = await params
6378
const post = await getPost(slug, draft)
6479

6580
const ogImage =
@@ -80,7 +95,7 @@ export async function generateMetadata({
8095
]
8196
: undefined,
8297
title: post?.meta?.title ?? undefined,
83-
url: `/blog/${slug}`,
98+
url: `/${category}/${slug}`,
8499
}),
85100
title: post?.meta?.title,
86101
}

src/app/(frontend)/(pages)/blog/index.module.scss renamed to src/app/(frontend)/(pages)/[category]/index.module.scss

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,14 @@
3535
row-gap: 2.5rem;
3636
}
3737
}
38+
39+
.noPosts {
40+
width: 100%;
41+
position: relative;
42+
display: flex;
43+
justify-content: center;
44+
align-items: center;
45+
padding: 2rem;
46+
background: var(--theme-bg);
47+
border: 1px solid var(--theme-border-color);
48+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { fetchArchive, fetchArchives } from '@data'
2+
import { unstable_cache } from 'next/cache'
3+
import { draftMode } from 'next/headers'
4+
5+
import React from 'react'
6+
7+
import { notFound } from 'next/navigation'
8+
import { Archive } from '@components/Archive'
9+
10+
export default async ({
11+
params,
12+
}: {
13+
params: Promise<{
14+
category: string
15+
}>
16+
}) => {
17+
const { category } = await params
18+
const { isEnabled: draft } = await draftMode()
19+
const archive = draft
20+
? await fetchArchive(category, draft)
21+
: await unstable_cache(fetchArchive, [`${category}-archive`])(category, draft)
22+
23+
const posts = archive?.posts?.docs
24+
25+
if (!archive || !posts) {
26+
notFound()
27+
}
28+
29+
return <Archive category={category} />
30+
}
31+
32+
export const generateStaticParams = async () => {
33+
const archives = await fetchArchives()
34+
return archives.map((archive) => ({
35+
category: archive.slug,
36+
}))
37+
}
38+
39+
export const generateMetadata = async ({ params }: { params: Promise<{ category: string }> }) => {
40+
const { category } = await params
41+
const archive = await fetchArchive(category)
42+
43+
if (!archive) {
44+
return null
45+
}
46+
47+
const { name, description } = archive
48+
49+
return {
50+
title: `${name} | Payload`,
51+
description,
52+
}
53+
}

src/app/(frontend)/(pages)/blog/layout.tsx

Lines changed: 0 additions & 19 deletions
This file was deleted.

src/app/(frontend)/(pages)/blog/page.tsx

Lines changed: 0 additions & 24 deletions
This file was deleted.

src/app/(frontend)/(pages)/blog/renderBlogArchive.tsx

Lines changed: 0 additions & 66 deletions
This file was deleted.

0 commit comments

Comments
 (0)