Add posts
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
tobias 2024-06-24 01:22:01 +02:00
parent 5eb7b7037b
commit d6fe6446a5
10 changed files with 234 additions and 112 deletions

View File

@ -33,6 +33,7 @@ const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename) const dirname = path.dirname(filename)
export default buildConfig({ export default buildConfig({
collections: [Users, Posts, Authors, Media, Pages],
admin: { admin: {
autoLogin: { autoLogin: {
email: 'dev@payloadcms.com', email: 'dev@payloadcms.com',
@ -41,7 +42,6 @@ export default buildConfig({
}, },
}, },
editor: lexicalEditor(), editor: lexicalEditor(),
collections: [Users, Posts, Authors, Media, Pages],
secret: process.env.PAYLOAD_SECRET || '', secret: process.env.PAYLOAD_SECRET || '',
typescript: { typescript: {
outputFile: path.resolve(dirname, 'types/payload-types.ts'), outputFile: path.resolve(dirname, 'types/payload-types.ts'),

View File

@ -1,8 +1,11 @@
import ThemeSwitcher from '@/components/ThemeSwitcher'
import { getCurrentYear } from '@/utils/date'
import Link from 'next/link' import Link from 'next/link'
import React from 'react' import React from 'react'
import Header from '@/components/Header' 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 {} interface Props {}
@ -17,16 +20,11 @@ const Page = (props: Props) => {
<br /> <br />
{`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.`} {`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.`}
</p> </p>
{/* <section className="mt-4"> <section className="mt-4">
<Posts posts={posts} /> <Posts />
</section> */} </section>
</main> </main>
<footer className="flex justify-between items-center py-4"> <Footer />
<p className="text-lg font-semibold">
&copy; {getCurrentYear()} Autonomic. Template distributed under AGPL 3.0.
</p>
<ThemeSwitcher />
</footer>
</> </>
) )
} }

View File

@ -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<Metadata> {
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 <PostPage post={post} />
}
export default Page

View File

@ -1,10 +1,19 @@
import { CollectionConfig } from 'payload/types' import { CollectionConfig } from 'payload/types'
import { isEditor } from '@payload/access/isEditor' import { isEditor } from '@payload/access/isEditor'
import { isUser } from '@payload/access/isUser' 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 = { const Posts: CollectionConfig = {
slug: 'posts', slug: 'posts',
versions: true, versions: {
drafts: {
autosave: false,
},
maxPerDoc: 10,
},
admin: { admin: {
defaultColumns: ['title', 'author', 'status'], defaultColumns: ['title', 'author', 'status'],
useAsTitle: 'title', useAsTitle: 'title',
@ -18,22 +27,8 @@ const Posts: CollectionConfig = {
}, },
hooks: { hooks: {
afterChange: [ afterChange: [
async () => { async ({ doc, collection }) => {
console.log(process.env.TOKEN) revalidateTag(generateDocumentCacheKey(collection.slug, doc.path))
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)
}
}, },
], ],
}, },
@ -62,62 +57,26 @@ const Posts: CollectionConfig = {
relationTo: 'media', relationTo: 'media',
required: true, required: true,
}, },
/* { {
name: 'content', type: 'tabs',
type: 'richText', tabs: [
editor: slateEditor({ {
admin: { label: 'Content',
elements: ['h2', 'h3', 'h4', 'link', 'ol', 'ul', 'upload', 'blockquote', 'indent'], fields: [blocksField()],
leaves: ['bold', 'italic', 'underline', 'strikethrough'],
upload: {
collections: {
media: {
fields: [
{
name: 'image',
type: 'upload',
relationTo: 'media',
required: true,
},
],
},
},
},
}, },
}), ],
}, */ },
//TODO: Add author as block?
{ {
name: 'author', name: 'author',
//TODO: Add active user as default
type: 'relationship', type: 'relationship',
relationTo: 'authors', relationTo: 'authors',
admin: { admin: {
position: 'sidebar', position: 'sidebar',
}, },
}, },
{ slugField(),
name: 'status', pathField(),
type: 'select',
required: true,
options: [
{
value: 'draft',
label: 'Draft',
},
{
value: 'published',
label: 'Published',
},
{
value: 'archived',
label: 'Archived',
},
],
defaultValue: 'draft',
admin: {
position: 'sidebar',
},
},
], ],
} }

View File

@ -4,6 +4,7 @@ export const COLLECTION_SLUG_FORMS = 'forms' as const
export const COLLECTION_SLUG_MEDIA = 'media' as const export const COLLECTION_SLUG_MEDIA = 'media' as const
*/ */
export const COLLECTION_SLUG_PAGE = 'pages' 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_PRODUCTS = 'products' as const
export const COLLECTION_SLUG_PRICES = 'prices' as const export const COLLECTION_SLUG_PRICES = 'prices' as const

View File

@ -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<Post> = await payload.find({
collection: 'posts',
})
export default function Posts(props: Props) {
return (
<div className="flex flex-col">
<h2 className="text-secondary mb-4">Posts</h2>
<div className="border-y-2 flex flex-col divide-y-2 border-secondary divide-secondary">
{posts.docs.length > 0 ? (
posts.docs.map(
(post, index) => typeof post === 'object' && <PostEntry key={index} post={post} />,
)
) : (
<p>No posts available</p>
)}
</div>
</div>
)
}

15
src/components/Footer.tsx Normal file
View File

@ -0,0 +1,15 @@
import ThemeSwitcher from '@/components/ThemeSwitcher'
import { getCurrentYear } from '@/utils/date'
type Props = {}
export default function Footer({}: Props) {
return (
<footer className="flex justify-between items-center py-4">
<p className="text-lg font-semibold">
&copy; {getCurrentYear()} Autonomic. Template distributed under AGPL 3.0.
</p>
<ThemeSwitcher />
</footer>
)
}

View File

@ -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 (
<a className="py-4 border-secondary decoration-transparent" href={`/posts/${props.post.slug}`}>
<article className="flex px-5 py-3 gap-8">
<Image
src={props.post.thumbnail.url || ''}
width={150}
height={150}
alt={props.post.thumbnail.alt || ''}
layout="fixed"
/>
<div className="flex flex-col gap-4 w-full">
<div className="flex justify-between w-full">
<h3 className="">{props.post.title}</h3>
{props.post.publishedDate && (
<p className="font-light">
{new Date(props.post.publishedDate).toLocaleDateString('de-DE')}
</p>
)}
</div>
{props.post.summary && <p className="max-w-prose">{props.post.summary}</p>}
</div>
{/* {props.post.author.name && (
<p>
{props.post.author.name}
</p>
)} */}
</article>
</a>
)
}

View File

@ -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 (
<article>
<Blocks blocks={props?.post.blocks} locale="en" />
</article>
)
}

View File

@ -50,10 +50,24 @@ export interface Post {
summary?: string | null; summary?: string | null;
publishedDate?: string | null; publishedDate?: string | null;
thumbnail: string | Media; 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; author?: (string | null) | Author;
status: 'draft' | 'published' | 'archived'; slug?: string | null;
path?: string | null;
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
_status?: ('draft' | 'published') | null;
} }
/** /**
* This interface was referenced by `Config`'s JSON-Schema * This interface was referenced by `Config`'s JSON-Schema
@ -74,41 +88,6 @@ export interface Media {
focalX?: number | null; focalX?: number | null;
focalY?: 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 * This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "RichTextBlock". * via the `definition` "RichTextBlock".
@ -133,6 +112,51 @@ export interface RichTextBlock {
blockName?: string | null; blockName?: string | null;
blockType: 'RichText'; 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 * This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-preferences". * via the `definition` "payload-preferences".