chore(examples): updates preview example (#3099)

This commit is contained in:
Jacob Fletcher
2023-08-01 16:53:45 -04:00
committed by GitHub
parent b84496e5da
commit 037ccdd96e
101 changed files with 135 additions and 90 deletions

View File

@@ -1,14 +1,14 @@
# Payload Preview Example Front-End
# Payload Draft Preview Example Front-End
This is a [Next.js](https://nextjs.org) app using the [App Router](https://nextjs.org/docs/app). It was made explicitly for Payload's [Preview Example](https://github.com/payloadcms/payload/tree/master/examples/preview/cms).
This is a [Next.js](https://nextjs.org) app using the [App Router](https://nextjs.org/docs/app). It was made explicitly for Payload's [Draft Preview Example](https://github.com/payloadcms/payload/tree/master/examples/draft-preview).
> This example uses the App Router, the latest API of Next.js. If your app is using the legacy [Pages Router](https://nextjs.org/docs/pages), check out the official [Pages Router Example](https://github.com/payloadcms/payload/tree/master/examples/preview/next-pages).
> This example uses the App Router, the latest API of Next.js. If your app is using the legacy [Pages Router](https://nextjs.org/docs/pages), check out the official [Pages Router Example](https://github.com/payloadcms/payload/tree/master/examples/draft-preview/next-pages).
## Getting Started
### Payload
First you'll need a running [Payload](https://github.com/payloadcms/payload) app. If you have not done so already, open up the `cms` folder and follow the setup instructions. Take note of your `serverURL`, you'll need this in the next step.
First you'll need a running Payload app. There is one made explicitly for this example and [can be found here](https://github.com/payloadcms/payload/tree/master/examples/draft-preview/payload). If you have not done so already, clone it down and follow the setup instructions there. This will provide all the necessary APIs that your Next.js app requires for authentication.
### Next.js
@@ -18,7 +18,7 @@ First you'll need a running [Payload](https://github.com/payloadcms/payload) app
4. `yarn dev` or `npm run dev` to start the server
5. `open http://localhost:3001` to see the result
Once running you will find a couple seeded pages on your local environment with some basic instructions. You can also start editing the pages by modifying the documents within Payload. See the [Preview Example](https://github.com/payloadcms/payload/tree/master/examples/preview/cms) for full details.
Once running you will find a couple seeded pages on your local environment with some basic instructions. You can also start editing the pages by modifying the documents within Payload. See the [Draft Preview Example](https://github.com/payloadcms/payload/tree/master/examples/draft-preview/payload) for full details.
## Learn More

View File

@@ -2,9 +2,10 @@ import { draftMode } from 'next/headers'
import { notFound } from 'next/navigation'
import { Page } from '../../payload-types'
import { fetchPage } from '../_api/fetchPage'
import { fetchPages } from '../_api/fetchPages'
import { Gutter } from '../_components/Gutter'
import RichText from '../_components/RichText'
import { fetchPage, fetchPages } from '../cms'
import classes from './index.module.scss'

View File

@@ -1,12 +1,17 @@
import { cookies } from 'next/headers'
import type { RequestCookie } from 'next/dist/compiled/@edge-runtime/cookies'
import type { Page } from '../payload-types'
import type { Page } from '../../payload-types'
export const fetchPage = async (
slug: string,
draft?: boolean,
): Promise<Page | undefined | null> => {
const payloadToken = cookies().get('payload-token')
let payloadToken: RequestCookie | undefined
if (draft) {
const { cookies } = await import('next/headers')
payloadToken = cookies().get('payload-token')
}
const pageRes: {
docs: Page[]
@@ -27,13 +32,3 @@ export const fetchPage = async (
return pageRes?.docs?.[0] ?? null
}
export const fetchPages = async (): Promise<Page[]> => {
const pageRes: {
docs: Page[]
} = await fetch(`${process.env.NEXT_PUBLIC_CMS_URL}/api/pages?depth=0&limit=100`).then(res =>
res.json(),
) // eslint-disable-line function-paren-newline
return pageRes?.docs ?? []
}

View File

@@ -0,0 +1,11 @@
import type { Page } from '../../payload-types'
export const fetchPages = async (): Promise<Page[]> => {
const pageRes: {
docs: Page[]
} = await fetch(`${process.env.NEXT_PUBLIC_CMS_URL}/api/pages?depth=0&limit=100`).then(res =>
res.json(),
) // eslint-disable-line function-paren-newline
return pageRes?.docs ?? []
}

View File

@@ -1,17 +1,22 @@
.adminBar {
z-index: 10;
width: 100%;
background-color: rgb(var(--foreground-rgb));
background-color: rgba(var(--foreground-rgb), 0.075);
padding: calc(var(--base) * 0.5) 0;
display: none;
visibility: hidden;
opacity: 0;
transition: opacity 150ms linear;
}
.payloadAdminBar {
color: rgb(var(--background-rgb)) !important;
color: rgb(var(--foreground-rgb)) !important;
}
.show {
display: block;
visibility: visible;
opacity: 1;
}
.controls {

View File

@@ -1,5 +1,5 @@
{
"name": "payload-example-nextjs-preview-app",
"name": "payload-draft-preview-next-app",
"version": "0.1.0",
"private": true,
"scripts": {

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 437 B

After

Width:  |  Height:  |  Size: 437 B

View File

@@ -1,14 +1,14 @@
# Payload Preview Example Front-End
# Payload Draft Preview Example Front-End
This is a [Next.js](https://nextjs.org) app using the [Pages Router](https://nextjs.org/docs/pages). It was made explicitly for Payload's [Preview Example](https://github.com/payloadcms/payload/tree/master/examples/preview/cms).
This is a [Next.js](https://nextjs.org) app using the [Pages Router](https://nextjs.org/docs/pages). It was made explicitly for Payload's [Draft Preview Example](https://github.com/payloadcms/payload/tree/master/examples/draft-preview).
> This example uses the Pages Router, the legacy API of Next.js. If your app is using the latest [App Router](https://nextjs.org/docs/app), check out the official [App Router Example](https://github.com/payloadcms/payload/tree/master/examples/preview/next-app).
> This example uses the Pages Router, the legacy API of Next.js. If your app is using the latest [App Router](https://nextjs.org/docs/app), check out the official [App Router Example](https://github.com/payloadcms/payload/tree/master/examples/draft-preview/next-app).
## Getting Started
### Payload
First you'll need a running [Payload](https://github.com/payloadcms/payload) app. If you have not done so already, open up the `cms` folder and follow the setup instructions. Take note of your `serverURL`, you'll need this in the next step.
First you'll need a running Payload app. There is one made explicitly for this example and [can be found here](https://github.com/payloadcms/payload/tree/master/examples/draft-preview/payload). If you have not done so already, clone it down and follow the setup instructions there. This will provide all the necessary APIs that your Next.js app requires for authentication.
### Next.js
@@ -18,7 +18,7 @@ First you'll need a running [Payload](https://github.com/payloadcms/payload) app
4. `yarn dev` or `npm run dev` to start the server
5. `open http://localhost:3001` to see the result
Once running you will find a couple seeded pages on your local environment with some basic instructions. You can also start editing the pages by modifying the documents within Payload. See the [Preview Example](https://github.com/payloadcms/payload/tree/master/examples/preview/cms) for full details.
Once running you will find a couple seeded pages on your local environment with some basic instructions. You can also start editing the pages by modifying the documents within Payload. See the [Draft Preview Example](https://github.com/payloadcms/payload/tree/master/examples/draft-preview/payload) for full details.
## Learn More

View File

@@ -1,5 +1,5 @@
{
"name": "payload-example-nextjs-preview-pages",
"name": "payload-draft-preview-next-pages",
"version": "0.1.0",
"private": true,
"scripts": {

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 437 B

After

Width:  |  Height:  |  Size: 437 B

View File

@@ -1,17 +1,22 @@
.adminBar {
z-index: 10;
width: 100%;
background-color: rgb(var(--foreground-rgb));
background-color: rgba(var(--foreground-rgb), 0.075);
padding: calc(var(--base) * 0.5) 0;
display: none;
visibility: hidden;
opacity: 0;
transition: opacity 150ms linear;
}
.payloadAdminBar {
color: rgb(var(--background-rgb)) !important;
color: rgb(var(--foreground-rgb)) !important;
}
.show {
display: block;
visibility: visible;
opacity: 1;
}
.controls {

View File

@@ -1,9 +1,9 @@
MONGODB_URI=mongodb://127.0.0.1/payload-example-preview
MONGODB_URI=mongodb://127.0.0.1/payload-example-draft-preview
PAYLOAD_SECRET=ENTER-STRING-HERE
PAYLOAD_PUBLIC_SERVER_URL=http://localhost:3000
PAYLOAD_PUBLIC_SITE_URL=http://localhost:3001
PAYLOAD_PUBLIC_DRAFT_SECRET=EXAMPLE_DRAFT_SECRET
COOKIE_DOMAIN=localhost
REVALIDATION_KEY=EXAMPLE_REVALIDATION_KEY
PAYLOAD_SEED=true
PAYLOAD_PUBLIC_SEED=true
PAYLOAD_DROP_DATABASE=true

View File

@@ -1,8 +1,11 @@
# Payload Preview Example
# Payload Draft Preview Example
This example demonstrates how to implement preview in [Payload](https://github.com/payloadcms/payload) using [Versions](https://payloadcms.com/docs/versions/overview) and [Drafts](https://payloadcms.com/docs/versions/drafts). Preview allows you to see draft content on your front-end before it is published.
The [Payload Draft Preview Example](https://github.com/payloadcms/payload/tree/master/examples/draft-preview) demonstrates how to implement draft preview in [Payload](https://github.com/payloadcms/payload) using [Versions](https://payloadcms.com/docs/versions/overview) and [Drafts](https://payloadcms.com/docs/versions/drafts). Draft preview allows you to see content on your front-end before it is published. There are various fully working front-ends made explicitly for this example, including:
There is a fully working Next.js app made explicitly for this example which can be found [here](../next-app). Follow the instructions there to get started. If you are setting up preview for another front-end, please consider contributing to this repo with your own example!
- [Next.js App Router](../next-app)
- [Next.js Pages Router](../next-pages)
Follow the instructions in each respective README to get started. If you are setting up draft preview for another front-end, please consider contributing to this repo with your own example!
## Quick Start
@@ -17,7 +20,7 @@ That's it! Changes made in `./src` will be reflected in your app. See the [Devel
## How it works
Preview works by sending the user to your front-end with a `secret` along with their http-only cookies. Your front-end catches the request, verifies the authenticity, then enters into it's own preview mode. Once in preview mode, your front-end can begin securely requesting draft documents from Payload.
Draft preview works by sending the user to your front-end with a `secret` along with their http-only cookies. Your front-end catches the request, verifies the authenticity, then enters into it's own preview mode. Once in preview mode, your front-end can begin securely requesting draft documents from Payload. See [Preview Mode](#preview-mode) for more details.
### Collections
@@ -27,11 +30,11 @@ See the [Collections](https://payloadcms.com/docs/configuration/collections) doc
The `users` collection is auth-enabled which provides access to the admin panel. When previewing documents on your front-end, the user's JWT is used to authenticate the request. See [Pages](#pages) for more details.
For additional help with authentication, see the [Authentication](https://payloadcms.com/docs/authentication/overview#authentication-overview) docs or the official [Auth Example](https://github.com/payloadcms/payload/tree/master/examples/auth/cms#readme).
For additional help with authentication, see the [Authentication](https://payloadcms.com/docs/authentication/overview#authentication-overview) docs or the official [Auth Example](https://github.com/payloadcms/payload/tree/master/examples/auth).
- #### Pages
The `pages` collection is draft-enabled and has access control that restricts public users from viewing pages with a `draft` status. To fetch draft documents on your front-end, simply include the `draft=true` query param along with the `Authorization` header once you have entered [Preview Mode](#preview-mode).
The `pages` collection is draft-enabled and has access control that restricts public users from viewing pages with a `_status` of `draft`. To fetch draft documents on your front-end, simply include the `draft=true` query param along with the `Authorization` header once you have entered [Preview Mode](#preview-mode).
```ts
const preview = true; // set this based on your own front-end environment (see `Preview Mode` below)
@@ -52,7 +55,7 @@ See the [Collections](https://payloadcms.com/docs/configuration/collections) doc
### Preview Mode
To enter preview mode, the user first needs to have at least one draft document saved. When they click the "preview" button from the Payload admin panel, a custom [preview function](https://payloadcms.com/docs/configuration/collections#preview) routes them to your front-end with a `secret` and their http-only cookies. An API route on your front-end will verify the secret and token before entering into it's own preview mode. Once in preview mode, it can begin requesting drafts from Payload using the `Authorization` header, see [Pages](#pages) for more details.
To preview draft documents, the user first needs to have at least one draft document saved. When they click the "preview" button from the Payload admin panel, a custom [preview function](https://payloadcms.com/docs/configuration/collections#preview) routes them to your front-end with a `secret` along with their http-only cookies. An API route on your front-end will verify the secret and token before entering into it's own preview mode. Once in preview mode, it can begin requesting drafts from Payload using the `Authorization` header. See [Pages](#pages) for more details.
> "Preview mode" looks differently for every front-end framework. For instance, check out the differences between Next.js [Preview Mode](https://nextjs.org/docs/pages/building-your-application/configuring/preview-mode) in the Pages Router and [Draft Mode](https://nextjs.org/docs/pages/building-your-application/configuring/draft-mode) in the App Router. In Next.js, methods are provided that set cookies in your browser, but this may not be the case for all frameworks.
@@ -64,11 +67,11 @@ If your front-end is statically generated then you may also want to regenerate t
### Admin Bar
You might also want to render an admin bar on your front-end so that logged-in users can quickly navigate between the front-end and Payload as they're editing. For React apps, check out the official [Payload Admin Bar](https://github.com/payloadcms/payload-admin-bar). For other frameworks, simply hit the `/me` route with `credentials: include` and render your own admin bar if the user is logged in in order to display quick links to your CMS.
You might also want to render an admin bar on your front-end so that logged-in users can quickly navigate between the front-end and Payload as they're editing. For React apps, check out the official [Payload Admin Bar](https://github.com/payloadcms/payload-admin-bar). For other frameworks, simply hit the `/me` route with `credentials: 'include'` and render your own admin bar if the user is logged in.
### CORS
The [`cors`](https://payloadcms.com/docs/production/preventing-abuse#cross-origin-resource-sharing-cors), [`csrf`](https://payloadcms.com/docs/production/preventing-abuse#cross-site-request-forgery-csrf), and [`cookies`](https://payloadcms.com/docs/authentication/config#options) settings are configured to ensure that the admin panel and front-end can communicate with each other securely. If you are combining your front-end and admin panel into a single application that runs of a shared port and domain, you can remove these settings from your config.
The [`cors`](https://payloadcms.com/docs/production/preventing-abuse#cross-origin-resource-sharing-cors), [`csrf`](https://payloadcms.com/docs/production/preventing-abuse#cross-site-request-forgery-csrf), and [`cookies`](https://payloadcms.com/docs/authentication/config#options) settings are configured to ensure that the admin panel and front-end can communicate with each other securely. If you are combining your front-end and admin panel into a single application that runs of a shared port and domain, you can simplify your config by removing these settings.
For more details on this, see the [CORS](https://payloadcms.com/docs/production/preventing-abuse#cross-origin-resource-sharing-cors) docs.
@@ -76,10 +79,9 @@ For more details on this, see the [CORS](https://payloadcms.com/docs/production/
To spin up this example locally, follow the [Quick Start](#quick-start).
### Seed
On boot, a seed script is included to scaffold a basic database for you to use as an example. This is done by setting the `PAYLOAD_DROP_DATABASE` and `PAYLOAD_SEED` environment variables which are included in the `.env.example` by default. You can remove these from your `.env` to prevent this behavior. You can also freshly seed your project at any time by running `yarn seed`. This seed creates a user with email `demo@payloadcms.com` and password `demo` along with a home page and an example page with two versions, one published and the other draft.
On boot, a seed script is included to scaffold a basic database for you to use as an example. This is done by setting the `PAYLOAD_DROP_DATABASE` and `PAYLOAD_PUBLIC_SEED` environment variables which are included in the `.env.example` by default. You can remove these from your `.env` to prevent this behavior. You can also freshly seed your project at any time by running `yarn seed`. This seed creates a user with email `demo@payloadcms.com` and password `demo` along with a home page and an example page with two versions, one published and the other draft.
> NOTICE: seeding the database is destructive because it drops your current database to populate a fresh one from the seed template. Only run this command if you are starting a new project or can afford to lose your current data.

View File

@@ -6,7 +6,7 @@
"license": "MIT",
"scripts": {
"dev": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts nodemon",
"seed": "rm -rf media && cross-env PAYLOAD_SEED=true PAYLOAD_DROP_DATABASE=true PAYLOAD_CONFIG_PATH=src/payload.config.ts ts-node src/server.ts",
"seed": "rm -rf media && cross-env PAYLOAD_PUBLIC_SEED=true PAYLOAD_DROP_DATABASE=true PAYLOAD_CONFIG_PATH=src/payload.config.ts ts-node src/server.ts",
"build:payload": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload build",
"build:server": "tsc",
"build": "yarn copyfiles && yarn build:payload && yarn build:server",

View File

@@ -0,0 +1,37 @@
import type { AfterChangeHook } from 'payload/dist/collections/config/types'
// ensure that the home page is revalidated at '/' instead of '/home'
export const formatAppURL = ({ doc }): string => {
const pathToUse = doc.slug === 'home' ? '' : doc.slug
const { pathname } = new URL(`${process.env.PAYLOAD_PUBLIC_SITE_URL}/${pathToUse}`)
return pathname
}
// Revalidate the page in the background, so the user doesn't have to wait
// Notice that the hook itself is not async and we are not awaiting `revalidate`
// Only revalidate existing docs that are published
export const revalidatePage: AfterChangeHook = ({ doc, req, operation }) => {
if (operation === 'update' && doc._status === 'published') {
const url = formatAppURL({ doc })
const revalidate = async (): Promise<void> => {
try {
const res = await fetch(
`${process.env.PAYLOAD_PUBLIC_SITE_URL}/api/revalidate?secret=${process.env.REVALIDATION_KEY}&revalidatePath=${url}`,
)
if (res.ok) {
req.payload.logger.info(`Revalidated path ${url}`)
} else {
req.payload.logger.error(`Error revalidating path ${url}`)
}
} catch (err: unknown) {
req.payload.logger.error(`Error hitting revalidate route for ${url}`)
}
}
revalidate()
}
return doc
}

View File

@@ -0,0 +1,17 @@
import React from 'react'
const BeforeLogin: React.FC = () => {
if (process.env.PAYLOAD_PUBLIC_SEED === 'true') {
return (
<p>
{'Log in with the email '}
<strong>demo@payloadcms.com</strong>
{' and the password '}
<strong>demo</strong>.
</p>
)
}
return null
}
export default BeforeLogin

View File

@@ -3,10 +3,16 @@ import { buildConfig } from 'payload/config'
import { Pages } from './collections/Pages'
import { Users } from './collections/Users'
import BeforeLogin from './components/BeforeLogin'
import { MainMenu } from './globals/MainMenu'
export default buildConfig({
collections: [Pages, Users],
admin: {
components: {
beforeLogin: [BeforeLogin],
},
},
serverURL: process.env.PAYLOAD_PUBLIC_SERVER_URL,
cors: [
process.env.PAYLOAD_PUBLIC_SERVER_URL || '',

View File

@@ -21,7 +21,7 @@ export const home: Partial<Page> = {
{
type: 'link',
newTab: true,
url: 'https://github.com/payloadcms/payload/tree/master/examples/redirects/cms',
url: 'https://github.com/payloadcms/payload/tree/master/examples/redirects',
children: [{ text: '' }],
},
{ text: '' },
@@ -29,10 +29,10 @@ export const home: Partial<Page> = {
type: 'link',
linkType: 'custom',
newTab: true,
url: 'https://github.com/payloadcms/payload/tree/master/examples/preview/cms',
children: [{ text: 'Preview Example' }],
url: 'https://github.com/payloadcms/payload/tree/master/examples/draft-preview/payload',
children: [{ text: 'Draft Preview Example' }],
},
{ text: '. This example demonstrates how to implement preview into Payload using ' },
{ text: '. This example demonstrates how to implement draft preview into Payload using ' },
{
type: 'link',
newTab: true,
@@ -50,9 +50,9 @@ export const home: Partial<Page> = {
linkType: 'custom',
url: 'http://localhost:3000/admin',
newTab: true,
children: [{ text: 'Log in' }],
children: [{ text: 'Log in to the admin panel' }],
},
{ text: ' to the admin panel and refresh this page to see the ' },
{ text: ' and refresh this page to see the ' },
{
type: 'link',
linkType: 'custom',
@@ -61,7 +61,7 @@ export const home: Partial<Page> = {
children: [{ text: 'Payload Admin Bar' }],
},
{
text: ' appear at the top of the viewport. This will allow you to seamlessly navigate between the two apps. Then, navigate to the ',
text: ' appear at the top of this site. This will allow you to seamlessly navigate between the two apps. Then, navigate to the ',
},
{
type: 'link',
@@ -69,7 +69,7 @@ export const home: Partial<Page> = {
url: 'http://localhost:3001/example-page',
children: [{ text: 'example page' }],
},
{ text: ' to see how access to draft content is controlled. ' },
{ text: ' to see how we control access to draft content. ' },
],
},
],

View File

@@ -22,10 +22,10 @@ export const examplePage: Partial<Page> = {
linkType: 'custom',
url: 'http://localhost:3000/admin',
newTab: true,
children: [{ text: 'Log in' }],
children: [{ text: 'Log in to the admin panel' }],
},
{
text: ' to the admin panel and click "preview" to return to this page and view the latest draft content in Next.js preview mode. To make additional changes to the draft, click "save draft" before returning to the preview.',
text: ' and click "preview" to return to this page and view the latest draft content in Next.js preview mode. To make additional changes to the draft, click "save draft" before returning to the preview.',
},
],
},

View File

@@ -26,7 +26,7 @@ const start = async (): Promise<void> => {
},
})
if (process.env.PAYLOAD_SEED === 'true') {
if (process.env.PAYLOAD_PUBLIC_SEED === 'true') {
payload.logger.info('---- SEEDING DATABASE ----')
await seed(payload)
}

Some files were not shown because too many files have changed in this diff Show More