From 1a56925191f92b45db5eb28c3e669fed92c999a2 Mon Sep 17 00:00:00 2001 From: Alex Whiteside Date: Mon, 16 Sep 2024 18:27:06 -0400 Subject: [PATCH] Example --- playground/.editorconfig | 10 + playground/.env.example | 16 + playground/.eslintignore | 12 + playground/.eslintrc.cjs | 8 + playground/.gitignore | 10 + playground/.prettierignore | 14 + playground/.prettierrc.json | 6 + playground/Dockerfile | 24 + playground/README.md | 223 ++++ playground/components.json | 17 + playground/docker-compose.yml | 31 + playground/next-env.d.ts | 5 + playground/next.config.js | 25 + playground/package.json | 86 ++ playground/playground.db | Bin 0 -> 962560 bytes playground/postcss.config.js | 6 + playground/public/favicon.ico | Bin 0 -> 17223 bytes playground/public/favicon.svg | 15 + playground/public/website-template-OG.webp | Bin 0 -> 13670 bytes playground/redirects.js | 20 + playground/src/Footer/Component.tsx | 39 + playground/src/Footer/config.ts | 26 + .../src/Footer/hooks/revalidateFooter.ts | 11 + playground/src/Header/Component.client.tsx | 43 + playground/src/Header/Component.tsx | 11 + playground/src/Header/Nav/index.tsx | 19 + playground/src/Header/config.ts | 26 + .../src/Header/hooks/revalidateHeader.ts | 11 + playground/src/access/anyone.ts | 3 + playground/src/access/authenticated.ts | 9 + .../src/access/authenticatedOrPublished.ts | 13 + playground/src/app/(frontend)/[slug]/page.tsx | 89 ++ playground/src/app/(frontend)/globals.css | 103 ++ playground/src/app/(frontend)/layout.tsx | 53 + .../app/(frontend)/next/exit-preview/GET.ts | 6 + .../app/(frontend)/next/exit-preview/route.ts | 6 + .../src/app/(frontend)/next/preview/route.ts | 47 + playground/src/app/(frontend)/not-found.tsx | 18 + playground/src/app/(frontend)/page.tsx | 5 + .../(frontend)/posts/[slug]/page.client.tsx | 15 + .../src/app/(frontend)/posts/[slug]/page.tsx | 92 ++ playground/src/app/(frontend)/posts/page.tsx | 54 + .../posts/page/[pageNumber]/page.tsx | 72 ++ .../admin/[[...segments]]/not-found.tsx | 24 + .../(payload)/admin/[[...segments]]/page.tsx | 24 + .../src/app/(payload)/admin/importMap.js | 43 + .../src/app/(payload)/api/[...slug]/route.ts | 10 + .../(payload)/api/graphql-playground/route.ts | 6 + .../src/app/(payload)/api/graphql/route.ts | 6 + playground/src/app/(payload)/custom.scss | 0 playground/src/app/(payload)/layout.tsx | 21 + .../src/blocks/ArchiveBlock/Component.tsx | 65 ++ playground/src/blocks/ArchiveBlock/config.ts | 94 ++ playground/src/blocks/Banner/Component.tsx | 26 + playground/src/blocks/Banner/config.ts | 37 + .../src/blocks/CallToAction/Component.tsx | 29 + playground/src/blocks/CallToAction/config.ts | 42 + .../src/blocks/Code/Component.client.tsx | 31 + playground/src/blocks/Code/Component.tsx | 21 + playground/src/blocks/Code/config.ts | 33 + playground/src/blocks/Content/Component.tsx | 49 + playground/src/blocks/Content/config.ts | 74 ++ playground/src/blocks/Form/Checkbox/index.tsx | 44 + playground/src/blocks/Form/Component.tsx | 171 +++ playground/src/blocks/Form/Country/index.tsx | 63 ++ playground/src/blocks/Form/Country/options.ts | 982 ++++++++++++++++++ playground/src/blocks/Form/Email/index.tsx | 34 + playground/src/blocks/Form/Error/index.tsx | 5 + playground/src/blocks/Form/Message/index.tsx | 12 + playground/src/blocks/Form/Number/index.tsx | 32 + playground/src/blocks/Form/Select/index.tsx | 60 ++ playground/src/blocks/Form/State/index.tsx | 61 ++ playground/src/blocks/Form/State/options.ts | 52 + playground/src/blocks/Form/Text/index.tsx | 33 + playground/src/blocks/Form/Textarea/index.tsx | 45 + playground/src/blocks/Form/Width/index.tsx | 13 + .../src/blocks/Form/buildInitialFormState.tsx | 42 + playground/src/blocks/Form/config.ts | 51 + playground/src/blocks/Form/fields.tsx | 21 + .../src/blocks/MediaBlock/Component.tsx | 70 ++ playground/src/blocks/MediaBlock/config.ts | 29 + .../src/blocks/RelatedPosts/Component.tsx | 31 + playground/src/blocks/RenderBlocks.tsx | 52 + playground/src/collections/Categories.ts | 26 + playground/src/collections/Media.ts | 45 + .../collections/Pages/hooks/revalidatePage.ts | 30 + playground/src/collections/Pages/index.ts | 119 +++ .../Posts/hooks/populateAuthors.ts | 30 + .../collections/Posts/hooks/revalidatePost.ts | 30 + playground/src/collections/Posts/index.ts | 210 ++++ playground/src/collections/Users/index.ts | 28 + playground/src/components/AdminBar/index.tsx | 81 ++ .../BeforeDashboard/SeedButton/index.tsx | 51 + .../src/components/BeforeDashboard/index.scss | 24 + .../src/components/BeforeDashboard/index.tsx | 74 ++ .../src/components/BeforeLogin/index.tsx | 14 + playground/src/components/Card/index.tsx | 82 ++ .../components/CollectionArchive/index.tsx | 34 + playground/src/components/Link/index.tsx | 66 ++ .../components/LivePreviewListener/index.tsx | 11 + playground/src/components/Logo/Logo.tsx | 12 + .../src/components/Media/ImageMedia/index.tsx | 78 ++ .../src/components/Media/VideoMedia/index.tsx | 44 + playground/src/components/Media/index.tsx | 25 + playground/src/components/Media/types.ts | 20 + playground/src/components/PageRange/index.tsx | 54 + .../src/components/Pagination/index.tsx | 101 ++ .../src/components/PayloadRedirects/index.tsx | 49 + playground/src/components/RichText/index.tsx | 43 + .../src/components/RichText/nodeFormat.tsx | 125 +++ .../src/components/RichText/serialize.tsx | 212 ++++ playground/src/components/ui/button.tsx | 49 + playground/src/components/ui/card.tsx | 56 + playground/src/components/ui/checkbox.tsx | 27 + playground/src/components/ui/input.tsx | 23 + playground/src/components/ui/label.tsx | 20 + playground/src/components/ui/pagination.tsx | 100 ++ playground/src/components/ui/select.tsx | 152 +++ playground/src/components/ui/textarea.tsx | 22 + playground/src/cssVariables.js | 19 + playground/src/endpoints/seed/contact-form.ts | 111 ++ playground/src/endpoints/seed/contact-page.ts | 48 + playground/src/endpoints/seed/home-static.ts | 88 ++ playground/src/endpoints/seed/home.ts | 668 ++++++++++++ playground/src/endpoints/seed/image-1.ts | 67 ++ playground/src/endpoints/seed/image-2.ts | 67 ++ .../src/endpoints/seed/image-hero1.webp | Bin 0 -> 102614 bytes .../src/endpoints/seed/image-post1.webp | Bin 0 -> 117606 bytes .../src/endpoints/seed/image-post2.webp | Bin 0 -> 168786 bytes .../src/endpoints/seed/image-post3.webp | Bin 0 -> 112146 bytes playground/src/endpoints/seed/index.ts | 362 +++++++ playground/src/endpoints/seed/post-1.ts | 305 ++++++ playground/src/endpoints/seed/post-2.ts | 227 ++++ playground/src/endpoints/seed/post-3.ts | 263 +++++ playground/src/endpoints/seedHandler.ts | 27 + playground/src/fields/link.ts | 140 +++ playground/src/fields/linkGroup.ts | 25 + playground/src/fields/slug/SlugComponent.tsx | 80 ++ playground/src/fields/slug/formatSlug.ts | 25 + playground/src/fields/slug/index.scss | 12 + playground/src/fields/slug/index.ts | 54 + playground/src/heros/HighImpact/index.tsx | 46 + playground/src/heros/LowImpact/index.tsx | 25 + playground/src/heros/MediumImpact/index.tsx | 46 + playground/src/heros/PostHero/index.tsx | 88 ++ playground/src/heros/RenderHero.tsx | 25 + playground/src/heros/config.ts | 72 ++ playground/src/hooks/formatSlug.ts | 27 + playground/src/hooks/populatePublishedAt.ts | 15 + playground/src/hooks/revalidateRedirects.ts | 11 + playground/src/payload-types.ts | 710 +++++++++++++ playground/src/payload.config.ts | 198 ++++ .../src/providers/HeaderTheme/index.tsx | 37 + .../src/providers/Theme/InitTheme/index.tsx | 50 + .../providers/Theme/ThemeSelector/index.tsx | 48 + .../providers/Theme/ThemeSelector/types.ts | 5 + playground/src/providers/Theme/index.tsx | 57 + playground/src/providers/Theme/shared.ts | 17 + playground/src/providers/Theme/types.ts | 10 + playground/src/providers/index.tsx | 14 + playground/src/utilities/canUseDOM.ts | 1 + playground/src/utilities/cn.ts | 6 + playground/src/utilities/deepMerge.ts | 34 + playground/src/utilities/formatDateTime.ts | 20 + playground/src/utilities/generateMeta.ts | 36 + .../src/utilities/generatePreviewPath.ts | 1 + playground/src/utilities/getDocument.ts | 31 + playground/src/utilities/getGlobals.ts | 26 + playground/src/utilities/getMeUser.ts | 42 + playground/src/utilities/getRedirects.ts | 26 + playground/src/utilities/mergeOpenGraph.ts | 23 + playground/src/utilities/toKebabCase.ts | 5 + playground/src/utilities/useClickableCard.ts | 107 ++ playground/tailwind.config.mjs | 122 +++ playground/tsconfig.json | 51 + 175 files changed, 10624 insertions(+) create mode 100644 playground/.editorconfig create mode 100644 playground/.env.example create mode 100644 playground/.eslintignore create mode 100644 playground/.eslintrc.cjs create mode 100644 playground/.gitignore create mode 100644 playground/.prettierignore create mode 100644 playground/.prettierrc.json create mode 100644 playground/Dockerfile create mode 100644 playground/README.md create mode 100644 playground/components.json create mode 100644 playground/docker-compose.yml create mode 100644 playground/next-env.d.ts create mode 100644 playground/next.config.js create mode 100644 playground/package.json create mode 100644 playground/playground.db create mode 100644 playground/postcss.config.js create mode 100644 playground/public/favicon.ico create mode 100644 playground/public/favicon.svg create mode 100644 playground/public/website-template-OG.webp create mode 100644 playground/redirects.js create mode 100644 playground/src/Footer/Component.tsx create mode 100644 playground/src/Footer/config.ts create mode 100644 playground/src/Footer/hooks/revalidateFooter.ts create mode 100644 playground/src/Header/Component.client.tsx create mode 100644 playground/src/Header/Component.tsx create mode 100644 playground/src/Header/Nav/index.tsx create mode 100644 playground/src/Header/config.ts create mode 100644 playground/src/Header/hooks/revalidateHeader.ts create mode 100644 playground/src/access/anyone.ts create mode 100644 playground/src/access/authenticated.ts create mode 100644 playground/src/access/authenticatedOrPublished.ts create mode 100644 playground/src/app/(frontend)/[slug]/page.tsx create mode 100644 playground/src/app/(frontend)/globals.css create mode 100644 playground/src/app/(frontend)/layout.tsx create mode 100644 playground/src/app/(frontend)/next/exit-preview/GET.ts create mode 100644 playground/src/app/(frontend)/next/exit-preview/route.ts create mode 100644 playground/src/app/(frontend)/next/preview/route.ts create mode 100644 playground/src/app/(frontend)/not-found.tsx create mode 100644 playground/src/app/(frontend)/page.tsx create mode 100644 playground/src/app/(frontend)/posts/[slug]/page.client.tsx create mode 100644 playground/src/app/(frontend)/posts/[slug]/page.tsx create mode 100644 playground/src/app/(frontend)/posts/page.tsx create mode 100644 playground/src/app/(frontend)/posts/page/[pageNumber]/page.tsx create mode 100644 playground/src/app/(payload)/admin/[[...segments]]/not-found.tsx create mode 100644 playground/src/app/(payload)/admin/[[...segments]]/page.tsx create mode 100644 playground/src/app/(payload)/admin/importMap.js create mode 100644 playground/src/app/(payload)/api/[...slug]/route.ts create mode 100644 playground/src/app/(payload)/api/graphql-playground/route.ts create mode 100644 playground/src/app/(payload)/api/graphql/route.ts create mode 100644 playground/src/app/(payload)/custom.scss create mode 100644 playground/src/app/(payload)/layout.tsx create mode 100644 playground/src/blocks/ArchiveBlock/Component.tsx create mode 100644 playground/src/blocks/ArchiveBlock/config.ts create mode 100644 playground/src/blocks/Banner/Component.tsx create mode 100644 playground/src/blocks/Banner/config.ts create mode 100644 playground/src/blocks/CallToAction/Component.tsx create mode 100644 playground/src/blocks/CallToAction/config.ts create mode 100644 playground/src/blocks/Code/Component.client.tsx create mode 100644 playground/src/blocks/Code/Component.tsx create mode 100644 playground/src/blocks/Code/config.ts create mode 100644 playground/src/blocks/Content/Component.tsx create mode 100644 playground/src/blocks/Content/config.ts create mode 100644 playground/src/blocks/Form/Checkbox/index.tsx create mode 100644 playground/src/blocks/Form/Component.tsx create mode 100644 playground/src/blocks/Form/Country/index.tsx create mode 100644 playground/src/blocks/Form/Country/options.ts create mode 100644 playground/src/blocks/Form/Email/index.tsx create mode 100644 playground/src/blocks/Form/Error/index.tsx create mode 100644 playground/src/blocks/Form/Message/index.tsx create mode 100644 playground/src/blocks/Form/Number/index.tsx create mode 100644 playground/src/blocks/Form/Select/index.tsx create mode 100644 playground/src/blocks/Form/State/index.tsx create mode 100644 playground/src/blocks/Form/State/options.ts create mode 100644 playground/src/blocks/Form/Text/index.tsx create mode 100644 playground/src/blocks/Form/Textarea/index.tsx create mode 100644 playground/src/blocks/Form/Width/index.tsx create mode 100644 playground/src/blocks/Form/buildInitialFormState.tsx create mode 100644 playground/src/blocks/Form/config.ts create mode 100644 playground/src/blocks/Form/fields.tsx create mode 100644 playground/src/blocks/MediaBlock/Component.tsx create mode 100644 playground/src/blocks/MediaBlock/config.ts create mode 100644 playground/src/blocks/RelatedPosts/Component.tsx create mode 100644 playground/src/blocks/RenderBlocks.tsx create mode 100644 playground/src/collections/Categories.ts create mode 100644 playground/src/collections/Media.ts create mode 100644 playground/src/collections/Pages/hooks/revalidatePage.ts create mode 100644 playground/src/collections/Pages/index.ts create mode 100644 playground/src/collections/Posts/hooks/populateAuthors.ts create mode 100644 playground/src/collections/Posts/hooks/revalidatePost.ts create mode 100644 playground/src/collections/Posts/index.ts create mode 100644 playground/src/collections/Users/index.ts create mode 100644 playground/src/components/AdminBar/index.tsx create mode 100644 playground/src/components/BeforeDashboard/SeedButton/index.tsx create mode 100644 playground/src/components/BeforeDashboard/index.scss create mode 100644 playground/src/components/BeforeDashboard/index.tsx create mode 100644 playground/src/components/BeforeLogin/index.tsx create mode 100644 playground/src/components/Card/index.tsx create mode 100644 playground/src/components/CollectionArchive/index.tsx create mode 100644 playground/src/components/Link/index.tsx create mode 100644 playground/src/components/LivePreviewListener/index.tsx create mode 100644 playground/src/components/Logo/Logo.tsx create mode 100644 playground/src/components/Media/ImageMedia/index.tsx create mode 100644 playground/src/components/Media/VideoMedia/index.tsx create mode 100644 playground/src/components/Media/index.tsx create mode 100644 playground/src/components/Media/types.ts create mode 100644 playground/src/components/PageRange/index.tsx create mode 100644 playground/src/components/Pagination/index.tsx create mode 100644 playground/src/components/PayloadRedirects/index.tsx create mode 100644 playground/src/components/RichText/index.tsx create mode 100644 playground/src/components/RichText/nodeFormat.tsx create mode 100644 playground/src/components/RichText/serialize.tsx create mode 100644 playground/src/components/ui/button.tsx create mode 100644 playground/src/components/ui/card.tsx create mode 100644 playground/src/components/ui/checkbox.tsx create mode 100644 playground/src/components/ui/input.tsx create mode 100644 playground/src/components/ui/label.tsx create mode 100644 playground/src/components/ui/pagination.tsx create mode 100644 playground/src/components/ui/select.tsx create mode 100644 playground/src/components/ui/textarea.tsx create mode 100644 playground/src/cssVariables.js create mode 100644 playground/src/endpoints/seed/contact-form.ts create mode 100644 playground/src/endpoints/seed/contact-page.ts create mode 100644 playground/src/endpoints/seed/home-static.ts create mode 100644 playground/src/endpoints/seed/home.ts create mode 100644 playground/src/endpoints/seed/image-1.ts create mode 100644 playground/src/endpoints/seed/image-2.ts create mode 100644 playground/src/endpoints/seed/image-hero1.webp create mode 100644 playground/src/endpoints/seed/image-post1.webp create mode 100644 playground/src/endpoints/seed/image-post2.webp create mode 100644 playground/src/endpoints/seed/image-post3.webp create mode 100644 playground/src/endpoints/seed/index.ts create mode 100644 playground/src/endpoints/seed/post-1.ts create mode 100644 playground/src/endpoints/seed/post-2.ts create mode 100644 playground/src/endpoints/seed/post-3.ts create mode 100644 playground/src/endpoints/seedHandler.ts create mode 100644 playground/src/fields/link.ts create mode 100644 playground/src/fields/linkGroup.ts create mode 100644 playground/src/fields/slug/SlugComponent.tsx create mode 100644 playground/src/fields/slug/formatSlug.ts create mode 100644 playground/src/fields/slug/index.scss create mode 100644 playground/src/fields/slug/index.ts create mode 100644 playground/src/heros/HighImpact/index.tsx create mode 100644 playground/src/heros/LowImpact/index.tsx create mode 100644 playground/src/heros/MediumImpact/index.tsx create mode 100644 playground/src/heros/PostHero/index.tsx create mode 100644 playground/src/heros/RenderHero.tsx create mode 100644 playground/src/heros/config.ts create mode 100644 playground/src/hooks/formatSlug.ts create mode 100644 playground/src/hooks/populatePublishedAt.ts create mode 100644 playground/src/hooks/revalidateRedirects.ts create mode 100644 playground/src/payload-types.ts create mode 100644 playground/src/payload.config.ts create mode 100644 playground/src/providers/HeaderTheme/index.tsx create mode 100644 playground/src/providers/Theme/InitTheme/index.tsx create mode 100644 playground/src/providers/Theme/ThemeSelector/index.tsx create mode 100644 playground/src/providers/Theme/ThemeSelector/types.ts create mode 100644 playground/src/providers/Theme/index.tsx create mode 100644 playground/src/providers/Theme/shared.ts create mode 100644 playground/src/providers/Theme/types.ts create mode 100644 playground/src/providers/index.tsx create mode 100644 playground/src/utilities/canUseDOM.ts create mode 100644 playground/src/utilities/cn.ts create mode 100644 playground/src/utilities/deepMerge.ts create mode 100644 playground/src/utilities/formatDateTime.ts create mode 100644 playground/src/utilities/generateMeta.ts create mode 100644 playground/src/utilities/generatePreviewPath.ts create mode 100644 playground/src/utilities/getDocument.ts create mode 100644 playground/src/utilities/getGlobals.ts create mode 100644 playground/src/utilities/getMeUser.ts create mode 100644 playground/src/utilities/getRedirects.ts create mode 100644 playground/src/utilities/mergeOpenGraph.ts create mode 100644 playground/src/utilities/toKebabCase.ts create mode 100644 playground/src/utilities/useClickableCard.ts create mode 100644 playground/tailwind.config.mjs create mode 100644 playground/tsconfig.json diff --git a/playground/.editorconfig b/playground/.editorconfig new file mode 100644 index 0000000..d8e085a --- /dev/null +++ b/playground/.editorconfig @@ -0,0 +1,10 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +end_of_line = lf +max_line_length = null diff --git a/playground/.env.example b/playground/.env.example new file mode 100644 index 0000000..45327b9 --- /dev/null +++ b/playground/.env.example @@ -0,0 +1,16 @@ +# Added by Payload +# Database connection string +DATABASE_URI=file:./playground.db +# Used to encrypt JWT tokens +PAYLOAD_SECRET=5a9a296d283d03980a3110f3 +# Used to format links and URLs +PAYLOAD_PUBLIC_SERVER_URL=http://localhost:3000 +NEXT_PUBLIC_SERVER_URL=http://localhost:3000 +# Allow robots to index the site (optional) +NEXT_PUBLIC_IS_LIVE= +# Used to preview drafts +PAYLOAD_PUBLIC_DRAFT_SECRET=demo-draft-secret +NEXT_PRIVATE_DRAFT_SECRET=demo-draft-secret +# Used to revalidate static pages +REVALIDATION_KEY=demo-revalation-key +NEXT_PRIVATE_REVALIDATION_KEY=demo-revalation-key diff --git a/playground/.eslintignore b/playground/.eslintignore new file mode 100644 index 0000000..fa111f7 --- /dev/null +++ b/playground/.eslintignore @@ -0,0 +1,12 @@ +.tmp +**/.git +**/.hg +**/.pnp.* +**/.svn +**/.yarn/** +**/build +**/dist/** +**/node_modules +**/temp +playwright.config.ts +jest.config.js diff --git a/playground/.eslintrc.cjs b/playground/.eslintrc.cjs new file mode 100644 index 0000000..d21138f --- /dev/null +++ b/playground/.eslintrc.cjs @@ -0,0 +1,8 @@ +module.exports = { + extends: 'next', + root: true, + parserOptions: { + project: ['./tsconfig.json'], + tsconfigRootDir: __dirname, + }, +} diff --git a/playground/.gitignore b/playground/.gitignore new file mode 100644 index 0000000..52bec0f --- /dev/null +++ b/playground/.gitignore @@ -0,0 +1,10 @@ +build +dist / media +node_modules +.DS_Store +.env +.next +.vercel + +# Payload default media upload directory +public/media/ diff --git a/playground/.prettierignore b/playground/.prettierignore new file mode 100644 index 0000000..996b10e --- /dev/null +++ b/playground/.prettierignore @@ -0,0 +1,14 @@ +**/payload-types.ts +.tmp +**/.git +**/.hg +**/.pnp.* +**/.svn +**/.yarn/** +**/build +**/dist/** +**/node_modules +**/temp +**/docs/** +tsconfig.json + diff --git a/playground/.prettierrc.json b/playground/.prettierrc.json new file mode 100644 index 0000000..cb8ee26 --- /dev/null +++ b/playground/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "singleQuote": true, + "trailingComma": "all", + "printWidth": 100, + "semi": false +} diff --git a/playground/Dockerfile b/playground/Dockerfile new file mode 100644 index 0000000..e7a5ead --- /dev/null +++ b/playground/Dockerfile @@ -0,0 +1,24 @@ +FROM node:18.8-alpine as base + +FROM base as builder + +WORKDIR /home/node/app +COPY package*.json ./ + +COPY . . +RUN yarn install +RUN yarn build + +FROM base as runtime + +ENV NODE_ENV=production + +WORKDIR /home/node/app +COPY package*.json ./ +COPY yarn.lock ./ + +RUN yarn install --production + +EXPOSE 3000 + +CMD ["node", "dist/server.js"] diff --git a/playground/README.md b/playground/README.md new file mode 100644 index 0000000..a926690 --- /dev/null +++ b/playground/README.md @@ -0,0 +1,223 @@ +# Payload Website Template + +This is the official [Payload Website Template](https://github.com/payloadcms/payload/blob/main/templates/website). Use it to power websites, blogs, or portfolios from small to enterprise. This repo includes a fully-working backend, enterprise-grade admin panel, and a beautifully designed, production-ready website. + +This template is right for you if you are working on: + +- A personal or enterprise-grade website, blog, or portfolio +- A content publishing platform with a fully featured publication workflow +- A lead generation website with premium content gated behind authentication + +Core features: + +- [Pre-configured Payload Config](#how-it-works) +- [Authentication](#users-authentication) +- [Access Control](#access-control) +- [Layout Builder](#layout-builder) +- [Draft Preview](#draft-preview) +- [Live Preview](#live-preview) +- [Redirects](#redirects) +- [SEO](#seo) +- [Website](#website) + +## Quick Start + +To spin up this example locally, follow these steps: + +### Clone + +If you have not done so already, you need to have standalone copy of this repo on your machine. If you've already cloned this repo, skip to [Development](#development). + +#### Method 1 (recommended) + +Go to Payload Cloud and [clone this template](https://payloadcms.com/new/clone/website). This will create a new repository on your GitHub account with this template's code which you can then clone to your own machine. + +#### Method 2 + +Use the `create-payload-app` CLI to clone this template directly to your machine: + + npx create-payload-app@beta my-project -t website + +#### Method 3 + +Use the `git` CLI to clone this template directly to your machine: + + git clone -n --depth=1 --filter=tree:0 https://github.com/payloadcms/payload my-project && cd my-project && git sparse-checkout set --no-cone templates/website && git checkout && rm -rf .git && git init && git add . && git mv -f templates/website/{.,}* . && git add . && git commit -m "Initial commit" + +### Development + +1. First [clone the repo](#clone) if you have not done so already +1. `cd my-project && cp .env.example .env` to copy the example environment variables +1. `pnpm install && pnpm dev` to install dependencies and start the dev server +1. open `http://localhost:3000` to open the app in your browser + +That's it! Changes made in `./src` will be reflected in your app. Follow the on-screen instructions to login and create your first admin user. Then check out [Production](#production) once you're ready to build and serve your app, and [Deployment](#deployment) when you're ready to go live. + +## How it works + +The Payload config is tailored specifically to the needs of most websites. It is pre-configured in the following ways: + +### Collections + +See the [Collections](https://payloadcms.com/docs/beta/configuration/collections) docs for details on how to extend this functionality. + +- #### Users (Authentication) + + Users are auth-enabled collections that have access to the admin panel and unpublished content. See [Access Control](#access-control) for more details. + + For additional help, see the official [Auth Example](https://github.com/payloadcms/payload/tree/beta/examples/auth) or the [Authentication](https://payloadcms.com/docs/beta/authentication/overview#authentication-overview) docs. + +- #### Posts + + Posts are used to generated blog posts, news articles, or any other type of content that is published over time. All posts are layout builder enabled so you can generate unique layouts for each post using layout-building blocks, see [Layout Builder](#layout-builder) for more details. Posts are also draft-enabled so you can preview them before publishing them to your website, see [Draft Preview](#draft-preview) for more details. + +- #### Pages + + All pages are layout builder enabled so you can generate unique layouts for each page using layout-building blocks, see [Layout Builder](#layout-builder) for more details. Pages are also draft-enabled so you can preview them before publishing them to your website, see [Draft Preview](#draft-preview) for more details. + +- #### Media + + This is the uploads enabled collection used by pages, posts, and projects to contain media like images, videos, downloads, and other assets. + +- #### Categories + + A taxonomy used to group posts together. Categories can be nested inside of one another, for example "News > Technology". See the official [Payload Nested Docs Plugin](https://payloadcms.com/docs/beta/plugins/nested-docs) for more details. + +### Globals + +See the [Globals](https://payloadcms.com/docs/configuration/globals) docs for details on how to extend this functionality. + +- `Header` + + The data required by the header on your front-end like nav links. + +- `Footer` + + Same as above but for the footer of your site. + +## Access control + +Basic access control is setup to limit access to various content based based on publishing status. + +- `users`: Users can access the admin panel and create or edit content. +- `posts`: Everyone can access published posts, but only users can create, update, or delete them. +- `pages`: Everyone can access published pages, but only users can create, update, or delete them. + +For more details on how to extend this functionality, see the [Payload Access Control](https://payloadcms.com/docs/beta/access-control/overview#access-control) docs. + +## Layout Builder + +Create unique page layouts for any type of content using a powerful layout builder. This template comes pre-configured with the following layout building blocks: + +- Hero +- Content +- Media +- Call To Action +- Archive + +Each block is fully designed and built into the front-end website that comes with this template. See [Website](#website) for more details. + +## Lexical editor + +A deep editorial experience that allows complete freedom to focus just on writing content without breaking out of the flow with support for Payload blocks, media, links and other features provided out of the box. See [Lexical](https://payloadcms.com/docs/beta/lexical/overview) docs. + +## Draft Preview + +All posts and pages are draft-enabled so you can preview them before publishing them to your website. To do this, these collections use [Versions](https://payloadcms.com/docs/beta/configuration/collections#versions) with `drafts` set to `true`. This means that when you create a new post, project, or page, it will be saved as a draft and will not be visible on your website until you publish it. This also means that you can preview your draft before publishing it to your website. To do this, we automatically format a custom URL which redirects to your front-end to securely fetch the draft version of your content. + +Since the front-end of this template is statically generated, this also means that pages, posts, and projects will need to be regenerated as changes are made to published documents. To do this, we use an `afterChange` hook to regenerate the front-end when a document has changed and its `_status` is `published`. + +For more details on how to extend this functionality, see the official [Draft Preview Example](https://github.com/payloadcms/payload/tree/beta/examples/draft-preview). + +## Live preview + +In addition to draft previews you can also enable live preview to view your end resulting page as you're editing content with full support for SSR rendering. See [Live preview docs](https://payloadcms.com/docs/beta/live-preview/overview) for more details. + +## SEO + +This template comes pre-configured with the official [Payload SEO Plugin](https://payloadcms.com/docs/beta/plugins/seo) for complete SEO control from the admin panel. All SEO data is fully integrated into the front-end website that comes with this template. See [Website](#website) for more details. + +## Redirects + +If you are migrating an existing site or moving content to a new URL, you can use the `redirects` collection to create a proper redirect from old URLs to new ones. This will ensure that proper request status codes are returned to search engines and that your users are not left with a broken link. This template comes pre-configured with the official [Payload Redirects Plugin](https://payloadcms.com/docs/beta/plugins/redirects) for complete redirect control from the admin panel. All redirects are fully integrated into the front-end website that comes with this template. See [Website](#website) for more details. + +## Website + +This template includes a beautifully designed, production-ready front-end built with the [Next.js App Router](https://nextjs.org), served right alongside your Payload app in a instance. This makes it so that you can deploy both your backend and website where you need it. + +Core features: + +- [Next.js App Router](https://nextjs.org) +- [TypeScript](https://www.typescriptlang.org) +- [React Hook Form](https://react-hook-form.com) +- [Payload Admin Bar](https://github.com/payloadcms/payload-admin-bar) +- [TailwindCSS styling](https://tailwindcss.com/) +- [shadcn/ui components](https://ui.shadcn.com/) +- Authentication +- Fully featured blog +- Publication workflow +- User accounts +- Dark mode +- Pre-made layout building blocks +- SEO +- Redirects +- Live preview + +### Cache + +Although Next.js includes a robust set of caching strategies out of the box, Payload Cloud proxies and caches all files through Cloudflare using the [Official Cloud Plugin](https://github.com/payloadcms/plugin-cloud). This means that Next.js caching is not needed and is disabled by default. If you are hosting your app outside of Payload Cloud, you can easily reenable the Next.js caching mechanisms by removing the `no-store` directive from all fetch requests in `./src/app/_api` and then removing all instances of `export const dynamic = 'force-dynamic'` from pages files, such as `./src/app/(pages)/[slug]/page.tsx`. For more details, see the official [Next.js Caching Docs](https://nextjs.org/docs/app/building-your-application/caching). + +## Development + +To spin up this example locally, follow the [Quick Start](#quick-start). Then [Seed](#seed) the database with a few pages, posts, and projects. + +### Docker + +Alternatively, you can use [Docker](https://www.docker.com) to spin up this template locally. To do so, follow these steps: + +1. Follow [steps 1 and 2 from above](#development), the docker-compose file will automatically use the `.env` file in your project root +1. Next run `docker-compose up` +1. Follow [steps 4 and 5 from above](#development) to login and create your first admin user + +That's it! The Docker instance will help you get up and running quickly while also standardizing the development environment across your teams. + +### Seed + +To seed the database with a few pages, posts, and projects you can click the 'seed database' link from the admin panel. + +The seed script will also create a demo user for demonstration purposes only: + +- Demo Author + - Email: `demo-author@payloadcms.com` + - Password: `password` + +> 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. + +## Production + +To run Payload in production, you need to build and start the Admin panel. To do so, follow these steps: + +1. Invoke the `next build` script by running `pnpm build` or `npm run build` in your project root. This creates a `.next` directory with a production-ready admin bundle. +1. Finally run `pnpm start` or `npm run start` to run Node in production and serve Payload from the `.build` directory. +1. When you're ready to go live, see [Deployment](#deployment) for more details. + +### Deploying to Payload Cloud + +The easiest way to deploy your project is to use [Payload Cloud](https://payloadcms.com/new/import), a one-click hosting solution to deploy production-ready instances of your Payload apps directly from your GitHub repo. + +### Deploying to Vercel + +Coming soon. + +### Self-hosting + +Before deploying your app, you need to: + +1. Ensure your app builds and serves in production. See [Production](#production) for more details. +2. Serve it from a + +You can also deploy your app manually, check out the [deployment documentation](https://payloadcms.com/docs/beta/production/deployment) for full details. + +## Questions + +If you have any issues or questions, reach out to us on [Discord](https://discord.com/invite/payload) or start a [GitHub discussion](https://github.com/payloadcms/payload/discussions). diff --git a/playground/components.json b/playground/components.json new file mode 100644 index 0000000..f7e50ae --- /dev/null +++ b/playground/components.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.js", + "css": "src/app/(frontend)/globals.css", + "baseColor": "slate", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/_utilities" + } +} diff --git a/playground/docker-compose.yml b/playground/docker-compose.yml new file mode 100644 index 0000000..4c9fc51 --- /dev/null +++ b/playground/docker-compose.yml @@ -0,0 +1,31 @@ +version: '3' + +services: + payload: + image: node:18-alpine + ports: + - '3000:3000' + volumes: + - .:/home/node/app + - node_modules:/home/node/app/node_modules + working_dir: /home/node/app/ + command: sh -c "yarn install && yarn dev" + depends_on: + - mongo + env_file: + - .env + + mongo: + image: mongo:latest + ports: + - '27017:27017' + command: + - --storageEngine=wiredTiger + volumes: + - data:/data/db + logging: + driver: none + +volumes: + data: + node_modules: diff --git a/playground/next-env.d.ts b/playground/next-env.d.ts new file mode 100644 index 0000000..4f11a03 --- /dev/null +++ b/playground/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/playground/next.config.js b/playground/next.config.js new file mode 100644 index 0000000..e8e6935 --- /dev/null +++ b/playground/next.config.js @@ -0,0 +1,25 @@ +import { withPayload } from '@payloadcms/next/withPayload' + +import redirects from './redirects.js' + +const NEXT_PUBLIC_SERVER_URL = process.env.NEXT_PUBLIC_SERVER_URL || 'http://localhost:3000' + +/** @type {import('next').NextConfig} */ +const nextConfig = { + images: { + remotePatterns: [ + ...[NEXT_PUBLIC_SERVER_URL /* 'https://example.com' */].map((item) => { + const url = new URL(item) + + return { + hostname: url.hostname, + protocol: url.protocol.replace(':', ''), + } + }), + ], + }, + reactStrictMode: true, + redirects, +} + +export default withPayload(nextConfig) diff --git a/playground/package.json b/playground/package.json new file mode 100644 index 0000000..4af065a --- /dev/null +++ b/playground/package.json @@ -0,0 +1,86 @@ +{ + "name": "playground", + "version": "1.0.0", + "description": "Website template for Payload", + "license": "MIT", + "type": "module", + "scripts": { + "build": "cross-env NODE_OPTIONS=--no-deprecation next build", + "dev": "cross-env NODE_OPTIONS=--no-deprecation next dev", + "dev:prod": "cross-env NODE_OPTIONS=--no-deprecation rm -rf .next && pnpm build && pnpm start", + "generate:importmap": "cross-env NODE_OPTIONS=--no-deprecation payload generate:importmap", + "generate:types": "cross-env NODE_OPTIONS=--no-deprecation payload generate:types", + "ii": "cross-env NODE_OPTIONS=--no-deprecation pnpm --ignore-workspace install", + "lint": "cross-env NODE_OPTIONS=--no-deprecation next lint", + "lint:fix": "cross-env NODE_OPTIONS=--no-deprecation next lint --fix", + "payload": "cross-env NODE_OPTIONS=--no-deprecation payload", + "reinstall": "cross-env NODE_OPTIONS=--no-deprecation rm -rf node_modules && rm pnpm-lock.yaml && pnpm --ignore-workspace install", + "start": "cross-env NODE_OPTIONS=--no-deprecation next start" + }, + "dependencies": { + "@payload-llm-plugins/semantic-search": "workspace:*", + "@payloadcms/live-preview-react": "beta", + "@payloadcms/next": "beta", + "@payloadcms/plugin-cloud": "beta", + "@payloadcms/plugin-form-builder": "beta", + "@payloadcms/plugin-nested-docs": "beta", + "@payloadcms/plugin-redirects": "beta", + "@payloadcms/plugin-seo": "beta", + "@payloadcms/richtext-lexical": "beta", + "@payloadcms/ui": "beta", + "@radix-ui/react-checkbox": "^1.0.4", + "@radix-ui/react-label": "^2.0.2", + "@radix-ui/react-select": "^2.0.0", + "@radix-ui/react-slot": "^1.0.2", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", + "cross-env": "^7.0.3", + "geist": "^1.3.0", + "graphql": "^16.8.2", + "jsonwebtoken": "9.0.2", + "lexical": "0.17.0", + "lucide-react": "^0.378.0", + "next": "15.0.0-canary.104", + "payload": "beta", + "payload-admin-bar": "^1.0.6", + "prism-react-renderer": "^2.3.1", + "react": "19.0.0-rc-06d0b89e-20240801", + "react-dom": "19.0.0-rc-06d0b89e-20240801", + "react-hook-form": "7.45.4", + "sharp": "0.32.6", + "tailwind-merge": "^2.3.0", + "tailwindcss-animate": "^1.0.7", + "@payloadcms/db-sqlite": "beta" + }, + "devDependencies": { + "@next/eslint-plugin-next": "^13.1.6", + "@payloadcms/eslint-config": "^1.1.1", + "@tailwindcss/typography": "^0.5.13", + "@types/escape-html": "^1.0.2", + "@types/jsonwebtoken": "^9.0.6", + "@types/node": "22.5.4", + "@types/react": "npm:types-react@19.0.0-rc.0", + "@types/react-dom": "npm:types-react-dom@19.0.0-rc.0", + "autoprefixer": "^10.4.19", + "copyfiles": "^2.4.1", + "eslint": "^8", + "eslint-config-next": "15.0.0-canary.104", + "postcss": "^8.4.38", + "prettier": "^3.0.3", + "tailwindcss": "^3.4.3", + "typescript": "5.6.2" + }, + "engines": { + "node": "^18.20.2 || >=20.9.0" + }, + "pnpm": { + "overrides": { + "@types/react": "npm:types-react@19.0.0-rc.0", + "@types/react-dom": "npm:types-react-dom@19.0.0-rc.0" + } + }, + "overrides": { + "@types/react": "npm:types-react@19.0.0-rc.0", + "@types/react-dom": "npm:types-react-dom@19.0.0-rc.0" + } +} diff --git a/playground/playground.db b/playground/playground.db new file mode 100644 index 0000000000000000000000000000000000000000..a8b3d606d5b0a315c50ec7858bab63e16c75dcbe GIT binary patch literal 962560 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 literal 0 HcmV?d00001 diff --git a/playground/public/favicon.svg b/playground/public/favicon.svg new file mode 100644 index 0000000..86dc2de --- /dev/null +++ b/playground/public/favicon.svg @@ -0,0 +1,15 @@ + + + + + diff --git a/playground/public/website-template-OG.webp b/playground/public/website-template-OG.webp new file mode 100644 index 0000000000000000000000000000000000000000..74bc241ca0ecc1484531c1565da701f513c4ac3d GIT binary patch literal 13670 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 { + const internetExplorerRedirect = { + destination: '/ie-incompatible.html', + has: [ + { + type: 'header', + key: 'user-agent', + value: '(.*Trident.*)', // all ie browsers + }, + ], + permanent: false, + source: '/:path((?!ie-incompatible.html$).*)', // all pages except the incompatibility page + } + + const redirects = [internetExplorerRedirect] + + return redirects +} + +export default redirects diff --git a/playground/src/Footer/Component.tsx b/playground/src/Footer/Component.tsx new file mode 100644 index 0000000..00d5d5e --- /dev/null +++ b/playground/src/Footer/Component.tsx @@ -0,0 +1,39 @@ +import { getCachedGlobal } from '../utilities/getGlobals' +import Link from 'next/link' +import React from 'react' + +import type { Footer } from '../payload-types' + +import { ThemeSelector } from '../providers/Theme/ThemeSelector' +import { CMSLink } from '../components/Link' + +export async function Footer() { + const footer: Footer = await getCachedGlobal('footer')() + + const navItems = footer?.navItems || [] + + return ( +
+
+ + + Payload Logo + + + +
+ + +
+
+
+ ) +} diff --git a/playground/src/Footer/config.ts b/playground/src/Footer/config.ts new file mode 100644 index 0000000..5813a7e --- /dev/null +++ b/playground/src/Footer/config.ts @@ -0,0 +1,26 @@ +import type { GlobalConfig } from 'payload' + +import { link } from '../fields/link' +import { revalidateFooter } from './hooks/revalidateFooter' + +export const Footer: GlobalConfig = { + slug: 'footer', + access: { + read: () => true, + }, + fields: [ + { + name: 'navItems', + type: 'array', + fields: [ + link({ + appearances: false, + }), + ], + maxRows: 6, + }, + ], + hooks: { + afterChange: [revalidateFooter], + }, +} diff --git a/playground/src/Footer/hooks/revalidateFooter.ts b/playground/src/Footer/hooks/revalidateFooter.ts new file mode 100644 index 0000000..f6267c0 --- /dev/null +++ b/playground/src/Footer/hooks/revalidateFooter.ts @@ -0,0 +1,11 @@ +import type { GlobalAfterChangeHook } from 'payload' + +import { revalidateTag } from 'next/cache' + +export const revalidateFooter: GlobalAfterChangeHook = ({ doc, req: { payload } }) => { + payload.logger.info(`Revalidating footer`) + + revalidateTag('global_footer') + + return doc +} diff --git a/playground/src/Header/Component.client.tsx b/playground/src/Header/Component.client.tsx new file mode 100644 index 0000000..b4c2518 --- /dev/null +++ b/playground/src/Header/Component.client.tsx @@ -0,0 +1,43 @@ +'use client' +import { useHeaderTheme } from '../providers/HeaderTheme' +import Link from 'next/link' +import { usePathname } from 'next/navigation' +import React, { useEffect, useState } from 'react' + +import type { Header } from '../payload-types' + +import { Logo } from '../components/Logo/Logo' +import { HeaderNav } from './Nav' + +interface HeaderClientProps { + header: Header +} + +export const HeaderClient: React.FC = ({ header }) => { + /* Storing the value in a useState to avoid hydration errors */ + const [theme, setTheme] = useState(null) + const { headerTheme, setHeaderTheme } = useHeaderTheme() + const pathname = usePathname() + + useEffect(() => { + setHeaderTheme(null) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [pathname]) + + useEffect(() => { + if (headerTheme && headerTheme !== theme) setTheme(headerTheme) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [headerTheme]) + + return ( +
+ + + + +
+ ) +} diff --git a/playground/src/Header/Component.tsx b/playground/src/Header/Component.tsx new file mode 100644 index 0000000..e73e570 --- /dev/null +++ b/playground/src/Header/Component.tsx @@ -0,0 +1,11 @@ +import { HeaderClient } from './Component.client' +import { getCachedGlobal } from '../utilities/getGlobals' +import React from 'react' + +import type { Header } from '../payload-types' + +export async function Header() { + const header: Header = await getCachedGlobal('header', 1)() + + return +} diff --git a/playground/src/Header/Nav/index.tsx b/playground/src/Header/Nav/index.tsx new file mode 100644 index 0000000..8aa8922 --- /dev/null +++ b/playground/src/Header/Nav/index.tsx @@ -0,0 +1,19 @@ +'use client' + +import React from 'react' + +import type { Header as HeaderType } from '../../payload-types' + +import { CMSLink } from '../../components/Link' + +export const HeaderNav: React.FC<{ header: HeaderType }> = ({ header }) => { + const navItems = header?.navItems || [] + + return ( + + ) +} diff --git a/playground/src/Header/config.ts b/playground/src/Header/config.ts new file mode 100644 index 0000000..918666a --- /dev/null +++ b/playground/src/Header/config.ts @@ -0,0 +1,26 @@ +import type { GlobalConfig } from 'payload' + +import { link } from '../fields/link' +import { revalidateHeader } from './hooks/revalidateHeader' + +export const Header: GlobalConfig = { + slug: 'header', + access: { + read: () => true, + }, + fields: [ + { + name: 'navItems', + type: 'array', + fields: [ + link({ + appearances: false, + }), + ], + maxRows: 6, + }, + ], + hooks: { + afterChange: [revalidateHeader], + }, +} diff --git a/playground/src/Header/hooks/revalidateHeader.ts b/playground/src/Header/hooks/revalidateHeader.ts new file mode 100644 index 0000000..7315cc2 --- /dev/null +++ b/playground/src/Header/hooks/revalidateHeader.ts @@ -0,0 +1,11 @@ +import type { GlobalAfterChangeHook } from 'payload' + +import { revalidateTag } from 'next/cache' + +export const revalidateHeader: GlobalAfterChangeHook = ({ doc, req: { payload } }) => { + payload.logger.info(`Revalidating header`) + + revalidateTag('global_header') + + return doc +} diff --git a/playground/src/access/anyone.ts b/playground/src/access/anyone.ts new file mode 100644 index 0000000..bf37c3a --- /dev/null +++ b/playground/src/access/anyone.ts @@ -0,0 +1,3 @@ +import type { Access } from 'payload' + +export const anyone: Access = () => true diff --git a/playground/src/access/authenticated.ts b/playground/src/access/authenticated.ts new file mode 100644 index 0000000..752ac55 --- /dev/null +++ b/playground/src/access/authenticated.ts @@ -0,0 +1,9 @@ +import type { AccessArgs } from 'payload' + +import type { User } from '../payload-types' + +type isAuthenticated = (args: AccessArgs) => boolean + +export const authenticated: isAuthenticated = ({ req: { user } }) => { + return Boolean(user) +} diff --git a/playground/src/access/authenticatedOrPublished.ts b/playground/src/access/authenticatedOrPublished.ts new file mode 100644 index 0000000..e49198f --- /dev/null +++ b/playground/src/access/authenticatedOrPublished.ts @@ -0,0 +1,13 @@ +import type { Access } from 'payload' + +export const authenticatedOrPublished: Access = ({ req: { user } }) => { + if (user) { + return true + } + + return { + _status: { + equals: 'published', + }, + } +} diff --git a/playground/src/app/(frontend)/[slug]/page.tsx b/playground/src/app/(frontend)/[slug]/page.tsx new file mode 100644 index 0000000..4d02f05 --- /dev/null +++ b/playground/src/app/(frontend)/[slug]/page.tsx @@ -0,0 +1,89 @@ +import type { Metadata } from 'next' + +import { PayloadRedirects } from '../../../components/PayloadRedirects' +import configPromise from '@payload-config' +import { getPayloadHMR } from '@payloadcms/next/utilities' +import { draftMode } from 'next/headers' +import React, { cache } from 'react' +import { homeStatic } from '../../../endpoints/seed/home-static' + +import type { Page as PageType } from '../../../payload-types' + +import { RenderBlocks } from '../../../blocks/RenderBlocks' +import { RenderHero } from '../../../heros/RenderHero' +import { generateMeta } from '../../../utilities/generateMeta' + +export async function generateStaticParams() { + const payload = await getPayloadHMR({ config: configPromise }) + const pages = await payload.find({ + collection: 'pages', + draft: false, + limit: 1000, + overrideAccess: false, + }) + + return pages.docs + ?.filter((doc) => { + return doc.slug !== 'home' + }) + .map(({ slug }) => slug) +} + +export default async function Page({ params: { slug = 'home' } }) { + const url = '/' + slug + + let page: PageType | null + + page = await queryPageBySlug({ + slug, + }) + + // Remove this code once your website is seeded + if (!page) { + page = homeStatic + } + + if (!page) { + return + } + + const { hero, layout } = page + + return ( +
+ {/* Allows redirects for valid pages too */} + + + + +
+ ) +} + +export async function generateMetadata({ params: { slug = 'home' } }): Promise { + const page = await queryPageBySlug({ + slug, + }) + + return generateMeta({ doc: page }) +} + +const queryPageBySlug = cache(async ({ slug }: { slug: string }) => { + const { isEnabled: draft } = draftMode() + + const payload = await getPayloadHMR({ config: configPromise }) + + const result = await payload.find({ + collection: 'pages', + draft, + limit: 1, + overrideAccess: true, + where: { + slug: { + equals: slug, + }, + }, + }) + + return result.docs?.[0] || null +}) diff --git a/playground/src/app/(frontend)/globals.css b/playground/src/app/(frontend)/globals.css new file mode 100644 index 0000000..13be9b7 --- /dev/null +++ b/playground/src/app/(frontend)/globals.css @@ -0,0 +1,103 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + h1, + h2, + h3, + h4, + h5, + h6 { + font-size: auto; + font-weight: auto; + } + + :root { + --background: 0 0% 100%; + --foreground: 222.2 84% 4.9%; + + --card: 240 5% 96%; + --card-foreground: 222.2 84% 4.9%; + + --popover: 0 0% 100%; + --popover-foreground: 222.2 84% 4.9%; + + --primary: 222.2 47.4% 11.2%; + --primary-foreground: 210 40% 98%; + + --secondary: 210 40% 96.1%; + --secondary-foreground: 222.2 47.4% 11.2%; + + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; + + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 210 40% 98%; + + --border: 240 6% 90%; + --input: 214.3 31.8% 91.4%; + --ring: 222.2 84% 4.9%; + + --radius: 0.2rem; + + --success: 196 52% 74%; + --warning: 34 89% 85%; + --error: 10 100% 86%; + } + + [data-theme='dark'] { + --background: 0 0% 0%; + --foreground: 210 40% 98%; + + --card: 240 6% 10%; + --card-foreground: 210 40% 98%; + + --popover: 222.2 84% 4.9%; + --popover-foreground: 210 40% 98%; + + --primary: 210 40% 98%; + --primary-foreground: 222.2 47.4% 11.2%; + + --secondary: 217.2 32.6% 17.5%; + --secondary-foreground: 210 40% 98%; + + --muted: 217.2 32.6% 17.5%; + --muted-foreground: 215 20.2% 65.1%; + + --accent: 217.2 32.6% 17.5%; + --accent-foreground: 210 40% 98%; + + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 210 40% 98%; + + --border: 240 4% 16%; + --input: 217.2 32.6% 17.5%; + --ring: 212.7 26.8% 83.9%; + + --success: 196 100% 14%; + --warning: 34 51% 25%; + --error: 10 39% 43%; + } +} + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} + +html { + opacity: 0; +} + +html[data-theme='dark'], +html[data-theme='light'] { + opacity: initial; +} diff --git a/playground/src/app/(frontend)/layout.tsx b/playground/src/app/(frontend)/layout.tsx new file mode 100644 index 0000000..cff2fbf --- /dev/null +++ b/playground/src/app/(frontend)/layout.tsx @@ -0,0 +1,53 @@ +import type { Metadata } from 'next' + +import { cn } from '../../utilities/cn' +import { GeistMono } from 'geist/font/mono' +import { GeistSans } from 'geist/font/sans' +import React from 'react' + +import { AdminBar } from '../../components/AdminBar' +import { Footer } from '../../Footer/Component' +import { Header } from '../../Header/Component' +import { LivePreviewListener } from '../../components/LivePreviewListener' +import { Providers } from '../../providers' +import { InitTheme } from '../../providers/Theme/InitTheme' +import { mergeOpenGraph } from '../../utilities/mergeOpenGraph' +import './globals.css' +import { draftMode } from 'next/headers' + +export default async function RootLayout({ children }: { children: React.ReactNode }) { + const { isEnabled } = draftMode() + + return ( + + + + + + + + + + + +
+ {children} +