From d6fe6446a5b2a3d6393dda33445f211968086bf0 Mon Sep 17 00:00:00 2001
From: tobias
Date: Mon, 24 Jun 2024 01:22:01 +0200
Subject: [PATCH] Add posts
---
payload.config.ts | 2 +-
src/app/(app)/page.tsx | 20 +++--
src/app/(app)/posts/[[...path]]/page.tsx | 36 +++++++++
src/app/(payload)/collections/Posts.ts | 87 ++++++---------------
src/app/(payload)/collections/config.ts | 1 +
src/components/Blocks/Posts.tsx | 32 ++++++++
src/components/Footer.tsx | 15 ++++
src/components/PostEntry.tsx | 41 ++++++++++
src/components/PostPage.tsx | 16 ++++
types/payload-types.ts | 96 +++++++++++++++---------
10 files changed, 234 insertions(+), 112 deletions(-)
create mode 100644 src/app/(app)/posts/[[...path]]/page.tsx
create mode 100644 src/components/Blocks/Posts.tsx
create mode 100644 src/components/Footer.tsx
create mode 100644 src/components/PostEntry.tsx
create mode 100644 src/components/PostPage.tsx
diff --git a/payload.config.ts b/payload.config.ts
index 08ab6d1..9755ad6 100644
--- a/payload.config.ts
+++ b/payload.config.ts
@@ -33,6 +33,7 @@ const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
export default buildConfig({
+ collections: [Users, Posts, Authors, Media, Pages],
admin: {
autoLogin: {
email: 'dev@payloadcms.com',
@@ -41,7 +42,6 @@ export default buildConfig({
},
},
editor: lexicalEditor(),
- collections: [Users, Posts, Authors, Media, Pages],
secret: process.env.PAYLOAD_SECRET || '',
typescript: {
outputFile: path.resolve(dirname, 'types/payload-types.ts'),
diff --git a/src/app/(app)/page.tsx b/src/app/(app)/page.tsx
index 5d3b22e..59e524e 100644
--- a/src/app/(app)/page.tsx
+++ b/src/app/(app)/page.tsx
@@ -1,8 +1,11 @@
-import ThemeSwitcher from '@/components/ThemeSwitcher'
-import { getCurrentYear } from '@/utils/date'
import Link from 'next/link'
import React from 'react'
import Header from '@/components/Header'
+import Footer from '@/components/Footer'
+
+import { getPayloadHMR } from '@payloadcms/next/utilities'
+import configPromise from '@payload-config'
+import Posts from '@/components/Blocks/Posts'
interface Props {}
@@ -17,16 +20,11 @@ const Page = (props: Props) => {
{`When you're ready to deploy the website on your own server, Nextload comes with a production environment that requires the use of Traefik as a reverse proxy. This setup provides a secure and scalable production environment for your website.`}
- {/* */}
+
-
+
>
)
}
diff --git a/src/app/(app)/posts/[[...path]]/page.tsx b/src/app/(app)/posts/[[...path]]/page.tsx
new file mode 100644
index 0000000..b696143
--- /dev/null
+++ b/src/app/(app)/posts/[[...path]]/page.tsx
@@ -0,0 +1,36 @@
+import Blocks from '@/components/Blocks'
+import { COLLECTION_SLUG_POST } from '@payload/collections/config'
+import { getDocument } from '@/utils/getDocument'
+import { generateMeta } from '@/utils/generateMeta'
+import { Metadata } from 'next'
+import { notFound } from 'next/navigation'
+import PostPage from '@/components/PostPage'
+
+type PageArgs = {
+ params: {
+ path: string[]
+ }
+}
+
+export async function generateMetadata({ params }: PageArgs): Promise {
+ const page = await getDocument({
+ collection: COLLECTION_SLUG_POST,
+ path: params.path,
+ depth: 3,
+ })
+ if (!page) notFound()
+
+ return generateMeta(params?.path)
+}
+
+const Page = async ({ params }: PageArgs) => {
+ const post = await getDocument({
+ collection: COLLECTION_SLUG_POST,
+ path: params.path,
+ depth: 3,
+ })
+ if (!post) notFound()
+ return
+}
+
+export default Page
diff --git a/src/app/(payload)/collections/Posts.ts b/src/app/(payload)/collections/Posts.ts
index 313b47e..bb921a6 100644
--- a/src/app/(payload)/collections/Posts.ts
+++ b/src/app/(payload)/collections/Posts.ts
@@ -1,10 +1,19 @@
import { CollectionConfig } from 'payload/types'
import { isEditor } from '@payload/access/isEditor'
import { isUser } from '@payload/access/isUser'
+import { revalidateTag } from 'next/cache'
+import { generateDocumentCacheKey } from '@/utils/getDocument'
+import { slugField, pathField } from '@payload/fields/'
+import { blocksField } from '@payload/fields/blocks'
const Posts: CollectionConfig = {
slug: 'posts',
- versions: true,
+ versions: {
+ drafts: {
+ autosave: false,
+ },
+ maxPerDoc: 10,
+ },
admin: {
defaultColumns: ['title', 'author', 'status'],
useAsTitle: 'title',
@@ -18,22 +27,8 @@ const Posts: CollectionConfig = {
},
hooks: {
afterChange: [
- async () => {
- console.log(process.env.TOKEN)
-
- try {
- process.env.NODE_ENV !== 'development' &&
- console.log(
- await fetch(`${process.env.DRONE_URL}/api/repos/${process.env.REPOSITORY}/builds`, {
- method: 'POST',
- headers: {
- Authorization: `Bearer ${process.env.TOKEN}`,
- },
- }),
- )
- } catch (e) {
- console.log(e)
- }
+ async ({ doc, collection }) => {
+ revalidateTag(generateDocumentCacheKey(collection.slug, doc.path))
},
],
},
@@ -62,62 +57,26 @@ const Posts: CollectionConfig = {
relationTo: 'media',
required: true,
},
- /* {
- name: 'content',
- type: 'richText',
- editor: slateEditor({
- admin: {
- elements: ['h2', 'h3', 'h4', 'link', 'ol', 'ul', 'upload', 'blockquote', 'indent'],
- leaves: ['bold', 'italic', 'underline', 'strikethrough'],
- upload: {
- collections: {
- media: {
- fields: [
- {
- name: 'image',
- type: 'upload',
- relationTo: 'media',
- required: true,
- },
- ],
- },
- },
- },
+ {
+ type: 'tabs',
+ tabs: [
+ {
+ label: 'Content',
+ fields: [blocksField()],
},
- }),
- }, */
+ ],
+ },
+ //TODO: Add author as block?
{
name: 'author',
- //TODO: Add active user as default
type: 'relationship',
relationTo: 'authors',
admin: {
position: 'sidebar',
},
},
- {
- name: 'status',
- type: 'select',
- required: true,
- options: [
- {
- value: 'draft',
- label: 'Draft',
- },
- {
- value: 'published',
- label: 'Published',
- },
- {
- value: 'archived',
- label: 'Archived',
- },
- ],
- defaultValue: 'draft',
- admin: {
- position: 'sidebar',
- },
- },
+ slugField(),
+ pathField(),
],
}
diff --git a/src/app/(payload)/collections/config.ts b/src/app/(payload)/collections/config.ts
index 28e0b76..c16e632 100644
--- a/src/app/(payload)/collections/config.ts
+++ b/src/app/(payload)/collections/config.ts
@@ -4,6 +4,7 @@ export const COLLECTION_SLUG_FORMS = 'forms' as const
export const COLLECTION_SLUG_MEDIA = 'media' as const
*/
export const COLLECTION_SLUG_PAGE = 'pages' as const
+export const COLLECTION_SLUG_POST = 'posts' as const
/* export const COLLECTION_SLUG_PRODUCTS = 'products' as const
export const COLLECTION_SLUG_PRICES = 'prices' as const
diff --git a/src/components/Blocks/Posts.tsx b/src/components/Blocks/Posts.tsx
new file mode 100644
index 0000000..a6fd33c
--- /dev/null
+++ b/src/components/Blocks/Posts.tsx
@@ -0,0 +1,32 @@
+import { Post } from 'types/payload-types'
+import PostEntry from '@/components/PostEntry'
+import { getPayloadHMR } from '@payloadcms/next/utilities'
+import configPromise from '@payload-config'
+import { PaginatedDocs } from 'payload/database'
+
+type Props = {}
+
+const payload = await getPayloadHMR({
+ config: configPromise,
+})
+
+const posts: PaginatedDocs = await payload.find({
+ collection: 'posts',
+})
+
+export default function Posts(props: Props) {
+ return (
+
+
Posts
+
+ {posts.docs.length > 0 ? (
+ posts.docs.map(
+ (post, index) => typeof post === 'object' &&
,
+ )
+ ) : (
+
No posts available
+ )}
+
+
+ )
+}
diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx
new file mode 100644
index 0000000..16ed702
--- /dev/null
+++ b/src/components/Footer.tsx
@@ -0,0 +1,15 @@
+import ThemeSwitcher from '@/components/ThemeSwitcher'
+import { getCurrentYear } from '@/utils/date'
+
+type Props = {}
+
+export default function Footer({}: Props) {
+ return (
+
+ )
+}
diff --git a/src/components/PostEntry.tsx b/src/components/PostEntry.tsx
new file mode 100644
index 0000000..1d207d4
--- /dev/null
+++ b/src/components/PostEntry.tsx
@@ -0,0 +1,41 @@
+import { Post } from 'types/payload-types'
+import Image from 'next/image'
+
+interface Props {
+ post: Post
+}
+
+export default function PostEntry(props: Props) {
+ if (typeof props.post.thumbnail === 'string') return
+
+ return (
+
+
+
+
+
+
+
{props.post.title}
+ {props.post.publishedDate && (
+
+ {new Date(props.post.publishedDate).toLocaleDateString('de-DE')}
+
+ )}
+
+ {props.post.summary &&
{props.post.summary}
}
+
+ {/* {props.post.author.name && (
+
+ {props.post.author.name}
+
+ )} */}
+
+
+ )
+}
diff --git a/src/components/PostPage.tsx b/src/components/PostPage.tsx
new file mode 100644
index 0000000..1c7d0f2
--- /dev/null
+++ b/src/components/PostPage.tsx
@@ -0,0 +1,16 @@
+import React from 'react'
+import { Post } from 'types/payload-types'
+import Blocks from './Blocks'
+
+interface Props {
+ post: Post
+ locale: string
+}
+
+export default function PostPage(props: Props) {
+ return (
+
+
+
+ )
+}
diff --git a/types/payload-types.ts b/types/payload-types.ts
index 056796a..4c7fbb2 100644
--- a/types/payload-types.ts
+++ b/types/payload-types.ts
@@ -50,10 +50,24 @@ export interface Post {
summary?: string | null;
publishedDate?: string | null;
thumbnail: string | Media;
+ 'content-title'?: string | null;
+ blocks?:
+ | (
+ | RichTextBlock
+ | {
+ author?: (string | null) | Author;
+ id?: string | null;
+ blockName?: string | null;
+ blockType: 'Author';
+ }
+ )[]
+ | null;
author?: (string | null) | Author;
- status: 'draft' | 'published' | 'archived';
+ slug?: string | null;
+ path?: string | null;
updatedAt: string;
createdAt: string;
+ _status?: ('draft' | 'published') | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
@@ -74,41 +88,6 @@ export interface Media {
focalX?: number | null;
focalY?: number | null;
}
-/**
- * This interface was referenced by `Config`'s JSON-Schema
- * via the `definition` "authors".
- */
-export interface Author {
- id: string;
- avatar: string | Media;
- name: string;
- bio?: string | null;
- user?: string | User | null;
- updatedAt: string;
- createdAt: string;
-}
-/**
- * This interface was referenced by `Config`'s JSON-Schema
- * via the `definition` "pages".
- */
-export interface Page {
- id: string;
- title?: string | null;
- blocks?: RichTextBlock[] | null;
- slug?: string | null;
- path?: string | null;
- breadcrumbs?:
- | {
- doc?: (string | null) | Page;
- url?: string | null;
- label?: string | null;
- id?: string | null;
- }[]
- | null;
- updatedAt: string;
- createdAt: string;
- _status?: ('draft' | 'published') | null;
-}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "RichTextBlock".
@@ -133,6 +112,51 @@ export interface RichTextBlock {
blockName?: string | null;
blockType: 'RichText';
}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "authors".
+ */
+export interface Author {
+ id: string;
+ avatar: string | Media;
+ name: string;
+ bio?: string | null;
+ user?: string | User | null;
+ updatedAt: string;
+ createdAt: string;
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "pages".
+ */
+export interface Page {
+ id: string;
+ title?: string | null;
+ blocks?:
+ | (
+ | RichTextBlock
+ | {
+ author?: (string | null) | Author;
+ id?: string | null;
+ blockName?: string | null;
+ blockType: 'Author';
+ }
+ )[]
+ | null;
+ slug?: string | null;
+ path?: string | null;
+ breadcrumbs?:
+ | {
+ doc?: (string | null) | Page;
+ url?: string | null;
+ label?: string | null;
+ id?: string | null;
+ }[]
+ | null;
+ updatedAt: string;
+ createdAt: string;
+ _status?: ('draft' | 'published') | null;
+}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-preferences".