This commit is contained in:
parent
5eb7b7037b
commit
d6fe6446a5
@ -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'),
|
||||
|
@ -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) => {
|
||||
<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.`}
|
||||
</p>
|
||||
{/* <section className="mt-4">
|
||||
<Posts posts={posts} />
|
||||
</section> */}
|
||||
<section className="mt-4">
|
||||
<Posts />
|
||||
</section>
|
||||
</main>
|
||||
<footer className="flex justify-between items-center py-4">
|
||||
<p className="text-lg font-semibold">
|
||||
© {getCurrentYear()} Autonomic. Template distributed under AGPL 3.0.
|
||||
</p>
|
||||
<ThemeSwitcher />
|
||||
</footer>
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
36
src/app/(app)/posts/[[...path]]/page.tsx
Normal file
36
src/app/(app)/posts/[[...path]]/page.tsx
Normal 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
|
@ -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(),
|
||||
],
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
32
src/components/Blocks/Posts.tsx
Normal file
32
src/components/Blocks/Posts.tsx
Normal 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
15
src/components/Footer.tsx
Normal 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">
|
||||
© {getCurrentYear()} Autonomic. Template distributed under AGPL 3.0.
|
||||
</p>
|
||||
<ThemeSwitcher />
|
||||
</footer>
|
||||
)
|
||||
}
|
41
src/components/PostEntry.tsx
Normal file
41
src/components/PostEntry.tsx
Normal 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>
|
||||
)
|
||||
}
|
16
src/components/PostPage.tsx
Normal file
16
src/components/PostPage.tsx
Normal 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>
|
||||
)
|
||||
}
|
@ -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".
|
||||
|
Loading…
Reference in New Issue
Block a user