Erste brauchbare Version
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
DATABASE_URL=postgresql://...
|
||||
PAYLOAD_SECRET=ein-langer-zufälliger-secret-string
|
||||
|
||||
MAIL_HOST=smtp.example.org
|
||||
MAIL_PORT=465
|
||||
MAIL_DISPLAYNAME=My Company Name
|
||||
MAIL_USER=noreply@example.org
|
||||
MAIL_PASSWORD=password
|
||||
|
||||
NEXT_PUBLIC_PAYLOAD_URL=http://example.org
|
||||
@@ -39,3 +39,7 @@ yarn-error.log*
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
# Payload CMS
|
||||
/media/
|
||||
*.db
|
||||
@@ -1,6 +1,9 @@
|
||||
import { type ReactNode } from 'react';
|
||||
import { type Metadata } from 'next';
|
||||
import '@/app/globals.css';
|
||||
import { getPreview } from '@/lib/preview';
|
||||
import RefreshRouteOnSave from '@/components/RefreshRouteOnSave';
|
||||
import EndPreview from '@/components/EndPreviewButton';
|
||||
import '@/app/(app)/globals.css';
|
||||
|
||||
type Props = Readonly<{
|
||||
children: ReactNode;
|
||||
@@ -18,11 +21,15 @@ export const metadata: Metadata = {
|
||||
}
|
||||
};
|
||||
|
||||
export default function RootLayout({ children }: Props) {
|
||||
export default async function RootLayout({ children }: Props) {
|
||||
const { isPreview } = await getPreview();
|
||||
|
||||
return (
|
||||
<html lang="de" >
|
||||
<body>
|
||||
<RefreshRouteOnSave />
|
||||
{children}
|
||||
{isPreview && <EndPreview />}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
@@ -0,0 +1,29 @@
|
||||
import Link from 'next/link';
|
||||
import { getPreview } from '@/lib/preview';
|
||||
import config from '@payload-config';
|
||||
import { getPayload } from 'payload';
|
||||
|
||||
export default async function Page() {
|
||||
const { isPreview, where } = await getPreview();
|
||||
const payload = await getPayload({ config });
|
||||
|
||||
const posts = await payload.find({
|
||||
collection: 'posts',
|
||||
where,
|
||||
draft: isPreview
|
||||
});
|
||||
|
||||
return (
|
||||
<main className="flex flex-col">
|
||||
{posts.docs.map((post) => (
|
||||
<Link
|
||||
key={post.id}
|
||||
className="border border-1 border-primary p-4 m-4"
|
||||
href={`/post/${post.slug}`}
|
||||
>
|
||||
{post.title}
|
||||
</Link>
|
||||
))}
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col justify-center items-center">
|
||||
<p>Post nicht gefunden</p>
|
||||
<Link
|
||||
className="border border-1 border-primary"
|
||||
href="/"
|
||||
>
|
||||
Zurück
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import { notFound } from 'next/navigation';
|
||||
import Link from 'next/link';
|
||||
import { getPreview } from '@/lib/preview';
|
||||
import config from '@payload-config';
|
||||
import { getPayload } from 'payload';
|
||||
import { RichText } from '@payloadcms/richtext-lexical/react';
|
||||
|
||||
type Props = Readonly<{
|
||||
params: Promise<{
|
||||
slug: string;
|
||||
}>;
|
||||
}>;
|
||||
|
||||
export default async function Page({ params }: Props) {
|
||||
const { isPreview, where } = await getPreview();
|
||||
const { slug } = await params;
|
||||
const payload = await getPayload({ config });
|
||||
|
||||
const result = await payload.find({
|
||||
collection: 'posts',
|
||||
where: {
|
||||
...where,
|
||||
slug: {
|
||||
equals: slug
|
||||
}
|
||||
},
|
||||
draft: isPreview,
|
||||
limit: 1
|
||||
});
|
||||
|
||||
const post = result.docs[0];
|
||||
|
||||
if (!post)
|
||||
return notFound();
|
||||
|
||||
return (
|
||||
<div className="m-4">
|
||||
<Link
|
||||
className="border border-1 border-primary"
|
||||
href="/"
|
||||
>
|
||||
Zurück
|
||||
</Link>
|
||||
<h1 className="font-bold">{post.title}</h1>
|
||||
<RichText data={post.content} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import config from '@payload-config';
|
||||
import { NotFoundPage, generatePageMetadata } from '@payloadcms/next/views';
|
||||
import { importMap } from '../importMap';
|
||||
|
||||
type Props = Readonly<{
|
||||
params: Promise<{
|
||||
segments: string[];
|
||||
}>;
|
||||
searchParams: Promise<{
|
||||
[key: string]: string | string[];
|
||||
}>;
|
||||
}>;
|
||||
|
||||
export function generateMetadata({ params, searchParams }: Props) {
|
||||
return generatePageMetadata({
|
||||
config,
|
||||
params,
|
||||
searchParams
|
||||
});
|
||||
}
|
||||
|
||||
export default function NotFound({ params, searchParams }: Props) {
|
||||
return NotFoundPage({
|
||||
config,
|
||||
params,
|
||||
searchParams,
|
||||
importMap
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import config from '@payload-config';
|
||||
import { RootPage, generatePageMetadata } from '@payloadcms/next/views';
|
||||
import { importMap } from '../importMap';
|
||||
|
||||
type Props = Readonly<{
|
||||
params: Promise<{
|
||||
segments: string[];
|
||||
}>;
|
||||
searchParams: Promise<{
|
||||
[key: string]: string | string[];
|
||||
}>;
|
||||
}>;
|
||||
|
||||
export function generateMetadata({ params, searchParams}: Props) {
|
||||
return generatePageMetadata({
|
||||
config,
|
||||
params,
|
||||
searchParams
|
||||
});
|
||||
}
|
||||
|
||||
export default function Page({ params, searchParams }: Props) {
|
||||
return RootPage({
|
||||
config,
|
||||
params,
|
||||
searchParams,
|
||||
importMap
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { RscEntryLexicalCell as RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
|
||||
import { RscEntryLexicalField as RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
|
||||
import { LexicalDiffComponent as LexicalDiffComponent_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
|
||||
import { UploadFeatureClient as UploadFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { BoldFeatureClient as BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { ItalicFeatureClient as ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { UnderlineFeatureClient as UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { StrikethroughFeatureClient as StrikethroughFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { SubscriptFeatureClient as SubscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { SuperscriptFeatureClient as SuperscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { InlineCodeFeatureClient as InlineCodeFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { ParagraphFeatureClient as ParagraphFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { HeadingFeatureClient as HeadingFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { AlignFeatureClient as AlignFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { IndentFeatureClient as IndentFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { UnorderedListFeatureClient as UnorderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { OrderedListFeatureClient as OrderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { ChecklistFeatureClient as ChecklistFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { LinkFeatureClient as LinkFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { RelationshipFeatureClient as RelationshipFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { BlockquoteFeatureClient as BlockquoteFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { HorizontalRuleFeatureClient as HorizontalRuleFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { InlineToolbarFeatureClient as InlineToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { Icon as Icon_f934946492fabd3bfa95068204328949 } from '../../../components/PayloadInjects'
|
||||
import { Logo as Logo_f934946492fabd3bfa95068204328949 } from '../../../components/PayloadInjects'
|
||||
import { Action as Action_f934946492fabd3bfa95068204328949 } from '../../../components/PayloadInjects'
|
||||
import { CollectionCards as CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1 } from '@payloadcms/next/rsc'
|
||||
|
||||
/** @type import('payload').ImportMap */
|
||||
export const importMap = {
|
||||
"@payloadcms/richtext-lexical/rsc#RscEntryLexicalCell": RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e,
|
||||
"@payloadcms/richtext-lexical/rsc#RscEntryLexicalField": RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e,
|
||||
"@payloadcms/richtext-lexical/rsc#LexicalDiffComponent": LexicalDiffComponent_44fe37237e0ebf4470c9990d8cb7b07e,
|
||||
"@payloadcms/richtext-lexical/client#UploadFeatureClient": UploadFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#BoldFeatureClient": BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#ItalicFeatureClient": ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#UnderlineFeatureClient": UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#StrikethroughFeatureClient": StrikethroughFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#SubscriptFeatureClient": SubscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#SuperscriptFeatureClient": SuperscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#InlineCodeFeatureClient": InlineCodeFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#ParagraphFeatureClient": ParagraphFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#HeadingFeatureClient": HeadingFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#AlignFeatureClient": AlignFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#IndentFeatureClient": IndentFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#UnorderedListFeatureClient": UnorderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#OrderedListFeatureClient": OrderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#ChecklistFeatureClient": ChecklistFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#LinkFeatureClient": LinkFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#RelationshipFeatureClient": RelationshipFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#BlockquoteFeatureClient": BlockquoteFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#HorizontalRuleFeatureClient": HorizontalRuleFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#InlineToolbarFeatureClient": InlineToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"/components/PayloadInjects#Icon": Icon_f934946492fabd3bfa95068204328949,
|
||||
"/components/PayloadInjects#Logo": Logo_f934946492fabd3bfa95068204328949,
|
||||
"/components/PayloadInjects#Action": Action_f934946492fabd3bfa95068204328949,
|
||||
"@payloadcms/next/rsc#CollectionCards": CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import { RscEntryLexicalCell } from'@payloadcms/richtext-lexical/rsc';
|
||||
import { RscEntryLexicalField } from'@payloadcms/richtext-lexical/rsc';
|
||||
import { LexicalDiffComponent } from'@payloadcms/richtext-lexical/rsc';
|
||||
import { InlineToolbarFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||
import { HorizontalRuleFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||
import { UploadFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||
import { BlockquoteFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||
import { RelationshipFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||
import { LinkFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||
import { ChecklistFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||
import { OrderedListFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||
import { UnorderedListFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||
import { IndentFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||
import { AlignFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||
import { HeadingFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||
import { ParagraphFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||
import { InlineCodeFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||
import { SuperscriptFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||
import { SubscriptFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||
import { StrikethroughFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||
import { UnderlineFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||
import { BoldFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||
import { ItalicFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||
import { CollectionCards } from'@payloadcms/next/rsc';
|
||||
import { Logo, Icon, Action } from '@/components/PayloadInjects';
|
||||
|
||||
export const importMap = {
|
||||
'@payloadcms/richtext-lexical/rsc#RscEntryLexicalCell': RscEntryLexicalCell,
|
||||
'@payloadcms/richtext-lexical/rsc#RscEntryLexicalField': RscEntryLexicalField,
|
||||
'@payloadcms/richtext-lexical/rsc#LexicalDiffComponent': LexicalDiffComponent,
|
||||
'@payloadcms/richtext-lexical/client#InlineToolbarFeatureClient': InlineToolbarFeatureClient,
|
||||
'@payloadcms/richtext-lexical/client#HorizontalRuleFeatureClient': HorizontalRuleFeatureClient,
|
||||
'@payloadcms/richtext-lexical/client#UploadFeatureClient': UploadFeatureClient,
|
||||
'@payloadcms/richtext-lexical/client#BlockquoteFeatureClient': BlockquoteFeatureClient,
|
||||
'@payloadcms/richtext-lexical/client#RelationshipFeatureClient': RelationshipFeatureClient,
|
||||
'@payloadcms/richtext-lexical/client#LinkFeatureClient': LinkFeatureClient,
|
||||
'@payloadcms/richtext-lexical/client#ChecklistFeatureClient': ChecklistFeatureClient,
|
||||
'@payloadcms/richtext-lexical/client#OrderedListFeatureClient': OrderedListFeatureClient,
|
||||
'@payloadcms/richtext-lexical/client#UnorderedListFeatureClient': UnorderedListFeatureClient,
|
||||
'@payloadcms/richtext-lexical/client#IndentFeatureClient': IndentFeatureClient,
|
||||
'@payloadcms/richtext-lexical/client#AlignFeatureClient': AlignFeatureClient,
|
||||
'@payloadcms/richtext-lexical/client#HeadingFeatureClient': HeadingFeatureClient,
|
||||
'@payloadcms/richtext-lexical/client#ParagraphFeatureClient': ParagraphFeatureClient,
|
||||
'@payloadcms/richtext-lexical/client#InlineCodeFeatureClient': InlineCodeFeatureClient,
|
||||
'@payloadcms/richtext-lexical/client#SuperscriptFeatureClient': SuperscriptFeatureClient,
|
||||
'@payloadcms/richtext-lexical/client#SubscriptFeatureClient': SubscriptFeatureClient,
|
||||
'@payloadcms/richtext-lexical/client#StrikethroughFeatureClient': StrikethroughFeatureClient,
|
||||
'@payloadcms/richtext-lexical/client#UnderlineFeatureClient': UnderlineFeatureClient,
|
||||
'@payloadcms/richtext-lexical/client#BoldFeatureClient': BoldFeatureClient,
|
||||
'@payloadcms/richtext-lexical/client#ItalicFeatureClient': ItalicFeatureClient,
|
||||
'@payloadcms/next/rsc#CollectionCards': CollectionCards,
|
||||
'/components/PayloadInjects#Logo': Logo,
|
||||
'/components/PayloadInjects#Icon': Icon,
|
||||
'/components/PayloadInjects#Action': Action
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
import config from '@payload-config';
|
||||
import { REST_DELETE, REST_GET, REST_OPTIONS, REST_PATCH, REST_POST, REST_PUT } from '@payloadcms/next/routes';
|
||||
import '@payloadcms/next/css';
|
||||
|
||||
export const GET = REST_GET(config);
|
||||
export const POST = REST_POST(config);
|
||||
export const DELETE = REST_DELETE(config);
|
||||
export const PATCH = REST_PATCH(config);
|
||||
export const PUT = REST_PUT(config);
|
||||
export const OPTIONS = REST_OPTIONS(config);
|
||||
@@ -0,0 +1,35 @@
|
||||
import { draftMode } from 'next/headers';
|
||||
import { redirect } from 'next/navigation';
|
||||
import { getPayload } from 'payload';
|
||||
import config from '@payload-config';
|
||||
|
||||
export async function GET(request: Request) {
|
||||
const { searchParams } = new URL(request.url);
|
||||
|
||||
const slug = searchParams.get('slug');
|
||||
|
||||
if (!slug)
|
||||
return new Response('Missing slug', {
|
||||
status: 400
|
||||
});
|
||||
|
||||
const payload = await getPayload({
|
||||
config
|
||||
});
|
||||
|
||||
const { user } = await payload.auth({
|
||||
headers: request.headers
|
||||
});
|
||||
|
||||
if (!user)
|
||||
return new Response('Unauthorized', {
|
||||
status: 401
|
||||
});
|
||||
|
||||
const draft = await draftMode();
|
||||
|
||||
draft.enable();
|
||||
|
||||
// redirect(`/${slug}`);
|
||||
redirect('/');
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import config from '@payload-config';
|
||||
import { GRAPHQL_PLAYGROUND_GET } from '@payloadcms/next/routes';
|
||||
import '@payloadcms/next/css';
|
||||
|
||||
export const GET = GRAPHQL_PLAYGROUND_GET(config);
|
||||
@@ -0,0 +1,5 @@
|
||||
import config from '@payload-config';
|
||||
import { GRAPHQL_POST, REST_OPTIONS } from '@payloadcms/next/routes';
|
||||
|
||||
export const POST = GRAPHQL_POST(config);
|
||||
export const OPTIONS = REST_OPTIONS(config);
|
||||
@@ -0,0 +1,32 @@
|
||||
import { type ReactNode } from 'react';
|
||||
import config from '@payload-config';
|
||||
import { type ServerFunctionClientArgs } from 'payload';
|
||||
import { handleServerFunctions, RootLayout } from '@payloadcms/next/layouts';
|
||||
import { importMap } from './admin/importMap';
|
||||
import '@payloadcms/next/css';
|
||||
|
||||
type Props = Readonly<{
|
||||
children: ReactNode;
|
||||
}>;
|
||||
|
||||
async function serverFunction(args: ServerFunctionClientArgs) {
|
||||
'use server';
|
||||
|
||||
return handleServerFunctions({
|
||||
...args,
|
||||
config,
|
||||
importMap
|
||||
});
|
||||
}
|
||||
|
||||
export default function Layout({ children }: Props) {
|
||||
return (
|
||||
<RootLayout
|
||||
config={config}
|
||||
importMap={importMap}
|
||||
serverFunction={serverFunction}
|
||||
>
|
||||
{children}
|
||||
</RootLayout>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
export default function Page() {
|
||||
return (
|
||||
<p>Works!</p>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { type CollectionConfig } from 'payload';
|
||||
|
||||
export const Media: CollectionConfig = {
|
||||
slug: 'media',
|
||||
|
||||
upload: {
|
||||
staticDir: 'media'
|
||||
},
|
||||
|
||||
labels: {
|
||||
singular: 'Medium',
|
||||
plural: 'Medien'
|
||||
},
|
||||
|
||||
admin: {
|
||||
useAsTitle: 'title',
|
||||
group: 'Inhalte',
|
||||
description: 'Artikel verwalten',
|
||||
defaultColumns: [ 'id', 'title', 'alt' ],
|
||||
hideAPIURL: true
|
||||
},
|
||||
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
label: 'Titel',
|
||||
type: 'text',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'alt',
|
||||
label: 'Alt-Tag',
|
||||
type: 'text'
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -0,0 +1,83 @@
|
||||
import { type CollectionConfig } from 'payload';
|
||||
import { lexicalEditor, UploadFeature } from '@payloadcms/richtext-lexical';
|
||||
|
||||
export const Posts: CollectionConfig = {
|
||||
slug: 'posts',
|
||||
|
||||
labels: {
|
||||
singular: 'Artikel',
|
||||
plural: 'Artikel'
|
||||
},
|
||||
|
||||
admin: {
|
||||
useAsTitle: 'title',
|
||||
group: 'Inhalte',
|
||||
description: 'Artikel verwalten',
|
||||
defaultColumns: [ 'id', 'slug', 'title', 'author', '_status' ],
|
||||
hideAPIURL: true,
|
||||
preview: doc =>
|
||||
`${process.env.NEXT_PUBLIC_PAYLOAD_URL}/post/${doc.slug}/?preview`
|
||||
},
|
||||
|
||||
versions: {
|
||||
drafts: true
|
||||
},
|
||||
|
||||
access: {
|
||||
read: ({ data, req }) => (data?._status && data?._status !== 'draft') || [ 'admin', 'superadmin' ].includes(req.user?.role),
|
||||
create: ({ req }) => [ 'admin', 'superadmin' ].includes(req.user?.role),
|
||||
update: ({ req }) => [ 'admin', 'superadmin' ].includes(req.user?.role),
|
||||
delete: ({ req }) => [ 'admin', 'superadmin' ].includes(req.user?.role)
|
||||
},
|
||||
|
||||
orderable: true,
|
||||
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
label: 'Titel',
|
||||
type: 'text',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'slug',
|
||||
label: 'Slug',
|
||||
type: 'text',
|
||||
required: true,
|
||||
unique: true,
|
||||
admin: {
|
||||
placeholder: 'Zum Beispiel: mein-toller-post'
|
||||
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'content',
|
||||
label: 'Inhalt',
|
||||
type: 'richText',
|
||||
editor: lexicalEditor({
|
||||
features: ({ defaultFeatures }) => [
|
||||
...defaultFeatures,
|
||||
UploadFeature({
|
||||
collections: {
|
||||
media: {
|
||||
fields: []
|
||||
}
|
||||
}
|
||||
})
|
||||
]
|
||||
})
|
||||
},
|
||||
{
|
||||
name: 'author',
|
||||
label: 'Autor',
|
||||
type: 'relationship',
|
||||
relationTo: 'users',
|
||||
filterOptions: () => {
|
||||
return {
|
||||
// eslint-disable-next-line camelcase
|
||||
role: { not_equals: 'superadmin' }
|
||||
};
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -0,0 +1,89 @@
|
||||
import { type CollectionConfig, type AccessArgs } from 'payload';
|
||||
|
||||
function isSelfOrAdmin({ req }: AccessArgs) {
|
||||
if (req.user?.role === 'superadmin')
|
||||
return true;
|
||||
|
||||
if (req.user?.role === 'admin')
|
||||
return {
|
||||
id: {
|
||||
equals: req.user?.id
|
||||
}
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export const Users: CollectionConfig = {
|
||||
slug: 'users',
|
||||
|
||||
labels: {
|
||||
singular: 'Benutzer',
|
||||
plural: 'Benutzer'
|
||||
},
|
||||
|
||||
admin: {
|
||||
useAsTitle: 'fullName',
|
||||
group: 'System',
|
||||
description: 'Benutzer verwalten',
|
||||
defaultColumns: [ 'id', 'fullName', 'email', 'role' ],
|
||||
hideAPIURL: true
|
||||
},
|
||||
|
||||
access: {
|
||||
read: isSelfOrAdmin,
|
||||
create: ({ req }) => req.user?.role === 'superadmin',
|
||||
update: isSelfOrAdmin,
|
||||
delete: ({ req }) => req.user?.role === 'superadmin'
|
||||
},
|
||||
|
||||
auth: true,
|
||||
|
||||
fields: [
|
||||
{
|
||||
name: 'firstName',
|
||||
label: 'Vorname',
|
||||
type: 'text',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'lastName',
|
||||
label: 'Nachname',
|
||||
type: 'text',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'role',
|
||||
label: 'Rolle',
|
||||
type: 'select',
|
||||
required: true,
|
||||
access: {
|
||||
update: ({ req }) => req.user?.role === 'superadmin'
|
||||
},
|
||||
options: [
|
||||
{
|
||||
label: 'Super-Admin (GSH)',
|
||||
value: 'superadmin'
|
||||
},
|
||||
{
|
||||
label: 'Administrator',
|
||||
value: 'admin'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'fullName',
|
||||
label: 'Name',
|
||||
type: 'text',
|
||||
admin: {
|
||||
hidden: true,
|
||||
readOnly: true
|
||||
},
|
||||
hooks: {
|
||||
beforeChange: [
|
||||
({ data }) => `${data?.firstName} ${data?.lastName}`
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
export default function EndPreviewButton() {
|
||||
const router = useRouter();
|
||||
|
||||
const endPreview = () => {
|
||||
// Cookie entfernen
|
||||
document.cookie = 'payload-preview=; Max-Age=0; Path=/';
|
||||
|
||||
// ?preview entfernen
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.delete('preview');
|
||||
|
||||
// Neuladen
|
||||
router.replace(url.pathname + url.search);
|
||||
router.refresh();
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
className="fixed bottom-0 left-0 bg-red-500 rounded-md p-2 m-2 text-sm text-white cursor-pointer"
|
||||
onClick={endPreview}
|
||||
>
|
||||
Preview beenden
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
'use client';
|
||||
|
||||
import Image from 'next/image';
|
||||
import { Button } from '@payloadcms/ui';
|
||||
|
||||
export function Logo() {
|
||||
return (
|
||||
<Image
|
||||
src="/logo.png"
|
||||
width={7845}
|
||||
height={956}
|
||||
alt=""
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function Icon() {
|
||||
return (
|
||||
<Image
|
||||
src="/icon.png"
|
||||
width={836}
|
||||
height={956}
|
||||
alt=""
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function Action() {
|
||||
function goToSite() {
|
||||
window.location.href = window.location.origin;
|
||||
}
|
||||
|
||||
return (
|
||||
<Button onClick={goToSite}>
|
||||
Zur Website wechseln
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { RefreshRouteOnSave as PayloadLivePreview } from '@payloadcms/live-preview-react';
|
||||
|
||||
export default function RefreshRouteOnSave() {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<PayloadLivePreview
|
||||
refresh={() => router.refresh()}
|
||||
serverURL={process.env.NEXT_PUBLIC_PAYLOAD_URL ?? ''}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { cookies, headers } from 'next/headers';
|
||||
import { getPayload, type Where } from 'payload';
|
||||
import config from '@payload-config';
|
||||
|
||||
|
||||
export async function getPreview() {
|
||||
const cookieStore = await cookies();
|
||||
|
||||
let isPreview = cookieStore.get('payload-preview')?.value !== undefined;
|
||||
|
||||
if (isPreview) {
|
||||
const payload = await getPayload({ config });
|
||||
|
||||
const headersList = await headers();
|
||||
|
||||
const { user } = await payload.auth({
|
||||
headers: headersList
|
||||
});
|
||||
|
||||
if (!user)
|
||||
isPreview = false;
|
||||
}
|
||||
|
||||
const where: Where = {};
|
||||
|
||||
if (!isPreview)
|
||||
where._status = {
|
||||
equals: 'published'
|
||||
};
|
||||
|
||||
return {
|
||||
isPreview,
|
||||
where
|
||||
};
|
||||
};
|
||||
+2
-1
@@ -1,8 +1,9 @@
|
||||
import { type NextConfig } from 'next';
|
||||
import { withPayload } from '@payloadcms/next/withPayload';
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
output: 'standalone',
|
||||
poweredByHeader: false
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
export default withPayload(nextConfig);
|
||||
Generated
+6292
-209
File diff suppressed because it is too large
Load Diff
+8
-1
@@ -9,9 +9,16 @@
|
||||
"lint": "eslint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@payloadcms/db-postgres": "^3.88.0",
|
||||
"@payloadcms/db-sqlite": "^3.88.0",
|
||||
"@payloadcms/email-nodemailer": "^3.88.0",
|
||||
"@payloadcms/live-preview-react": "^3.88.0",
|
||||
"@payloadcms/next": "^3.88.0",
|
||||
"@payloadcms/richtext-lexical": "^3.88.0",
|
||||
"next": "16.2.10",
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4"
|
||||
"react-dom": "19.2.4",
|
||||
"sharp": "^0.35.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4",
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
import sharp from 'sharp';
|
||||
import { lexicalEditor } from '@payloadcms/richtext-lexical';
|
||||
import { postgresAdapter } from '@payloadcms/db-postgres';
|
||||
import { sqliteAdapter } from '@payloadcms/db-sqlite';
|
||||
import { nodemailerAdapter } from '@payloadcms/email-nodemailer';
|
||||
import { buildConfig } from 'payload';
|
||||
import { de } from '@payloadcms/translations/languages/de';
|
||||
import { Users } from '@/collections/Users';
|
||||
import { Media } from '@/collections/Media';
|
||||
import { Posts } from '@/collections/Posts';
|
||||
|
||||
export default buildConfig({
|
||||
admin: {
|
||||
user: 'users',
|
||||
avatar: 'default',
|
||||
meta: {
|
||||
icons: [
|
||||
{
|
||||
rel: 'icon',
|
||||
url: '/favicon.ico'
|
||||
}
|
||||
]
|
||||
},
|
||||
components: {
|
||||
graphics: {
|
||||
Logo: '/components/PayloadInjects#Logo',
|
||||
Icon: '/components/PayloadInjects#Icon'
|
||||
},
|
||||
actions: [ '/components/PayloadInjects#Action' ]
|
||||
},
|
||||
|
||||
livePreview: {
|
||||
url: ({ collectionConfig, data }) => {
|
||||
if (collectionConfig?.slug === 'posts')
|
||||
return `${process.env.NEXT_PUBLIC_PAYLOAD_URL}/post/${data.slug}/?preview`;
|
||||
|
||||
return process.env.NEXT_PUBLIC_PAYLOAD_URL;
|
||||
},
|
||||
collections: [ 'posts' ]
|
||||
}
|
||||
},
|
||||
|
||||
i18n: {
|
||||
supportedLanguages: { de }
|
||||
},
|
||||
|
||||
editor: lexicalEditor(),
|
||||
|
||||
collections: [
|
||||
Users,
|
||||
Media,
|
||||
Posts
|
||||
],
|
||||
|
||||
secret: process.env.PAYLOAD_SECRET || '',
|
||||
|
||||
// db: postgresAdapter({
|
||||
// pool: {
|
||||
// connectionString: process.env.DATABASE_URL
|
||||
// }
|
||||
// }),
|
||||
db: sqliteAdapter({
|
||||
client: {
|
||||
url: process.env.DATABASE_URL || '',
|
||||
authToken: process.env.DATABASE_AUTH_TOKEN
|
||||
}
|
||||
}),
|
||||
|
||||
email: nodemailerAdapter({
|
||||
defaultFromAddress: process.env.MAIL_USER ?? '',
|
||||
defaultFromName: process.env.MAIL_DISPLAYNAME ?? '',
|
||||
transportOptions: {
|
||||
host: process.env.MAIL_HOST,
|
||||
port: process.env.MAIL_PORT,
|
||||
auth: {
|
||||
user: process.env.MAIL_USER,
|
||||
pass: process.env.MAIL_PASSWORD
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
sharp
|
||||
});
|
||||
@@ -0,0 +1,18 @@
|
||||
import { NextResponse, type NextRequest } from 'next/server';
|
||||
|
||||
export async function proxy(request: NextRequest) {
|
||||
const isPreview = request.nextUrl.searchParams.get('preview') !== null;
|
||||
|
||||
if (!isPreview)
|
||||
return NextResponse.next();
|
||||
|
||||
// ?preview entfernen
|
||||
const url = request.nextUrl.clone();
|
||||
url.searchParams.delete('preview');
|
||||
const response = NextResponse.redirect(url);
|
||||
|
||||
// Cookie hinzufügen
|
||||
response.cookies.set('payload-preview', '1');
|
||||
|
||||
return response;
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 51 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 275 KiB |
+2
-1
@@ -19,7 +19,8 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
"@/*": ["./*"],
|
||||
"@payload-config": ["./payload.config.ts"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
|
||||
Reference in New Issue
Block a user