Compare commits

...

96 Commits

Author SHA1 Message Date
tobias
2e372d1aae Makefile: sync_media
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-16 15:34:24 +02:00
tobias
1496eefc14 Makefile: restore mongodb 2024-07-16 13:53:19 +02:00
tobias
e93b5e653a Copy mongodump directly from container 2024-07-16 13:01:41 +02:00
tobias
8fa28a31ce Make command for dumping mongodb 2024-07-16 11:55:55 +02:00
tobias
64da6f4553 Restart nextjs container in prod
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-13 17:26:48 +02:00
tobias
5e74f45855 Always restart
Some checks failed
continuous-integration/drone/push Build is failing
2024-07-13 15:39:31 +02:00
tobias
ab8b409725 Bump combo x2
Some checks failed
continuous-integration/drone/push Build is failing
2024-07-11 21:40:18 +02:00
tobias
35ac1ece0e Fix uploads volume path in the correct file this time
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-11 21:35:24 +02:00
tobias
b8a92095d8 Bump
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-11 21:26:16 +02:00
tobias
9ce6eb5bcc Correct upload path for docker volume
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-11 21:21:18 +02:00
tobias
ee243f7d20 Don't fetch user on preview pages
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-11 21:06:38 +02:00
tobias
9edb207991 Clean up preview pages
Some checks failed
continuous-integration/drone/push Build is failing
2024-07-11 21:04:47 +02:00
tobias
e060d942f0 Fix payload_uploads volume?
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-11 21:00:30 +02:00
tobias
c09862c702 Don't check null user on preview pages
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-11 20:29:31 +02:00
tobias
fdfbce506e Revert "Remove baseURL from preview url"
All checks were successful
continuous-integration/drone/push Build is passing
This reverts commit f16e874f31.
2024-07-06 22:19:52 +02:00
tobias
f16e874f31 Remove baseURL from preview url
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-06 22:11:46 +02:00
tobias
8d05864c15 Only check roles if a user when accessing pages
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-06 19:57:55 +02:00
tobias
90f1750945 Deny access if no roles
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-06 19:49:50 +02:00
tobias
abe4a3c883 Revert "Temporarily turn off access controls"
This reverts commit 519df326ce.
2024-07-06 19:34:47 +02:00
tobias
519df326ce Temporarily turn off access controls
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-06 19:11:38 +02:00
tobias
3d4499fb80 Create new admin user
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-05 23:26:50 +02:00
tobias
a27c335113 Catch non-arrays
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-05 22:42:32 +02:00
tobias
199234a7e2 Remove misbehaving test route
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-05 22:34:09 +02:00
tobias
95a00c63ea Disable code obfuscation
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-05 22:24:10 +02:00
3wc
8a85724baf Revert bogus change to CMD syntax
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-05 16:13:32 -04:00
tobias
b25a83b164 Revert "Copy entrypoint"
All checks were successful
continuous-integration/drone/push Build is passing
This reverts commit 83ea63dde4.
2024-07-05 22:04:30 +02:00
3wc
23357f0934 Align image names
Some checks failed
continuous-integration/drone/push Build is failing
2024-07-05 16:02:31 -04:00
tobias
83ea63dde4 Copy entrypoint
Some checks failed
continuous-integration/drone/push Build is failing
2024-07-05 21:59:17 +02:00
3wc
efd4be35f7 Attempt to fix CMD syntax
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-05 15:53:47 -04:00
tobias
b6e56b0f83 chmod +x docker-entrypoint
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-05 21:47:22 +02:00
tobias
b6357c5676 Modify dockerfile
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2024-06-26 11:20:50 +02:00
tobias
7a198ec4c3 Depend on mongo
All checks were successful
continuous-integration/drone/push Build is passing
2024-06-25 23:27:28 +02:00
tobias
1ba93cfb6d Remove webpack config
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-25 22:44:54 +02:00
tobias
116bdcecdb Don't use variables in webpack config
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-25 22:44:15 +02:00
tobias
1b5f02637c Make posts page dynamic route
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-25 22:19:08 +02:00
tobias
75827c3bc7 Add standalone db script
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-25 11:50:12 +02:00
tobias
f1ac6429c7 Disable theme switcher for now
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-24 22:07:16 +02:00
tobias
efec10e6d6 Target dockerfile prod step in prod
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-24 18:00:44 +02:00
tobias
cb0a2f77a8 fix build errors
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-24 17:34:40 +02:00
tobias
043470fe0c Only target frontned for TW
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-24 13:52:25 +02:00
tobias
689c21b69a Separate posts & pages previews 2024-06-24 13:52:06 +02:00
tobias
7943ffbd55 Create post preview component 2024-06-24 13:39:37 +02:00
tobias
5d652dc606 Implement live preview 2024-06-24 11:53:43 +02:00
tobias
4dfe80213e Use collection config for slugs and correct locale for links
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-24 09:55:03 +02:00
tobias
f3596d4258 Improve post spacing 2024-06-24 09:35:19 +02:00
tobias
bb426df41a Style blockquotes 2024-06-24 09:03:08 +02:00
tobias
f558be2bcd Use base layout and style
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-24 08:47:53 +02:00
tobias
d6fe6446a5 Add posts
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-24 01:22:01 +02:00
tobias
5eb7b7037b Add paystro visual components 2024-06-24 00:05:41 +02:00
tobias
0a14bb39de Create author block
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-23 23:21:26 +02:00
tobias
fa48a192e3 Enable richtext block
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-23 23:12:32 +02:00
tobias
9fa279b962 Add deepmerge dependency 2024-06-23 22:33:25 +02:00
tobias
5d191e1e28 Update lockfile
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-23 22:23:24 +02:00
tobias
60f7a4e4f2 Add block editing
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-23 22:23:06 +02:00
tobias
f9ee909e01 Create author component
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-23 16:55:05 +02:00
tobias
b69d3ca9bc Clean up 2024-06-23 15:01:05 +02:00
tobias
d77c33f548 Add missing type declaration for isEditor
Some checks reported errors
continuous-integration/drone/promote/production Build is failing
continuous-integration/drone/push Build was killed
2024-06-23 11:21:50 +02:00
tobias
e63dd42b6c fix: Access function types
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-23 11:14:52 +02:00
tobias
2cfca5e7cf Add admin role for base user
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-23 10:52:32 +02:00
tobias
f928366b27 Push base file for payload types
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-23 10:46:29 +02:00
tobias
b448fcc8bb fix: Typo
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-23 10:42:27 +02:00
tobias
26363eb249 Generate pl types prior to building for production
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-23 10:39:59 +02:00
tobias
ec284177ee Update readme
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-23 10:32:36 +02:00
tobias
bc6c4ddde7 Use bind mount for dev container 2024-06-23 10:32:27 +02:00
tobias
ace6e50b73 Move payload-types dir 2024-06-23 10:31:42 +02:00
tobias
e002125805 Add collections & access functions from paystro 2024-06-23 10:30:00 +02:00
tobias
cab583964b Add dev volume for payload uploads
Some checks reported errors
continuous-integration/drone/push Build was killed
2024-06-20 10:41:57 +02:00
tobias
de5e8ab560 Create dev script
All checks were successful
continuous-integration/drone/push Build is passing
2024-06-20 10:37:21 +02:00
tobias
02556b6d62 Update env example 2024-06-19 22:46:17 +02:00
tobias
a81703d75a Remove yarnlock 2024-06-19 22:45:32 +02:00
tobias
22298a82e3 Remove drone token from docker-compose 2024-06-19 22:28:30 +02:00
tobias
c23501d5a5 Remove silly little typo
All checks were successful
continuous-integration/drone/push Build is passing
2024-06-19 22:02:09 +02:00
tobias
12621849e2 Move public folder .. 2024-06-19 22:01:54 +02:00
tobias
3a8e66dcec Revert "Configure nextjs output file tracing"
All checks were successful
continuous-integration/drone/push Build is passing
This reverts commit 63b8c3f5ae.
2024-06-19 21:53:55 +02:00
tobias
63b8c3f5ae Configure nextjs output file tracing
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-19 21:49:09 +02:00
tobias
6b49eebeb8 Cleanup README 2024-06-19 21:33:32 +02:00
tobias
82d5beaa90 Update README
All checks were successful
continuous-integration/drone/push Build is passing
2024-06-19 18:47:56 +02:00
tobias
18995ef44f Correct uploads path
All checks were successful
continuous-integration/drone/push Build is passing
2024-06-19 16:31:27 +02:00
tobias
d1a0449d67 Fix payload media volume & remove old volume
All checks were successful
continuous-integration/drone/push Build is passing
2024-06-19 16:21:12 +02:00
tobias
ed5a2ccc05 Remove vestigial env variables
All checks were successful
continuous-integration/drone/push Build is passing
2024-06-19 14:43:47 +02:00
tobias
57601b133a Add urlencoding for mongo_password
All checks were successful
continuous-integration/drone/push Build is passing
2024-06-19 12:08:41 +02:00
3wc
8ceaf85ce0 Add entrypoint to image, use it
All checks were successful
continuous-integration/drone/push Build is passing
2024-06-18 15:44:18 -06:00
tobias
4fb8c8362d Change port to 3000
All checks were successful
continuous-integration/drone/push Build is passing
2024-06-18 23:36:00 +02:00
tobias
09d8134d04 Set hostname -> 0.0.0.0
All checks were successful
continuous-integration/drone/push Build is passing
2024-06-18 19:51:59 +02:00
tobias
b437f7993d Get env variables correctly
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-18 08:30:03 +02:00
tobias
a169c111c2 Add hostname & port to nextjs.config
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-18 08:28:50 +02:00
tobias
f6c1f4fe78 Add hostname & port env
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-18 08:14:38 +02:00
tobias
97173fce29 Revert domain name
All checks were successful
continuous-integration/drone/push Build is passing
2024-06-17 23:37:01 +02:00
tobias
69ead39b39 Update domain
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-17 23:17:42 +02:00
tobias
6a151fdd6f Readd secret token for now
All checks were successful
continuous-integration/drone/push Build is passing
2024-06-17 22:14:15 +02:00
tobias
40e1ab22ad Fix drone step reference name
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-17 22:00:15 +02:00
tobias
e215f882b7 Fix payload secret name
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2024-06-17 17:25:18 +02:00
tobias
95e03404c5 Re-add payload secret
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-17 15:49:03 +02:00
tobias
e9d28c472d Clean up drone and docker-compose
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-17 15:39:11 +02:00
tobias
80fdf4332f Fix static assets dir
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-16 20:49:02 +02:00
tobias
0a9255122a Create yarnlock
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-16 16:39:12 +02:00
70 changed files with 2452 additions and 553 deletions

View File

@ -2,9 +2,10 @@
kind: pipeline
name: publish pipeline
steps:
- name: publish nextload container
- name: publish nextjs container
image: plugins/docker
settings:
target: prod
username: 3wordchant
password:
from_secret: git_autonomic_zone_token_3wc
@ -25,11 +26,7 @@ steps:
DOMAIN: nextload.swarm-demo.autonomic.zone
STACK_NAME: nextload-demo_autonomic_zone
SECRET_PAYLOAD_SECRET_VERSION: v1
SECRET_TOKEN_VERSION: v1
SECRET_MONGO_PASSWORD_VERSION: v1
NGINX_CONF_VERSION: v1
REPOSITORY: "autonomic-cooperative/nextload"
DRONE_URL: "https://drone.autonomic.zone"
PAYLOAD_URL: "https://nextload.swarm-demo.autonomic.zone/admin" # Not needed anymore?
depends_on:
- publish nextload container
- publish nextjs container

View File

@ -1,3 +1,7 @@
STACK_NAME=nextload
PAYLOAD_SECRET=jawliejfilwajefSEANlawefawfewag349jwgo3gj4w
MONGODB_URI=mongodb://127.0.0.1:27017/next-payload-3
# POSTGRES_URI=postgresql://postgres:password123@127.0.0.1:5432/next-payload-3
MONGO_USER=mongo
MONGO_PASSWORD=mongo
MONGO_HOST=mongo

View File

@ -1,5 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Beta Issues
url: https://github.com/payloadcms/payload/issues/new/choose
about: Any issues should be opened on the main repository

2
.gitignore vendored
View File

@ -41,3 +41,5 @@ next-env.d.ts
.env
/media
/types/payload-types.ts

View File

@ -1,37 +1,49 @@
# Build stage
FROM node:20-slim as builder
FROM node:20-slim as base
WORKDIR /base
# Local Development
FROM base AS dev
ENTRYPOINT []
CMD ["pnpm", "dev:next"]
# Production: build
FROM base AS builder
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
WORKDIR /builder
COPY package.json pnpm-lock.yaml ./
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
ENV NODE_ENV=production
RUN pnpm install --frozen-lockfile
COPY . .
ENV NODE_ENV=production
RUN pnpm generate:types
RUN pnpm build
# Production stage: serve the application
# Production: copy build artifacts
FROM node:20-slim as runner
WORKDIR /runner
WORKDIR /prod
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
## Copy build artifacts from the build stage
COPY --from=builder /builder/package.json ./
#COPY --from=builder /builder/node_modules ./node_modules
COPY --from=builder /builder/.next/standalone ./.next/standalone/
COPY --from=builder /builder/.next/static ./.next/static
COPY --from=builder /builder/public ./public
COPY --from=builder /builder/next.config.mjs ./
COPY --from=builder /base/package.json ./
COPY --from=builder /base/.next/standalone ./.next/standalone/
COPY --from=builder /base/.next/static ./.next/standalone/.next/static
COPY --from=builder /base/public ./.next/standalone/public
COPY --from=builder /base/next.config.mjs ./
FROM runner as server
EXPOSE 3000
# Production: serve the website
FROM runner as prod
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
COPY docker-entrypoint.sh /docker-entrypoint.sh
RUN chmod +x /docker-entrypoint.sh
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["pnpm", "start:standalone"]

49
Makefile Normal file
View File

@ -0,0 +1,49 @@
.PHONY: sync_db
# Define variables
REMOTE_USER=tma
REMOTE_SERVER=swarm-demo.autonomic.zone
REMOTE_PORT=222
REMOTE_CONTAINER_PATH=/data/db/mongodump
LOCAL_PATH=/tmp/mongodump
LOCAL_DB=test
LOCAL_CONTAINER_NAME=nextload-mongo-1
REMOTE_VOLUME_NAME = nextload-demo_autonomic_zone_payload_uploads
LOCAL_VOLUME_NAME = nextload_payload_uploads
ENV_FILE=.env
include $(ENV_FILE)
sync_db:
@echo "Step 1: Dump the database on production"
docker --context swarm-demo.autonomic.zone exec -it $$(docker --context swarm-demo.autonomic.zone ps -q -f name=nextload-demo_autonomic_zone_mongo) \
bash -c 'rm -rf $(REMOTE_CONTAINER_PATH) && mongodump -u mongo -p "$$(cat /run/secrets/mongo_password)" -o $(REMOTE_CONTAINER_PATH) && ls -l $(REMOTE_CONTAINER_PATH)'
@echo "Step 2: Copy the dump from the remote MongoDB container to the local machine"
rm -rf $(LOCAL_PATH)
mkdir -p $(LOCAL_PATH) # Create the target directory if it doesn't exist
ssh -p $(REMOTE_PORT) $(REMOTE_USER)@$(REMOTE_SERVER) \
"docker exec $$(docker --context swarm-demo.autonomic.zone ps -q -f name=nextload-demo_autonomic_zone_mongo) \
tar -cC $(REMOTE_CONTAINER_PATH) ." | tar -xC $(LOCAL_PATH)
@echo "Step 3: Copy the dump from local machine to MongoDB container"
docker cp $(LOCAL_PATH)/ $(LOCAL_CONTAINER_NAME):/tmp/mongodump
@echo "Step 4: Drop the existing local database and restore the dump"
docker exec -it $(LOCAL_CONTAINER_NAME) mongorestore --drop --username $(MONGO_USER) --password $(MONGO_PASSWORD) --authenticationDatabase admin --db $(LOCAL_DB) /tmp/mongodump/$(LOCAL_DB)
sync_media:
@echo "Step 1: Create a tar archive of the remote Docker volume"
rm -rf /tmp/$(REMOTE_VOLUME_NAME)
mkdir -p /tmp/$(REMOTE_VOLUME_NAME)
ssh -p $(REMOTE_PORT) $(REMOTE_USER)@$(REMOTE_SERVER) \
"docker run --rm -v $(REMOTE_VOLUME_NAME):/volume -v /tmp:/backup alpine tar -czf /backup/$(REMOTE_VOLUME_NAME).tar.gz -C /volume ."
@echo "Step 2: Copy the media from remote volume to local volume"
scp -P $(REMOTE_PORT) $(REMOTE_USER)@$(REMOTE_SERVER):/tmp/$(REMOTE_VOLUME_NAME).tar.gz /tmp/$(REMOTE_VOLUME_NAME).tar.gz
docker run --rm -v /tmp:/volume -v $(LOCAL_VOLUME_NAME):/backup alpine tar -xzf /volume/$(REMOTE_VOLUME_NAME).tar.gz -C /backup
@echo "Step 3: Cleanup temporary files"
rm /tmp/$(REMOTE_VOLUME_NAME).tar.gz
@echo "Media synchronization complete."

View File

@ -1,85 +1,43 @@
# Payload 3.0 Beta Demo
## Get started with nextload
### Prerequisites:
**node**
Using a version manager for installing node is recommended, see [nvm](https://github.com/nvm-sh/nvm) or [fnm](https://github.com/Schniz/fnm)
This repo showcases a demo of the Payload 3.0 Beta running completely within Next.js.
**pnpm**
`corepack enable pnpm`
> [!IMPORTANT]
> It's extremely important to note that as of now, this demo contains BETA software and you are 100% guaranteed to run into bugs / weird stuff.
>
> We're actively working toward a stable release as fast as we possibly can.
### Highlights
1. Payload is now Next.js-native
1. Turbopack works out of the box (this will get faster over time, expect more here)
1. The Payload admin UI is built with React Server Components and automatically eliminates server-side code from your admin bundle, completely alleviating the need to use Webpack aliases to remove hooks, access control, etc.
1. Payload is now fully-ESM across the board
1. GraphQL is now initialized only when you hit the GraphQL endpoint, and does not affect overhead of REST API routes
1. All UI components have been abstracted into a separate `@payloadcms/ui` package, which will be fully documented and exposed for your re-use once we hit stable 3.0 or before
1. You can run your own Next.js site alongside of Payload in the same app
1. You can now deploy Payload to Vercel and Netlify, and there will be official support for Vercel Blob Storage and Netlify Blobs coming soon (so no S3 needed for files)
1. Server-side HMR works out of the box, with no need for `nodemon` or similar. When the Payload config changes, your app will automatically re-initialize Payload seamlessly in the background
1. All custom React components can be server components by default, and you can decide if you want them to be server components or client components
1. Sharp has been abstracted to be an optional dependency
1. Payload now relies on the Web Request / Response APIs rather than the Node Request / Response
1. Express can still be used with Next.js' Custom Server functionality
1. Payload itself has slimmed down significantly and can now be fully portable, run anywhere. You can leverage the Payload Local API completely outside of Next.js if you want.
1. The data layer, including the shape of the database Payload used and the API responses in 2.0, has not been affected whatsoever
### Work to come
We are making this available to our community so that we can gather your feedback and test the new approach that Payload is taking. Don't expect it to be fully functional yet. There are some things that we are aware of that are not yet completed, but we're going to keep blazing through the remaining items as fast as we can to reach stable 3.0 as quickly and efficiently as possible. Here are a few of the items that we are still working on (not a full list):
1. Documentation
1. Vercel Blob Storage and Netlify Blobs adapter
1. Lots of bugs for sure
1. 100% of tests passing
1. Compiler speed improvements (turbo is beta still, it is slower than it should be. it will get faster)
1. Overall speed improvements
1. An install script to be able to install Payload easily into any existing Next.js app
1. A full list of breaking changes for 2.0 -> 3.0, including an in-depth migration guide
### Existing Next.js project
You can install Payload into your existing Next.js project using this command:
**docker**
[get docker script](https://get.docker.com/):
```
npx create-payload-app@beta
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
```
Contents from `src/app` will have to be moved into a new directory `src/app/(app)` so that Payload's root layout and routes can remain isolated from the rest of your app.
### Using this repo
### Start developing
1. **Copy the environment variables**
To try out this repo yourself, follow the steps below:
`cp .env.local.example .env.example`
1. Clone the repo to your computer (`git clone git@github.com:payloadcms/payload-3.0-demo.git`)
2. `cd` into the new folder by running `cd ./payload-3.0-demo`
3. Copy the `.env.local.example` by running `cp .env.local.example .env.example` in the repo, then fill out the values including the connection string to your DB
4. Install dependencies with whatever package manager you use (`pnpm o`, `npm install`, `yarn`, etc.). `pnpm` is highly recommended. The usage of yarn v1 is discouraged.
5. Start your database. For local postgresql use `.\start-database.sh` to start it in docker container.
6. Fire it up (`pnpm dev`, `npm run dev`, `yarn dev`, etc.)
7. Visit https://localhost:3000 and log in with the user created within the config's `onInit` method
2. **Install dependencies**
### Follow along with breaking changes
`pnpm install`
There is a possibility that we will make breaking changes before releasing the full stable version of Payload 3.0.
3. **Run dev container**
**To follow along with breaking changes in advance of the full, stable release,** you can keep an eye on the [CHANGELOG.md](https://github.com/payloadcms/payload-3.0-demo/blob/main/CHANGELOG.md).
`pnpm dev` or `docker compose up`
### Notes
- payload-types.ts file will be generated when starting the dev container, but can also be generated manually with `pnpm generate:types`
- Dev container will use `node_modules`, so make sure to run `pmpm install` prior to `pnpm dev`
### Technical details
Nextload is a fork of [payload 3 demo](https://github.com/payloadcms/payload-3.0-demo)
**The app folder**
You'll see that Payload requires a few files to be present in your `/app` folder. There are files for the admin UI as well as files for all route handlers. We've consolidated all admin views into a single `page.tsx` and consolidated most of the REST endpoints into a single `route.ts` file for simplicity, but also for development performance. With this pattern, you only have to compile the admin UI / REST API / GraphQL API a single time - and from there, it will be lightning-fast.
**The `next.config.js` `withPayload` function**
You'll see in the Next.js config that we have a `withPayload` function installed. This function is required for Payload to operate, and it ensures compatibility with packages that Payload needs such as `drizzle-kit`, `sharp`, `pino`, and `mongodb`.
**Using a TypeScript alias to point to your Payload config**
In the `tsconfig.json` within this repo, you'll see that we have `paths` set up to point `@payload-config` to the Payload config, which is located in the root. You can put your config wherever you want. By default, the `page.tsx` files and `route.ts` files within the `/app` folder use this alias. In the future, we might make it optional to use `paths` - and by default, we might just hard-code relative path imports to the config. We would like to hear your feedback on this part. What do you prefer? Use `paths` or just use relative imports?
---
### Find a bug?
Open an issue on this repo at `https://github.com/payloadcms/payload-3.0-demo` with as much detail as you can provide and we will tackle them as fast as we can. Let's get stable!

22
docker-compose.db.yml Normal file
View File

@ -0,0 +1,22 @@
---
services:
mongo:
image: mongo:6.0.5
restart: unless-stopped
volumes:
- mongo:/data/db
command:
- --storageEngine=wiredTiger
environment:
- "MONGO_INITDB_ROOT_USERNAME=mongo"
- "MONGO_INITDB_ROOT_PASSWORD=${MONGO_PASSWORD}"
networks:
- internal
ports:
- 27017:27017
networks:
internal:
volumes:
mongo:

View File

@ -4,24 +4,23 @@ version: "3.8"
services:
nextjs:
image: git.autonomic.zone/autonomic-cooperative/nextload:latest
restart: unless-stopped
environment:
- "NAME=${STACK_NAME}"
# Payload stuff propably not needed as the local API is used?
- "PAYLOAD_URL"
- "PAYLOAD_SECRET_FILE=/run/secrets/payload_secret"
- "BASE_URL=nextload.swarm-demo.autonomic.zone"
- "MONGODB_USER=mongo"
- "MONGODB_HOST=${STACK_NAME}_mongo"
- "MONGODB_PORT=27017"
- "MONGODB_PASSWORD_FILE=/run/secrets/mongo_password"
- "TOKEN_FILE=/run/secrets/token"
- "REPOSITORY"
- "DRONE_URL"
- "PORT=3000"
- "HOSTNAME=0.0.0.0"
secrets:
- mongo_password
- payload_secret
- token
volumes:
- payload_uploads:/prod/dist/media
- payload_uploads:/prod/.next/standalone/media
networks:
- proxy
- internal
@ -36,6 +35,8 @@ services:
- "traefik.http.routers.${STACK_NAME}-nextload.rule=Host(`${DOMAIN}`)"
- "traefik.http.routers.${STACK_NAME}-nextload.entrypoints=web-secure"
- "traefik.http.routers.${STACK_NAME}-nextload.tls.certresolver=production"
depends_on:
- mongo
mongo:
image: mongo:6.0.5
@ -61,14 +62,10 @@ secrets:
payload_secret:
external: true
name: ${STACK_NAME}_payload_secret_${SECRET_PAYLOAD_SECRET_VERSION}
token:
external: true
name: ${STACK_NAME}_token_${SECRET_TOKEN_VERSION}
mongo_password:
external: true
name: ${STACK_NAME}_mongo_password_${SECRET_MONGO_PASSWORD_VERSION}
volumes:
mongo:
html_content:
payload_uploads:

View File

@ -1,52 +1,31 @@
---
services:
astro:
container_name: ${NAME}-astro
image: git.autonomic.zone/autonomic-cooperative/astro-payload-test-astro:latest
nextjs:
image: git.autonomic.zone/autonomic-cooperative/nextload-dev:latest
restart: unless-stopped
build:
context: astro
command: ["yarn", "dev"]
environment:
DEV: 1
networks:
- front
volumes:
- ./astro:/base
- /base/node_modules/
ports:
- 3000:3000
depends_on:
- payload
payload:
image: git.autonomic.zone/autonomic-cooperative/astro-payload-test-payload-dev:latest
container_name: ${NAME}-payload
restart: unless-stopped
build:
context: payload
context: ./
target: dev
environment:
DEV: 1
NAME: ${NAME}
NODE_ENV: development
PAYLOAD_URL: "http://localhost:${PAYLOAD_PORT}"
PAYLOAD_PORT: ${PAYLOAD_PORT}
PAYLOAD_SECRET: ${PAYLOAD_SECRET}
MONGODB_URI: "mongodb://$MONGODB_USER:$MONGODB_PASSWORD@mongo:27017"
volumes:
- ./payload/src:/base/src
# - ./astro/src/types.ts:/types.ts
- payload_media:/base/src/media
ports:
- ${PAYLOAD_PORT}:${PAYLOAD_PORT}
- "NAME=${STACK_NAME}"
- "PAYLOAD_SECRET=${PAYLOAD_SECRET}"
- "MONGODB_USER=mongo"
- "MONGODB_HOST=${STACK_NAME}_mongo"
- "MONGODB_PORT=27017"
- "MONGODB_PASSWORD=${MONGO_PASSWORD}"
- "REPOSITORY"
- "PORT=3000"
- "HOSTNAME=0.0.0.0"
- "MONGODB_URI=mongodb://${MONGO_USER}:${MONGO_PASSWORD}@mongo:27017"
volumes:
- payload_uploads:/base/media
- ./:/base
networks:
- front
- back
depends_on:
- mongo
- internal
ports:
- 3000:3000
mongo:
container_name: ${NAME}-mongo
image: mongo:6.0.5
restart: unless-stopped
volumes:
@ -54,17 +33,16 @@ services:
command:
- --storageEngine=wiredTiger
environment:
MONGO_INITDB_ROOT_USERNAME: ${MONGODB_USER}
MONGO_INITDB_ROOT_PASSWORD: ${MONGODB_PASSWORD}
- "MONGO_INITDB_ROOT_USERNAME=mongo"
- "MONGO_INITDB_ROOT_PASSWORD=${MONGO_PASSWORD}"
networks:
- back
- internal
ports:
- 27017:27017
networks:
front:
back:
internal:
volumes:
volumes:
mongo:
payload_media:
payload_uploads:

View File

@ -23,10 +23,30 @@ file_env() {
unset "$fileVar"
}
urlencode() {
raw="$1"
encoded=""
length=$(echo -n "$raw" | wc -c)
i=0
while [ $i -lt $length ]; do
c=$(echo "$raw" | cut -c $((i + 1)))
case "$c" in
[a-zA-Z0-9.~_-]) encoded="$encoded$c" ;;
*) encoded="$encoded$(printf '%%%02X' "'$c")" ;;
esac
i=$((i + 1))
done
echo "$encoded"
}
file_env "TOKEN"
file_env "MONGODB_PASSWORD"
file_env "PAYLOAD_SECRET"
export MONGODB_URI="mongodb://$MONGODB_USER:$MONGODB_PASSWORD@$MONGODB_HOST:$MONGODB_PORT"
MONGODB_PASSWORD_ENCODED=$(urlencode "$MONGODB_PASSWORD")
export MONGODB_URI="mongodb://$MONGODB_USER:$MONGODB_PASSWORD_ENCODED@$MONGODB_HOST:$MONGODB_PORT"
"$@"

View File

@ -2,11 +2,25 @@ import { withPayload } from '@payloadcms/next/withPayload'
/** @type {import('next').NextConfig} */
const nextConfig = {
// Your Next.js config here
experimental: {
reactCompiler: false,
},
output: 'standalone',
env: {
PORT: process.env.PORT,
HOSTNAME: process.env.HOSTNAME,
},
// Disable minification & chunking for debugging
webpack(config, { dev }) {
config.optimization.minimize = false
config.optimization.splitChunks = {
cacheGroups: {
default: false,
},
}
config.optimization.runtimeChunk = false
return config
},
}
export default withPayload(nextConfig)

View File

@ -4,7 +4,9 @@
"private": true,
"type": "module",
"scripts": {
"dev": "cross-env NODE_OPTIONS=--no-deprecation next dev",
"dev": "docker compose up",
"dev:db": "docker compose -f docker-compose.db.yaml up",
"dev:next": "cross-env NODE_OPTIONS=--no-deprecation next dev",
"devturbo": "cross-env NODE_OPTIONS=--no-deprecation next dev --turbo",
"devsafe": "rm -rf .next && cross-env NODE_OPTIONS=--no-deprecation next dev",
"build": "cross-env NODE_OPTIONS=--no-deprecation next build",
@ -21,25 +23,36 @@
},
"dependencies": {
"@payloadcms/db-mongodb": "3.0.0-beta.40",
"@payloadcms/live-preview": "3.0.0-beta.35",
"@payloadcms/live-preview-react": "3.0.0-beta.35",
"@payloadcms/next": "3.0.0-beta.40",
"@payloadcms/plugin-nested-docs": "3.0.0-beta.35",
"@payloadcms/richtext-lexical": "3.0.0-beta.40",
"@payloadcms/ui": "3.0.0-beta.40",
"babel-plugin-react-compiler": "^0.0.0-experimental-592953e-20240517",
"clsx": "^2.1.1",
"cross-env": "^7.0.3",
"deepmerge": "^4.3.1",
"graphql": "^16.8.1",
"next": "15.0.0-rc.0",
"payload": "3.0.0-beta.40",
"react": "19.0.0-rc-f994737d14-20240522",
"react-dom": "19.0.0-rc-f994737d14-20240522",
"sharp": "0.32.6",
"babel-plugin-react-compiler": "^0.0.0-experimental-592953e-20240517"
"tailwind-merge": "^2.3.0",
"lodash": "^4.17.21"
},
"devDependencies": {
"@types/node": "^20.12.12",
"@types/react": "npm:types-react@19.0.0-beta.2",
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2",
"autoprefixer": "^10.4.19",
"eslint": "^8.57.0",
"eslint-config-next": "14.2.3",
"typescript": "^5.4.5"
"postcss": "^8.4.38",
"tailwindcss": "^3.4.4",
"typescript": "^5.4.5",
"@types/lodash": "^4.17.4"
},
"pnpm": {
"overrides": {
@ -50,5 +63,6 @@
"overrides": {
"@types/react": "npm:types-react@19.0.0-beta.2",
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2"
}
},
"packageManager": "pnpm@9.4.0+sha512.f549b8a52c9d2b8536762f99c0722205efc5af913e77835dbccc3b0b0b2ca9e7dc8022b78062c17291c48e88749c70ce88eb5a74f1fa8c4bf5e18bb46c8bd83a"
}

View File

@ -1,5 +1,4 @@
import path from 'path'
// import { postgresAdapter } from '@payloadcms/db-postgres'
import { en } from 'payload/i18n/en'
import {
AlignFeature,
@ -19,95 +18,58 @@ import {
UnorderedListFeature,
UploadFeature,
} from '@payloadcms/richtext-lexical'
//import { slateEditor } from '@payloadcms/richtext-slate'
import { mongooseAdapter } from '@payloadcms/db-mongodb'
import { buildConfig } from 'payload/config'
import sharp from 'sharp'
import { fileURLToPath } from 'url'
import Media from '@payload/collections/Media'
import Authors from '@payload/collections/Authors'
import Posts from '@payload/collections/Posts'
import Users from '@payload/collections/Users'
import Pages from '@payload/collections/Pages'
import { COLLECTION_SLUG_PAGE, COLLECTION_SLUG_POST } from '@/app/(payload)/collections/config'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
export default buildConfig({
//editor: slateEditor({}),
editor: lexicalEditor(),
collections: [
{
slug: 'users',
auth: true,
access: {
delete: () => false,
update: () => false,
},
fields: [],
},
{
slug: 'pages',
admin: {
useAsTitle: 'title',
},
fields: [
{
name: 'title',
type: 'text',
},
{
name: 'content',
type: 'richText',
},
],
},
{
slug: 'media',
upload: true,
fields: [
{
name: 'text',
type: 'text',
},
],
},
],
secret: process.env.PAYLOAD_SECRET || '',
typescript: {
outputFile: path.resolve(dirname, 'payload-types.ts'),
},
// db: postgresAdapter({
// pool: {
// connectionString: process.env.POSTGRES_URI || ''
// }
// }),
db: mongooseAdapter({
url: process.env.MONGODB_URI || '',
}),
/**
* Payload can now accept specific translations from 'payload/i18n/en'
* This is completely optional and will default to English if not provided
*/
i18n: {
supportedLanguages: { en },
},
collections: [Users, Posts, Authors, Media, Pages],
admin: {
autoLogin: {
email: 'dev@payloadcms.com',
email: 'admin@nextload.test',
password: 'test',
prefillOnly: true,
},
livePreview: {
url: ({ data, locale, collectionConfig }) =>
`${process.env.BASE_URL}/${collectionConfig?.slug || ''}/preview${data.path}${
locale ? `?locale=${locale.code}` : ''
}`,
collections: [COLLECTION_SLUG_PAGE, COLLECTION_SLUG_POST],
},
},
editor: lexicalEditor(),
secret: process.env.PAYLOAD_SECRET || '',
typescript: {
outputFile: path.resolve(dirname, 'types/payload-types.ts'),
},
db: mongooseAdapter({
url: process.env.MONGODB_URI || '',
}),
async onInit(payload) {
const existingUsers = await payload.find({
collection: 'users',
limit: 1,
limit: 2,
})
if (existingUsers.docs.length === 0) {
if (existingUsers.docs.length === 0 || existingUsers.docs.length === 1) {
await payload.create({
collection: 'users',
data: {
email: 'dev@payloadcms.com',
email: 'admin@nextload.test',
password: 'test',
roles: ['admin'],
},
})
}

View File

@ -15,24 +15,42 @@ importers:
'@payloadcms/db-mongodb':
specifier: 3.0.0-beta.40
version: 3.0.0-beta.40(@aws-sdk/client-sso-oidc@3.588.0)(payload@3.0.0-beta.40(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(graphql@16.8.1)(typescript@5.4.5))
'@payloadcms/live-preview':
specifier: 3.0.0-beta.35
version: 3.0.0-beta.35
'@payloadcms/live-preview-react':
specifier: 3.0.0-beta.35
version: 3.0.0-beta.35(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522)
'@payloadcms/next':
specifier: 3.0.0-beta.40
version: 3.0.0-beta.40(graphql@16.8.1)(monaco-editor@0.49.0)(next@15.0.0-rc.0(babel-plugin-react-compiler@0.0.0-experimental-938cd9a-20240601)(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522)(sass@1.77.4))(payload@3.0.0-beta.40(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(graphql@16.8.1)(typescript@5.4.5))(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522)(types-react@19.0.0-beta.2)(typescript@5.4.5)
'@payloadcms/plugin-nested-docs':
specifier: 3.0.0-beta.35
version: 3.0.0-beta.35(payload@3.0.0-beta.40(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(graphql@16.8.1)(typescript@5.4.5))
'@payloadcms/richtext-lexical':
specifier: 3.0.0-beta.40
version: 3.0.0-beta.40(@faceless-ui/modal@2.0.2(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522))(@faceless-ui/scroll-info@1.3.0(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522))(@lexical/headless@0.15.0)(@lexical/link@0.15.0)(@lexical/list@0.15.0)(@lexical/mark@0.15.0)(@lexical/markdown@0.15.0)(@lexical/react@0.15.0(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522)(yjs@13.6.15))(@lexical/rich-text@0.15.0)(@lexical/selection@0.15.0)(@lexical/utils@0.15.0)(@payloadcms/next@3.0.0-beta.40(graphql@16.8.1)(monaco-editor@0.49.0)(next@15.0.0-rc.0(babel-plugin-react-compiler@0.0.0-experimental-938cd9a-20240601)(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522)(sass@1.77.4))(payload@3.0.0-beta.40(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(graphql@16.8.1)(typescript@5.4.5))(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522)(types-react@19.0.0-beta.2)(typescript@5.4.5))(@payloadcms/translations@3.0.0-beta.29)(@payloadcms/ui@3.0.0-beta.40(monaco-editor@0.49.0)(next@15.0.0-rc.0(babel-plugin-react-compiler@0.0.0-experimental-938cd9a-20240601)(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522)(sass@1.77.4))(payload@3.0.0-beta.40(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(graphql@16.8.1)(typescript@5.4.5))(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522)(types-react@19.0.0-beta.2))(lexical@0.15.0)(payload@3.0.0-beta.40(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(graphql@16.8.1)(typescript@5.4.5))(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522)
version: 3.0.0-beta.40(tqwuhpzhdzlcn7wsspnixuamku)
'@payloadcms/ui':
specifier: 3.0.0-beta.40
version: 3.0.0-beta.40(monaco-editor@0.49.0)(next@15.0.0-rc.0(babel-plugin-react-compiler@0.0.0-experimental-938cd9a-20240601)(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522)(sass@1.77.4))(payload@3.0.0-beta.40(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(graphql@16.8.1)(typescript@5.4.5))(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522)(types-react@19.0.0-beta.2)
babel-plugin-react-compiler:
specifier: ^0.0.0-experimental-592953e-20240517
version: 0.0.0-experimental-938cd9a-20240601
clsx:
specifier: ^2.1.1
version: 2.1.1
cross-env:
specifier: ^7.0.3
version: 7.0.3
deepmerge:
specifier: ^4.3.1
version: 4.3.1
graphql:
specifier: ^16.8.1
version: 16.8.1
lodash:
specifier: ^4.17.21
version: 4.17.21
next:
specifier: 15.0.0-rc.0
version: 15.0.0-rc.0(babel-plugin-react-compiler@0.0.0-experimental-938cd9a-20240601)(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522)(sass@1.77.4)
@ -48,7 +66,13 @@ importers:
sharp:
specifier: 0.32.6
version: 0.32.6
tailwind-merge:
specifier: ^2.3.0
version: 2.3.0
devDependencies:
'@types/lodash':
specifier: ^4.17.4
version: 4.17.4
'@types/node':
specifier: ^20.12.12
version: 20.14.1
@ -58,18 +82,31 @@ importers:
'@types/react-dom':
specifier: npm:types-react-dom@19.0.0-beta.2
version: types-react-dom@19.0.0-beta.2
autoprefixer:
specifier: ^10.4.19
version: 10.4.19(postcss@8.4.38)
eslint:
specifier: ^8.57.0
version: 8.57.0
eslint-config-next:
specifier: 14.2.3
version: 14.2.3(eslint@8.57.0)(typescript@5.4.5)
postcss:
specifier: ^8.4.38
version: 8.4.38
tailwindcss:
specifier: ^3.4.4
version: 3.4.4
typescript:
specifier: ^5.4.5
version: 5.4.5
packages:
'@alloc/quick-lru@5.2.0':
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
engines: {node: '>=10'}
'@aws-crypto/ie11-detection@3.0.0':
resolution: {integrity: sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==}
@ -496,6 +533,24 @@ packages:
resolution: {integrity: sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==}
engines: {node: '>= 6'}
'@jridgewell/gen-mapping@0.3.5':
resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
engines: {node: '>=6.0.0'}
'@jridgewell/resolve-uri@3.1.2':
resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
engines: {node: '>=6.0.0'}
'@jridgewell/set-array@1.2.1':
resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
engines: {node: '>=6.0.0'}
'@jridgewell/sourcemap-codec@1.4.15':
resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
'@jridgewell/trace-mapping@0.3.25':
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
'@jsdevtools/ono@7.1.3':
resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==}
@ -672,6 +727,15 @@ packages:
graphql: ^16.8.1
payload: 3.0.0-beta.40
'@payloadcms/live-preview-react@3.0.0-beta.35':
resolution: {integrity: sha512-OAkuNILhSpFnHuUOThZ2c1D3DzHA3liUpgxG5VnbnMJzYThR7XE7R3jkeCEs+7V3bpy+oKyNxEZAc/jqufMhzA==}
peerDependencies:
react: ^18.2.0 || ^19.0.0
react-dom: ^18.2.0 || ^19.0.0
'@payloadcms/live-preview@3.0.0-beta.35':
resolution: {integrity: sha512-t0JdtfYYc4xU8F/3dj0iwaSoPhc/n2XXe6qsEKYtuKqFRNs00aX2fm1FSrQ8HG1onWnu7g+1VnEjsJeajnT7hw==}
'@payloadcms/next@3.0.0-beta.40':
resolution: {integrity: sha512-B/6uXGyrp0L8J6afSZ2hgB8pWe69O7PTdEDT0MG6gUAIg+EVxn54kFqA8YsR5l68Fa8q5xOPrliBfd58OAOYng==}
engines: {node: '>=18.20.2'}
@ -680,6 +744,11 @@ packages:
next: ^15.0.0-rc.0
payload: 3.0.0-beta.40
'@payloadcms/plugin-nested-docs@3.0.0-beta.35':
resolution: {integrity: sha512-rrtGmvjD38jX74Z3+WhwW+PCDYClp7/RDbuimjqmfjSvNJnDagwnp1WMjkEF0w37S1Wbi+DmFmLA6GtBVfj8Ng==}
peerDependencies:
payload: 3.0.0-beta.35
'@payloadcms/richtext-lexical@3.0.0-beta.40':
resolution: {integrity: sha512-erSfkn+GO8A+7yFOKbHrF3i1i2awXkGfurJsuJCetBm3M9Vb5++ZfgsbWpR1c3USSvt6+fLonGWwpYF4wD/XsQ==}
engines: {node: '>=18.20.2'}
@ -703,9 +772,6 @@ packages:
react: ^19.0.0 || ^19.0.0-rc-f994737d14-20240522
react-dom: ^19.0.0 || ^19.0.0-rc-f994737d14-20240522
'@payloadcms/translations@3.0.0-beta.29':
resolution: {integrity: sha512-mkjniP89wZiYKK6RbVavOlpu8kqKGG8VGGJC8KgEaYoLGHofIW8aDQwHuqjMBFfqhP6I5HFCeqZnZLKCRjuEvQ==}
'@payloadcms/translations@3.0.0-beta.40':
resolution: {integrity: sha512-Y6I2yZypxpa1gRDO5YQCnvf9MFRMy1ybWNQam0I3F/JDd46bJwNT0pDD521mE2eqdM7+Nw8PwU5c55hng4kdYw==}
@ -1121,6 +1187,9 @@ packages:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
engines: {node: '>= 8'}
arg@5.0.2:
resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
@ -1177,6 +1246,13 @@ packages:
resolution: {integrity: sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==}
engines: {node: '>=10.12.0'}
autoprefixer@10.4.19:
resolution: {integrity: sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==}
engines: {node: ^10 || ^12 || >=14}
hasBin: true
peerDependencies:
postcss: ^8.1.0
available-typed-arrays@1.0.7:
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
engines: {node: '>= 0.4'}
@ -1245,6 +1321,11 @@ packages:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
engines: {node: '>=8'}
browserslist@4.23.1:
resolution: {integrity: sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
bson-objectid@2.0.4:
resolution: {integrity: sha512-vgnKAUzcDoa+AeyYwXCoHyF2q6u/8H46dxu5JN+4/TZeq/Dlinn0K6GvxsCLb3LHUJl0m/TLiEK31kUwtgocMQ==}
@ -1279,9 +1360,16 @@ packages:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
engines: {node: '>=6'}
camelcase-css@2.0.1:
resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
engines: {node: '>= 6'}
caniuse-lite@1.0.30001627:
resolution: {integrity: sha512-4zgNiB8nTyV/tHhwZrFs88ryjls/lHiqFhrxCW4qSTeuRByBVnPYpDInchOIySWknznucaf31Z4KYqjfbrecVw==}
caniuse-lite@1.0.30001636:
resolution: {integrity: sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg==}
chalk@2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
engines: {node: '>=4'}
@ -1344,6 +1432,10 @@ packages:
commander@2.20.3:
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
commander@4.1.1:
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
engines: {node: '>= 6'}
concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
@ -1373,6 +1465,11 @@ packages:
crypt@0.0.2:
resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==}
cssesc@3.0.0:
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
engines: {node: '>=4'}
hasBin: true
cssfilter@0.0.10:
resolution: {integrity: sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==}
@ -1466,6 +1563,9 @@ packages:
resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==}
engines: {node: '>=8'}
didyoumean@1.2.2:
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
diff@5.2.0:
resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==}
engines: {node: '>=0.3.1'}
@ -1474,6 +1574,9 @@ packages:
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
engines: {node: '>=8'}
dlv@1.1.3:
resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
doctrine@2.1.0:
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
engines: {node: '>=0.10.0'}
@ -1495,6 +1598,9 @@ packages:
ecdsa-sig-formatter@1.0.11:
resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
electron-to-chromium@1.4.810:
resolution: {integrity: sha512-Kaxhu4T7SJGpRQx99tq216gCq2nMxJo+uuT6uzz9l8TVN2stL7M06MIIXAtr9jsrLs2Glflgf2vMQRepxawOdQ==}
emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
@ -1563,6 +1669,10 @@ packages:
es6-weak-map@2.0.3:
resolution: {integrity: sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==}
escalade@3.1.2:
resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==}
engines: {node: '>=6'}
escape-string-regexp@1.0.5:
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
engines: {node: '>=0.8.0'}
@ -1778,6 +1888,9 @@ packages:
resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==}
engines: {node: '>=14'}
fraction.js@4.3.7:
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
fs-constants@1.0.0:
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
@ -2113,6 +2226,10 @@ packages:
resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==}
engines: {node: '>=14'}
jiti@1.21.6:
resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==}
hasBin: true
joi@17.13.1:
resolution: {integrity: sha512-vaBlIKCyo4FCUtCm7Eu4QZd/q02bWcxfUO6YSXAZOWF6gzcLBeba8kwotUdYJjDLW8Cz8RywsSOqiNJZW0mNvg==}
@ -2209,6 +2326,14 @@ packages:
engines: {node: '>=16'}
hasBin: true
lilconfig@2.1.0:
resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==}
engines: {node: '>=10'}
lilconfig@3.1.2:
resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==}
engines: {node: '>=14'}
lines-and-columns@1.2.4:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
@ -2384,14 +2509,25 @@ packages:
node-addon-api@6.1.0:
resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==}
node-releases@2.0.14:
resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}
normalize-range@0.1.2:
resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==}
engines: {node: '>=0.10.0'}
object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
object-hash@3.0.0:
resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
engines: {node: '>= 6'}
object-inspect@1.13.1:
resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==}
@ -2541,6 +2677,10 @@ packages:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
pify@2.3.0:
resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
engines: {node: '>=0.10.0'}
pino-abstract-transport@1.0.0:
resolution: {integrity: sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA==}
@ -2558,6 +2698,10 @@ packages:
resolution: {integrity: sha512-olUADJByk4twxccmAxb1RiGKOSvddHugCV3wkqjyv+3Sooa2KLrmXrKEWOKi0XPCLasRR5jBXxioE1jxUa4KzQ==}
hasBin: true
pirates@4.0.6:
resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==}
engines: {node: '>= 6'}
pkg-up@3.1.0:
resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==}
engines: {node: '>=8'}
@ -2570,10 +2714,51 @@ packages:
resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==}
engines: {node: '>= 0.4'}
postcss-import@15.1.0:
resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
engines: {node: '>=14.0.0'}
peerDependencies:
postcss: ^8.0.0
postcss-js@4.0.1:
resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==}
engines: {node: ^12 || ^14 || >= 16}
peerDependencies:
postcss: ^8.4.21
postcss-load-config@4.0.2:
resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==}
engines: {node: '>= 14'}
peerDependencies:
postcss: '>=8.0.9'
ts-node: '>=9.0.0'
peerDependenciesMeta:
postcss:
optional: true
ts-node:
optional: true
postcss-nested@6.0.1:
resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==}
engines: {node: '>=12.0'}
peerDependencies:
postcss: ^8.2.14
postcss-selector-parser@6.1.0:
resolution: {integrity: sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==}
engines: {node: '>=4'}
postcss-value-parser@4.2.0:
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
postcss@8.4.31:
resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==}
engines: {node: ^10 || ^12 || >=14}
postcss@8.4.38:
resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==}
engines: {node: ^10 || ^12 || >=14}
prebuild-install@7.1.2:
resolution: {integrity: sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==}
engines: {node: '>=10'}
@ -2713,6 +2898,9 @@ packages:
resolution: {integrity: sha512-SeU2v5Xy6FotVhKz0pMS2gvYP7HlkF0qgTskj3JzA1vlxcb3dQjxlm9t0ZlJqcgoyI3VFAw7bomuDMdgy1nBuw==}
engines: {node: '>=0.10.0'}
read-cache@1.0.0:
resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
readable-stream@3.6.2:
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
engines: {node: '>= 6'}
@ -2993,6 +3181,11 @@ packages:
stylis@4.2.0:
resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==}
sucrase@3.35.0:
resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==}
engines: {node: '>=16 || 14 >=14.17'}
hasBin: true
supports-color@5.5.0:
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
engines: {node: '>=4'}
@ -3011,6 +3204,14 @@ packages:
tabbable@6.2.0:
resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==}
tailwind-merge@2.3.0:
resolution: {integrity: sha512-vkYrLpIP+lgR0tQCG6AP7zZXCTLc1Lnv/CCRT3BqJ9CZ3ui2++GPaGb1x/ILsINIMSYqqvrpqjUFsMNLlW99EA==}
tailwindcss@3.4.4:
resolution: {integrity: sha512-ZoyXOdJjISB7/BcLTR6SEsLgKtDStYyYZVLsUtWChO4Ps20CBad7lfJKVDiejocV4ME1hLmyY0WJE3hSDcmQ2A==}
engines: {node: '>=14.0.0'}
hasBin: true
tapable@2.2.1:
resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
engines: {node: '>=6'}
@ -3086,6 +3287,9 @@ packages:
peerDependencies:
typescript: '>=3.7.0'
ts-interface-checker@0.1.13:
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
tsconfig-paths@3.15.0:
resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==}
@ -3146,6 +3350,12 @@ packages:
resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==}
engines: {node: '>=18'}
update-browserslist-db@1.0.16:
resolution: {integrity: sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==}
hasBin: true
peerDependencies:
browserslist: '>= 4.21.0'
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
@ -3238,6 +3448,11 @@ packages:
resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
engines: {node: '>= 6'}
yaml@2.4.5:
resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==}
engines: {node: '>= 14'}
hasBin: true
yjs@13.6.15:
resolution: {integrity: sha512-moFv4uNYhp8BFxIk3AkpoAnnjts7gwdpiG8RtyFiKbMtxKCS0zVZ5wPaaGpwC3V2N/K8TK8MwtSI3+WO9CHWjQ==}
engines: {node: '>=16.0.0', npm: '>=8.0.0'}
@ -3261,6 +3476,8 @@ packages:
snapshots:
'@alloc/quick-lru@5.2.0': {}
'@aws-crypto/ie11-detection@3.0.0':
dependencies:
tslib: 1.14.1
@ -3304,7 +3521,7 @@ snapshots:
'@aws-sdk/client-sso-oidc': 3.588.0
'@aws-sdk/client-sts': 3.588.0(@aws-sdk/client-sso-oidc@3.588.0)
'@aws-sdk/core': 3.588.0
'@aws-sdk/credential-provider-node': 3.588.0(@aws-sdk/client-sso-oidc@3.588.0)(@aws-sdk/client-sts@3.588.0)
'@aws-sdk/credential-provider-node': 3.588.0(@aws-sdk/client-sso-oidc@3.588.0)(@aws-sdk/client-sts@3.588.0(@aws-sdk/client-sso-oidc@3.588.0))
'@aws-sdk/middleware-host-header': 3.577.0
'@aws-sdk/middleware-logger': 3.577.0
'@aws-sdk/middleware-recursion-detection': 3.577.0
@ -3350,7 +3567,7 @@ snapshots:
'@aws-crypto/sha256-js': 3.0.0
'@aws-sdk/client-sts': 3.588.0(@aws-sdk/client-sso-oidc@3.588.0)
'@aws-sdk/core': 3.588.0
'@aws-sdk/credential-provider-node': 3.588.0(@aws-sdk/client-sso-oidc@3.588.0)(@aws-sdk/client-sts@3.588.0)
'@aws-sdk/credential-provider-node': 3.588.0(@aws-sdk/client-sso-oidc@3.588.0)(@aws-sdk/client-sts@3.588.0(@aws-sdk/client-sso-oidc@3.588.0))
'@aws-sdk/middleware-host-header': 3.577.0
'@aws-sdk/middleware-logger': 3.577.0
'@aws-sdk/middleware-recursion-detection': 3.577.0
@ -3440,7 +3657,7 @@ snapshots:
'@aws-crypto/sha256-js': 3.0.0
'@aws-sdk/client-sso-oidc': 3.588.0
'@aws-sdk/core': 3.588.0
'@aws-sdk/credential-provider-node': 3.588.0(@aws-sdk/client-sso-oidc@3.588.0)(@aws-sdk/client-sts@3.588.0)
'@aws-sdk/credential-provider-node': 3.588.0(@aws-sdk/client-sso-oidc@3.588.0)(@aws-sdk/client-sts@3.588.0(@aws-sdk/client-sso-oidc@3.588.0))
'@aws-sdk/middleware-host-header': 3.577.0
'@aws-sdk/middleware-logger': 3.577.0
'@aws-sdk/middleware-recursion-detection': 3.577.0
@ -3524,14 +3741,14 @@ snapshots:
tslib: 2.6.2
optional: true
'@aws-sdk/credential-provider-ini@3.588.0(@aws-sdk/client-sso-oidc@3.588.0)(@aws-sdk/client-sts@3.588.0)':
'@aws-sdk/credential-provider-ini@3.588.0(@aws-sdk/client-sso-oidc@3.588.0)(@aws-sdk/client-sts@3.588.0(@aws-sdk/client-sso-oidc@3.588.0))':
dependencies:
'@aws-sdk/client-sts': 3.588.0(@aws-sdk/client-sso-oidc@3.588.0)
'@aws-sdk/credential-provider-env': 3.587.0
'@aws-sdk/credential-provider-http': 3.587.0
'@aws-sdk/credential-provider-process': 3.587.0
'@aws-sdk/credential-provider-sso': 3.588.0(@aws-sdk/client-sso-oidc@3.588.0)
'@aws-sdk/credential-provider-web-identity': 3.587.0(@aws-sdk/client-sts@3.588.0)
'@aws-sdk/credential-provider-web-identity': 3.587.0(@aws-sdk/client-sts@3.588.0(@aws-sdk/client-sso-oidc@3.588.0))
'@aws-sdk/types': 3.577.0
'@smithy/credential-provider-imds': 3.1.0
'@smithy/property-provider': 3.1.0
@ -3543,14 +3760,14 @@ snapshots:
- aws-crt
optional: true
'@aws-sdk/credential-provider-node@3.588.0(@aws-sdk/client-sso-oidc@3.588.0)(@aws-sdk/client-sts@3.588.0)':
'@aws-sdk/credential-provider-node@3.588.0(@aws-sdk/client-sso-oidc@3.588.0)(@aws-sdk/client-sts@3.588.0(@aws-sdk/client-sso-oidc@3.588.0))':
dependencies:
'@aws-sdk/credential-provider-env': 3.587.0
'@aws-sdk/credential-provider-http': 3.587.0
'@aws-sdk/credential-provider-ini': 3.588.0(@aws-sdk/client-sso-oidc@3.588.0)(@aws-sdk/client-sts@3.588.0)
'@aws-sdk/credential-provider-ini': 3.588.0(@aws-sdk/client-sso-oidc@3.588.0)(@aws-sdk/client-sts@3.588.0(@aws-sdk/client-sso-oidc@3.588.0))
'@aws-sdk/credential-provider-process': 3.587.0
'@aws-sdk/credential-provider-sso': 3.588.0(@aws-sdk/client-sso-oidc@3.588.0)
'@aws-sdk/credential-provider-web-identity': 3.587.0(@aws-sdk/client-sts@3.588.0)
'@aws-sdk/credential-provider-web-identity': 3.587.0(@aws-sdk/client-sts@3.588.0(@aws-sdk/client-sso-oidc@3.588.0))
'@aws-sdk/types': 3.577.0
'@smithy/credential-provider-imds': 3.1.0
'@smithy/property-provider': 3.1.0
@ -3586,7 +3803,7 @@ snapshots:
- aws-crt
optional: true
'@aws-sdk/credential-provider-web-identity@3.587.0(@aws-sdk/client-sts@3.588.0)':
'@aws-sdk/credential-provider-web-identity@3.587.0(@aws-sdk/client-sts@3.588.0(@aws-sdk/client-sso-oidc@3.588.0))':
dependencies:
'@aws-sdk/client-sts': 3.588.0(@aws-sdk/client-sso-oidc@3.588.0)
'@aws-sdk/types': 3.577.0
@ -3603,11 +3820,11 @@ snapshots:
'@aws-sdk/credential-provider-cognito-identity': 3.588.0
'@aws-sdk/credential-provider-env': 3.587.0
'@aws-sdk/credential-provider-http': 3.587.0
'@aws-sdk/credential-provider-ini': 3.588.0(@aws-sdk/client-sso-oidc@3.588.0)(@aws-sdk/client-sts@3.588.0)
'@aws-sdk/credential-provider-node': 3.588.0(@aws-sdk/client-sso-oidc@3.588.0)(@aws-sdk/client-sts@3.588.0)
'@aws-sdk/credential-provider-ini': 3.588.0(@aws-sdk/client-sso-oidc@3.588.0)(@aws-sdk/client-sts@3.588.0(@aws-sdk/client-sso-oidc@3.588.0))
'@aws-sdk/credential-provider-node': 3.588.0(@aws-sdk/client-sso-oidc@3.588.0)(@aws-sdk/client-sts@3.588.0(@aws-sdk/client-sso-oidc@3.588.0))
'@aws-sdk/credential-provider-process': 3.587.0
'@aws-sdk/credential-provider-sso': 3.588.0(@aws-sdk/client-sso-oidc@3.588.0)
'@aws-sdk/credential-provider-web-identity': 3.587.0(@aws-sdk/client-sts@3.588.0)
'@aws-sdk/credential-provider-web-identity': 3.587.0(@aws-sdk/client-sts@3.588.0(@aws-sdk/client-sso-oidc@3.588.0))
'@aws-sdk/types': 3.577.0
'@smithy/credential-provider-imds': 3.1.0
'@smithy/property-provider': 3.1.0
@ -4028,6 +4245,23 @@ snapshots:
'@types/istanbul-reports': 1.1.2
'@types/yargs': 13.0.12
'@jridgewell/gen-mapping@0.3.5':
dependencies:
'@jridgewell/set-array': 1.2.1
'@jridgewell/sourcemap-codec': 1.4.15
'@jridgewell/trace-mapping': 0.3.25
'@jridgewell/resolve-uri@3.1.2': {}
'@jridgewell/set-array@1.2.1': {}
'@jridgewell/sourcemap-codec@1.4.15': {}
'@jridgewell/trace-mapping@0.3.25':
dependencies:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.4.15
'@jsdevtools/ono@7.1.3': {}
'@lexical/clipboard@0.15.0':
@ -4266,6 +4500,14 @@ snapshots:
transitivePeerDependencies:
- typescript
'@payloadcms/live-preview-react@3.0.0-beta.35(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522)':
dependencies:
'@payloadcms/live-preview': 3.0.0-beta.35
react: 19.0.0-rc-f994737d14-20240522
react-dom: 19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522)
'@payloadcms/live-preview@3.0.0-beta.35': {}
'@payloadcms/next@3.0.0-beta.40(graphql@16.8.1)(monaco-editor@0.49.0)(next@15.0.0-rc.0(babel-plugin-react-compiler@0.0.0-experimental-938cd9a-20240601)(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522)(sass@1.77.4))(payload@3.0.0-beta.40(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(graphql@16.8.1)(typescript@5.4.5))(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522)(types-react@19.0.0-beta.2)(typescript@5.4.5)':
dependencies:
'@dnd-kit/core': 6.0.8(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522)
@ -4295,8 +4537,12 @@ snapshots:
- typescript
- utf-8-validate
? '@payloadcms/richtext-lexical@3.0.0-beta.40(@faceless-ui/modal@2.0.2(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522))(@faceless-ui/scroll-info@1.3.0(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522))(@lexical/headless@0.15.0)(@lexical/link@0.15.0)(@lexical/list@0.15.0)(@lexical/mark@0.15.0)(@lexical/markdown@0.15.0)(@lexical/react@0.15.0(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522)(yjs@13.6.15))(@lexical/rich-text@0.15.0)(@lexical/selection@0.15.0)(@lexical/utils@0.15.0)(@payloadcms/next@3.0.0-beta.40(graphql@16.8.1)(monaco-editor@0.49.0)(next@15.0.0-rc.0(babel-plugin-react-compiler@0.0.0-experimental-938cd9a-20240601)(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522)(sass@1.77.4))(payload@3.0.0-beta.40(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(graphql@16.8.1)(typescript@5.4.5))(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522)(types-react@19.0.0-beta.2)(typescript@5.4.5))(@payloadcms/translations@3.0.0-beta.29)(@payloadcms/ui@3.0.0-beta.40(monaco-editor@0.49.0)(next@15.0.0-rc.0(babel-plugin-react-compiler@0.0.0-experimental-938cd9a-20240601)(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522)(sass@1.77.4))(payload@3.0.0-beta.40(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(graphql@16.8.1)(typescript@5.4.5))(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522)(types-react@19.0.0-beta.2))(lexical@0.15.0)(payload@3.0.0-beta.40(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(graphql@16.8.1)(typescript@5.4.5))(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522)'
: dependencies:
'@payloadcms/plugin-nested-docs@3.0.0-beta.35(payload@3.0.0-beta.40(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(graphql@16.8.1)(typescript@5.4.5))':
dependencies:
payload: 3.0.0-beta.40(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(graphql@16.8.1)(typescript@5.4.5)
'@payloadcms/richtext-lexical@3.0.0-beta.40(tqwuhpzhdzlcn7wsspnixuamku)':
dependencies:
'@faceless-ui/modal': 2.0.2(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522)
'@faceless-ui/scroll-info': 1.3.0(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522)
'@lexical/headless': 0.15.0
@ -4309,7 +4555,7 @@ snapshots:
'@lexical/selection': 0.15.0
'@lexical/utils': 0.15.0
'@payloadcms/next': 3.0.0-beta.40(graphql@16.8.1)(monaco-editor@0.49.0)(next@15.0.0-rc.0(babel-plugin-react-compiler@0.0.0-experimental-938cd9a-20240601)(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522)(sass@1.77.4))(payload@3.0.0-beta.40(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(graphql@16.8.1)(typescript@5.4.5))(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522)(types-react@19.0.0-beta.2)(typescript@5.4.5)
'@payloadcms/translations': 3.0.0-beta.29
'@payloadcms/translations': 3.0.0-beta.40
'@payloadcms/ui': 3.0.0-beta.40(monaco-editor@0.49.0)(next@15.0.0-rc.0(babel-plugin-react-compiler@0.0.0-experimental-938cd9a-20240601)(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522)(sass@1.77.4))(payload@3.0.0-beta.40(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(graphql@16.8.1)(typescript@5.4.5))(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522)(types-react@19.0.0-beta.2)
'@types/uuid': 9.0.8
bson-objectid: 2.0.4
@ -4324,8 +4570,6 @@ snapshots:
react-error-boundary: 4.0.13(react@19.0.0-rc-f994737d14-20240522)
uuid: 9.0.1
'@payloadcms/translations@3.0.0-beta.29': {}
'@payloadcms/translations@3.0.0-beta.40': {}
'@payloadcms/ui@3.0.0-beta.40(monaco-editor@0.49.0)(next@15.0.0-rc.0(babel-plugin-react-compiler@0.0.0-experimental-938cd9a-20240601)(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522)(sass@1.77.4))(payload@3.0.0-beta.40(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(graphql@16.8.1)(typescript@5.4.5))(react-dom@19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522))(react@19.0.0-rc-f994737d14-20240522)(types-react@19.0.0-beta.2)':
@ -4876,6 +5120,8 @@ snapshots:
normalize-path: 3.0.0
picomatch: 2.3.1
arg@5.0.2: {}
argparse@2.0.1: {}
aria-query@5.3.0:
@ -4962,6 +5208,16 @@ snapshots:
atomically@1.7.0: {}
autoprefixer@10.4.19(postcss@8.4.38):
dependencies:
browserslist: 4.23.1
caniuse-lite: 1.0.30001627
fraction.js: 4.3.7
normalize-range: 0.1.2
picocolors: 1.0.1
postcss: 8.4.38
postcss-value-parser: 4.2.0
available-typed-arrays@1.0.7:
dependencies:
possible-typed-array-names: 1.0.0
@ -5045,6 +5301,13 @@ snapshots:
dependencies:
fill-range: 7.1.1
browserslist@4.23.1:
dependencies:
caniuse-lite: 1.0.30001636
electron-to-chromium: 1.4.810
node-releases: 2.0.14
update-browserslist-db: 1.0.16(browserslist@4.23.1)
bson-objectid@2.0.4: {}
bson@4.7.2:
@ -5081,8 +5344,12 @@ snapshots:
callsites@3.1.0: {}
camelcase-css@2.0.1: {}
caniuse-lite@1.0.30001627: {}
caniuse-lite@1.0.30001636: {}
chalk@2.4.2:
dependencies:
ansi-styles: 3.2.1
@ -5152,6 +5419,8 @@ snapshots:
commander@2.20.3: {}
commander@4.1.1: {}
concat-map@0.0.1: {}
conf@10.2.0:
@ -5193,6 +5462,8 @@ snapshots:
crypt@0.0.2: {}
cssesc@3.0.0: {}
cssfilter@0.0.10: {}
csstype@3.1.3: {}
@ -5308,12 +5579,16 @@ snapshots:
detect-libc@2.0.3: {}
didyoumean@1.2.2: {}
diff@5.2.0: {}
dir-glob@3.0.1:
dependencies:
path-type: 4.0.0
dlv@1.1.3: {}
doctrine@2.1.0:
dependencies:
esutils: 2.0.3
@ -5337,6 +5612,8 @@ snapshots:
dependencies:
safe-buffer: 5.2.1
electron-to-chromium@1.4.810: {}
emoji-regex@8.0.0: {}
emoji-regex@9.2.2: {}
@ -5485,6 +5762,8 @@ snapshots:
es6-iterator: 2.0.3
es6-symbol: 3.1.4
escalade@3.1.2: {}
escape-string-regexp@1.0.5: {}
escape-string-regexp@4.0.0: {}
@ -5496,8 +5775,8 @@ snapshots:
'@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.4.5)
eslint: 8.57.0
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0)
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0)
eslint-plugin-jsx-a11y: 6.8.0(eslint@8.57.0)
eslint-plugin-react: 7.34.2(eslint@8.57.0)
eslint-plugin-react-hooks: 4.6.2(eslint@8.57.0)
@ -5515,13 +5794,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0):
eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0):
dependencies:
debug: 4.3.5
enhanced-resolve: 5.16.1
eslint: 8.57.0
eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0)
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0)
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0)
fast-glob: 3.3.2
get-tsconfig: 4.7.5
is-core-module: 2.13.1
@ -5532,18 +5811,18 @@ snapshots:
- eslint-import-resolver-webpack
- supports-color
eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0):
eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0):
dependencies:
debug: 3.2.7
optionalDependencies:
'@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.4.5)
eslint: 8.57.0
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0)
transitivePeerDependencies:
- supports-color
eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0):
eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0):
dependencies:
array-includes: 3.1.8
array.prototype.findlastindex: 1.2.5
@ -5553,7 +5832,7 @@ snapshots:
doctrine: 2.1.0
eslint: 8.57.0
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0)
eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0)
hasown: 2.0.2
is-core-module: 2.13.1
is-glob: 4.0.3
@ -5799,6 +6078,8 @@ snapshots:
cross-spawn: 7.0.3
signal-exit: 4.1.0
fraction.js@4.3.7: {}
fs-constants@1.0.0: {}
fs.realpath@1.0.0: {}
@ -6125,6 +6406,8 @@ snapshots:
optionalDependencies:
'@pkgjs/parseargs': 0.11.0
jiti@1.21.6: {}
joi@17.13.1:
dependencies:
'@hapi/hoek': 9.3.0
@ -6230,6 +6513,10 @@ snapshots:
dependencies:
isomorphic.js: 0.2.5
lilconfig@2.1.0: {}
lilconfig@3.1.2: {}
lines-and-columns@1.2.4: {}
locate-path@3.0.0:
@ -6415,10 +6702,16 @@ snapshots:
node-addon-api@6.1.0: {}
node-releases@2.0.14: {}
normalize-path@3.0.0: {}
normalize-range@0.1.2: {}
object-assign@4.1.1: {}
object-hash@3.0.0: {}
object-inspect@1.13.1: {}
object-is@1.1.6:
@ -6591,6 +6884,8 @@ snapshots:
picomatch@2.3.1: {}
pify@2.3.0: {}
pino-abstract-transport@1.0.0:
dependencies:
readable-stream: 4.5.2
@ -6634,6 +6929,8 @@ snapshots:
sonic-boom: 3.8.1
thread-stream: 2.7.0
pirates@4.0.6: {}
pkg-up@3.1.0:
dependencies:
find-up: 3.0.0
@ -6642,12 +6939,49 @@ snapshots:
possible-typed-array-names@1.0.0: {}
postcss-import@15.1.0(postcss@8.4.38):
dependencies:
postcss: 8.4.38
postcss-value-parser: 4.2.0
read-cache: 1.0.0
resolve: 1.22.8
postcss-js@4.0.1(postcss@8.4.38):
dependencies:
camelcase-css: 2.0.1
postcss: 8.4.38
postcss-load-config@4.0.2(postcss@8.4.38):
dependencies:
lilconfig: 3.1.2
yaml: 2.4.5
optionalDependencies:
postcss: 8.4.38
postcss-nested@6.0.1(postcss@8.4.38):
dependencies:
postcss: 8.4.38
postcss-selector-parser: 6.1.0
postcss-selector-parser@6.1.0:
dependencies:
cssesc: 3.0.0
util-deprecate: 1.0.2
postcss-value-parser@4.2.0: {}
postcss@8.4.31:
dependencies:
nanoid: 3.3.7
picocolors: 1.0.1
source-map-js: 1.2.0
postcss@8.4.38:
dependencies:
nanoid: 3.3.7
picocolors: 1.0.1
source-map-js: 1.2.0
prebuild-install@7.1.2:
dependencies:
detect-libc: 2.0.3
@ -6809,6 +7143,10 @@ snapshots:
react@19.0.0-rc-f994737d14-20240522: {}
read-cache@1.0.0:
dependencies:
pify: 2.3.0
readable-stream@3.6.2:
dependencies:
inherits: 2.0.4
@ -7137,6 +7475,16 @@ snapshots:
stylis@4.2.0: {}
sucrase@3.35.0:
dependencies:
'@jridgewell/gen-mapping': 0.3.5
commander: 4.1.1
glob: 10.3.10
lines-and-columns: 1.2.4
mz: 2.7.0
pirates: 4.0.6
ts-interface-checker: 0.1.13
supports-color@5.5.0:
dependencies:
has-flag: 3.0.0
@ -7151,6 +7499,37 @@ snapshots:
tabbable@6.2.0: {}
tailwind-merge@2.3.0:
dependencies:
'@babel/runtime': 7.24.6
tailwindcss@3.4.4:
dependencies:
'@alloc/quick-lru': 5.2.0
arg: 5.0.2
chokidar: 3.6.0
didyoumean: 1.2.2
dlv: 1.1.3
fast-glob: 3.3.2
glob-parent: 6.0.2
is-glob: 4.0.3
jiti: 1.21.6
lilconfig: 2.1.0
micromatch: 4.0.7
normalize-path: 3.0.0
object-hash: 3.0.0
picocolors: 1.0.1
postcss: 8.4.38
postcss-import: 15.1.0(postcss@8.4.38)
postcss-js: 4.0.1(postcss@8.4.38)
postcss-load-config: 4.0.2(postcss@8.4.38)
postcss-nested: 6.0.1(postcss@8.4.38)
postcss-selector-parser: 6.1.0
resolve: 1.22.8
sucrase: 3.35.0
transitivePeerDependencies:
- ts-node
tapable@2.2.1: {}
tar-fs@2.1.1:
@ -7239,6 +7618,8 @@ snapshots:
dependencies:
typescript: 5.4.5
ts-interface-checker@0.1.13: {}
tsconfig-paths@3.15.0:
dependencies:
'@types/json5': 0.0.29
@ -7316,6 +7697,12 @@ snapshots:
unicorn-magic@0.1.0: {}
update-browserslist-db@1.0.16(browserslist@4.23.1):
dependencies:
browserslist: 4.23.1
escalade: 3.1.2
picocolors: 1.0.1
uri-js@4.4.1:
dependencies:
punycode: 2.3.1
@ -7411,6 +7798,8 @@ snapshots:
yaml@1.10.2: {}
yaml@2.4.5: {}
yjs@13.6.15:
dependencies:
lib0: 0.2.94

6
postcss.config.js Normal file
View File

@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

115
src/app/(app)/global.css Normal file
View File

@ -0,0 +1,115 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
.light {
--black: 10, 16, 19;
--white: 243, 252, 248;
--absolute-black: 0, 0, 0;
--absolute-white: 255, 255, 255;
/* Use these in tailwind.config.cjs */
--primary: var(--black);
--secondary: var(--black);
--tertiary: var(--black);
--text: var(--black);
--textInverted: var(--absolute-white);
--link: var(--black);
--background: var(--white);
}
.dark {
--black: 10, 16, 19;
--white: 243, 252, 248;
--absolute-black: 0, 0, 0;
--absolute-white: 255, 255, 255;
/* Use these in tailwind.config.cjs */
--primary: var(--white);
--secondary: var(--white);
--tertiary: var(--white);
--text: var(--white);
--textInverted: var(--absolute-black);
--link: var(--white);
--background: var(--black);
}
.autonomic {
/* Palette */
/* RGBA instead of hex so that opacity will work */
--absolute-white: 255, 255, 255;
--white: 243, 252, 248;
--shocking-pink: 253, 0, 159;
--shamrock-green: 0, 198, 88;
--bubblegum-pink: 255, 133, 203;
--bright-orange: 255, 96, 21;
/* Use these in tailwind.config.cjs */
--primary: var(--shocking-pink);
--secondary: var(--shamrock-green);
--tertiary: var(--bright-orange);
--text: var(--shocking-pink);
--textInverted: var(--absolute-white);
--link: var(--shamrock-green);
--background: var(--white);
}
@layer components {
:root {
@apply ring-secondary;
}
h1, h2, h3, h4, h5, h6 {
@apply mb-2;
}
h1 {
@apply text-5xl font-bold;
}
h2 {
@apply text-4xl font-semibold;
}
h3 {
@apply text-2xl font-semibold;
}
h4, h5, h6 {
@apply text-2xl font-medium;
}
p, span, li, a {
@apply text-xl;
}
a {
@apply text-link underline;
}
ol {
@apply list-decimal;
}
ul {
@apply list-disc;
}
select {
@apply bg-background border-primary border px-2 h-8 text-lg;
}
input {
@apply bg-background border-primary border px-2 h-8 text-lg;
}
blockquote {
@apply border-l-8 border-primary text-primary pl-4 ml-2 font-serif text-2xl;
}
}

View File

@ -1,191 +0,0 @@
* {
box-sizing: border-box;
}
html {
width: 100%;
height: 100%;
background-color: #000000;
}
body {
color: #ffffff;
margin: 0;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
padding: 0 20px;
}
main {
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
width: 100%;
min-height: 100vh;
max-width: 800px;
margin: 0 auto;
padding-block: 80px;
border-inline-width: 1px;
border-inline-style: solid;
border-image: linear-gradient(180deg, #ffffff00, #ffffff00, #ffffff1a, #ffffff00) 1;
@media screen and (max-width: 600px) {
padding-block: 20px;
}
}
article {
position: relative;
display: flex;
flex-direction: column;
gap: 20px;
padding: 60px 80px;
@media screen and (max-width: 600px) {
padding: 40px 40px;
}
}
.badge {
display: flex;
align-items: center;
gap: 10px;
color: #fff;
font-size: 14px;
font-style: normal;
font-weight: 300;
text-transform: uppercase;
letter-spacing: 2.6px;
}
h1 {
color: #ffffff;
font-size: 4rem;
font-weight: 600;
line-height: normal;
letter-spacing: -0.02rem;
margin: 0;
@media screen and (max-width: 600px) {
font-size: 2rem;
}
}
p {
color: #ffffff;
font-size: 16px;
font-weight: 300;
line-height: 28px;
margin: 0;
}
a {
color: #ffffff;
text-decoration: underline;
transition: color 0.2s ease-out;
&:hover {
color: #ffffff80;
}
}
.codeBlock {
position: relative;
background-color: #00000066;
margin: 0;
padding: 0;
pre {
margin: 0;
padding: 60px 80px;
overflow-x: auto;
@media screen and (max-width: 600px) {
padding: 40px 40px;
}
&::before {
content: '';
display: block;
position: absolute;
top: 0;
left: calc(50% - 50vw);
width: 100vw;
height: 1px;
background: linear-gradient(90deg, #ffffff00, #ffffff1a, #ffffff1a, #ffffff00);
z-index: 1;
}
&::after {
content: url('/crosshair.svg');
display: block;
height: 19px;
width: 19px;
position: absolute;
top: -9px;
left: -10px;
}
}
code {
font-size: 14px;
line-height: 2;
}
&::before {
content: '';
display: block;
position: absolute;
bottom: 0;
left: calc(50% - 50vw);
width: 100vw;
height: 1px;
background: linear-gradient(90deg, #ffffff00, #ffffff1a, #ffffff1a, #ffffff00);
z-index: 1;
}
&::after {
content: url('/crosshair.svg');
display: block;
height: 19px;
width: 19px;
position: absolute;
bottom: -9px;
right: -10px;
}
}
.background {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
div.blur {
display: block;
position: absolute;
width: 100%;
height: 100%;
background: url('/blur.png');
background-repeat: repeat;
background-size: 400px 400px;
background-blend-mode: soft-light, normal;
backdrop-filter: blur(60px);
}
div.gradient {
display: block;
position: absolute;
width: 100%;
height: 100%;
background: url('/gradient.webp');
background-size: cover;
background-position: center;
z-index: -2;
}
}

View File

@ -1,17 +1,17 @@
import React from 'react'
import './globals.scss'
import { Inter } from 'next/font/google'
import './global.css'
const inter = Inter({
subsets: ['latin'],
display: 'swap',
})
import Header from '@/components/Header'
import Footer from '@/components/Footer'
/* Our app sits here to not cause any conflicts with payload's root layout */
const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
return (
<html className={inter.className}>
<body>{children}</body>
<html>
<body className="autonomic min-h-screen flex flex-col mx-auto max-w-7xl px-6 text-primary bg-background">
<Header />
<main className="flex flex-col gap-4 flex-grow">{children}</main>
<Footer />
</body>
</html>
)
}

View File

@ -1,50 +1,44 @@
import { Badge } from '@/components/Badge'
import { Background } from '@/components/Background'
import Link from 'next/link'
import React from 'react'
import Header from '@/components/Header'
import Footer from '@/components/Footer'
import { getPayloadHMR } from '@payloadcms/next/utilities'
import configPromise from '@payload-config'
import PostList from '@/components/Blocks/PostList'
import { PaginatedDocs } from 'payload/database'
import { Post } from 'types/payload-types'
import { COLLECTION_SLUG_POST } from '../(payload)/collections/config'
interface Props {}
export const dynamic = 'force-dynamic'
async function getPosts() {
const payload = await getPayloadHMR({
config: configPromise,
})
const posts: PaginatedDocs<Post> = await payload.find({
collection: COLLECTION_SLUG_POST,
})
return posts
}
const Page = async (props: Props) => {
const posts = await getPosts()
const Page = () => {
return (
<>
<main>
<article>
<Badge />
<h1>Payload 3.0</h1>
<p>
This BETA is rapidly evolving, you can report any bugs against{' '}
<Link href="https://github.com/payloadcms/payload-3.0-demo/issues" target="_blank">
the repo
</Link>{' '}
or in the{' '}
<Link
href="https://discord.com/channels/967097582721572934/1215659716538273832"
target="_blank"
>
dedicated channel in Discord
</Link>
. Payload is running at <Link href="/admin">/admin</Link>. An example of a custom route
running the Local API can be found at <Link href="/my-route">/my-route</Link>.
</p>
<p>You can use the Local API in your server components like this:</p>
</article>
<div className="codeBlock">
<pre>
<code>
{`import { getPayloadHMR } from '@payloadcms/next/utilities'
import configPromise from '@payload-config'
const payload = await getPayloadHMR({ config: configPromise })
const data = await payload.find({
collection: 'posts',
})
return <Posts data={data} />
`}
</code>
</pre>
</div>
</main>
<Background />
<h1 className="">Nextload</h1>
<p className="mt-3">
{`Nextload is a pre-configured setup for Nextjs and PayloadCMS that makes it easy to get started with building your website. With Nextload, you'll have a complete development environment that you can run locally using Docker. This makes it easy to test and develop your website before deploying it to a production environment.`}
<br />
{`When you're ready to deploy the website on your own server, Nextload comes with a production environment that requires the use of Traefik as a reverse proxy. This setup provides a secure and scalable production environment for your website.`}
</p>
<section className="mt-4">
<PostList posts={posts.docs} />
</section>
</>
)
}

View File

@ -0,0 +1,35 @@
import Blocks from '@/components/Blocks'
import { COLLECTION_SLUG_PAGE } from '@payload/collections/config'
import { getDocument } from '@/utils/getDocument'
import { generateMeta } from '@/utils/generateMeta'
import { Metadata } from 'next'
import { notFound } from 'next/navigation'
type PageArgs = {
params: {
path: string[]
}
}
export async function generateMetadata({ params }: PageArgs): Promise<Metadata> {
const page = await getDocument({
collection: COLLECTION_SLUG_PAGE,
path: params.path,
depth: 3,
})
if (!page) notFound()
return generateMeta(params?.path)
}
const Page = async ({ params }: PageArgs) => {
const page = await getDocument({
collection: COLLECTION_SLUG_PAGE,
path: params.path,
depth: 3,
})
if (!page) notFound()
return <Blocks blocks={page?.blocks} locale="" />
}
export default Page

View File

@ -0,0 +1,23 @@
import PreviewBlocks from '@/components/PreviewBlocks'
import { getCurrentUser } from '@/lib/payload'
import { COLLECTION_SLUG_PAGE } from '@payload/collections/config'
import { getDocument } from '@/utils/getDocument'
import { unstable_noStore as noStore } from 'next/cache'
import { notFound } from 'next/navigation'
const PreviewCatchAllPage = async ({ params }: { params: { path: string[] } }) => {
noStore()
const [page] = await Promise.all([
getDocument({
collection: COLLECTION_SLUG_PAGE,
path: params.path,
depth: 3,
cache: false,
}),
])
if (!page) notFound()
return <PreviewBlocks initialData={page} locale="" url={process.env.BASE_URL || ''} />
}
export default PreviewCatchAllPage

View File

@ -0,0 +1,36 @@
import Blocks from '@/components/Blocks'
import { COLLECTION_SLUG_POST } from '@payload/collections/config'
import { getDocument } from '@/utils/getDocument'
import { generateMeta } from '@/utils/generateMeta'
import { Metadata } from 'next'
import { notFound } from 'next/navigation'
import PostPage from '@/components/PostPage'
type PageArgs = {
params: {
path: string[]
}
}
export async function generateMetadata({ params }: PageArgs): Promise<Metadata> {
const page = await getDocument({
collection: COLLECTION_SLUG_POST,
path: params.path,
depth: 3,
})
if (!page) notFound()
return generateMeta(params?.path)
}
const Page = async ({ params }: PageArgs) => {
const post = await getDocument({
collection: COLLECTION_SLUG_POST,
path: params.path,
depth: 3,
})
if (!post) notFound()
return <PostPage post={post} locale="" />
}
export default Page

View File

@ -0,0 +1,24 @@
import PreviewBlocks from '@/components/PreviewBlocks'
import { getCurrentUser } from '@/lib/payload'
import { COLLECTION_SLUG_POST } from '@payload/collections/config'
import { getDocument } from '@/utils/getDocument'
import { unstable_noStore as noStore } from 'next/cache'
import { notFound } from 'next/navigation'
import PreviewPostPage from '@/components/PreviewPostPage'
const PreviewCatchAllPage = async ({ params }: { params: { path: string[] } }) => {
noStore()
const [page] = await Promise.all([
getDocument({
collection: COLLECTION_SLUG_POST,
path: params.path,
depth: 3,
cache: false,
}),
])
if (!page) notFound()
return <PreviewPostPage initialData={page} locale="" url={process.env.BASE_URL || ''} />
}
export default PreviewCatchAllPage

View File

@ -0,0 +1,65 @@
import { Access } from 'payload/types'
import type { User } from 'types/payload-types'
export const isAdmin = ({ req: { user } }: any) => {
if (!user || !user.roles) {
return false
}
if (user.roles?.includes('admin')) {
return true
}
return false
}
export const isAdminOrCreatedBy = ({ req: { user } }: any) => {
if (!user || !user.roles) {
return false
}
if (user.roles?.includes('admin')) {
return true
}
if (user) {
return {
createdBy: {
equals: user.id,
},
}
}
return false
}
export const isAdminOrSelf = ({ req: { user } }: any) => {
if (!user || !user.roles) {
return false
}
if (user.roles?.includes('admin')) {
return true
}
// Non-admin: can only access themselves
return {
id: {
equals: user.id,
},
}
}
export const isAdminOrPublished = ({ req: { user } }: any) => {
if (user && user.roles) {
if (user.roles?.includes('admin')) {
return true
}
}
return {
_status: {
equals: 'published',
},
}
}

View File

@ -0,0 +1,14 @@
import { Access, FieldAccess } from 'payload/types'
import type { User } from 'types/payload-types'
export const isEditor = ({ req: { user } }: any) => {
if (!user || !user.roles) {
return false
}
if (user?.roles?.some((role: string) => ['editor', 'admin'].includes(role))) {
return true
}
return false
}

View File

@ -0,0 +1,14 @@
import { Access, FieldAccess } from 'payload/types'
import type { User } from 'types/payload-types'
export const isUser = ({ req: { user } }: any) => {
if (!user || !user.roles) {
return false
}
if (user?.roles?.some((role: string) => ['user', 'editor', 'admin'].includes(role))) {
return true
}
return false
}

View File

@ -0,0 +1,14 @@
import type { Block } from 'payload/types'
const Author: Block = {
slug: 'Author',
fields: [
{
name: 'author',
type: 'relationship',
relationTo: 'authors',
},
],
}
export default Author

View File

@ -0,0 +1,5 @@
import RichText from '@payload/blocks/rich-text'
import Author from '@payload/blocks/author'
// eslint-disable-next-line import/no-anonymous-default-export
export default [RichText, Author]

View File

@ -0,0 +1,14 @@
import type { Block } from 'payload/types'
const RichText: Block = {
slug: 'RichText',
interfaceName: 'RichTextBlock',
fields: [
{
name: 'content',
type: 'richText'
}
]
}
export default RichText

View File

@ -0,0 +1,48 @@
import { CollectionConfig } from 'payload/types'
import { isEditor } from '@payload/access/isEditor'
import { isUser } from '@payload/access/isUser'
import { COLLECTION_SLUG_AUTHOR } from './config'
const Authors: CollectionConfig = {
slug: COLLECTION_SLUG_AUTHOR,
admin: {
defaultColumns: ['name'],
useAsTitle: 'name',
},
access: {
//TODO: Author can CRUD own post
create: isUser,
read: () => true,
update: isEditor,
delete: isEditor,
},
fields: [
{
name: 'avatar',
type: 'upload',
relationTo: 'media',
required: true,
},
{
name: 'name',
type: 'text',
required: true,
},
{
name: 'bio',
type: 'text',
required: false,
},
{
name: 'user',
type: 'upload',
relationTo: 'users',
admin: {
description: 'The selected user will be able to edit this author',
},
},
],
}
export default Authors

View File

@ -0,0 +1,24 @@
import { isUser } from '@payload/access/isUser'
import { CollectionConfig } from 'payload/types'
import { COLLECTION_SLUG_MEDIA } from './config'
export const Media: CollectionConfig = {
slug: COLLECTION_SLUG_MEDIA,
admin: {},
access: {
read: (): boolean => true,
create: isUser,
update: isUser,
delete: isUser,
},
upload: true,
fields: [
{
name: 'alt',
type: 'text',
required: true,
},
],
}
export default Media

View File

@ -0,0 +1,62 @@
import { CollectionConfig } from 'payload/types'
import { slugField, pathField } from '@payload/fields/'
import { blocksField } from '@payload/fields/blocks'
import { COLLECTION_SLUG_PAGE } from './config'
import { createBreadcrumbsField } from '@payloadcms/plugin-nested-docs'
import { revalidateTag } from 'next/cache'
import { generateDocumentCacheKey } from '@/utils/getDocument'
import { isAdmin, isAdminOrPublished } from '@payload/access/isAdmin'
const Pages: CollectionConfig = {
slug: COLLECTION_SLUG_PAGE,
admin: {
useAsTitle: 'title',
defaultColumns: ['title', 'path', 'updatedAt', 'createdAt'],
},
versions: {
drafts: {
autosave: false,
},
maxPerDoc: 10,
},
access: {
read: isAdminOrPublished,
create: isAdmin,
update: isAdmin,
delete: isAdmin,
},
hooks: {
afterChange: [
async ({ doc, collection }) => {
revalidateTag(generateDocumentCacheKey(collection.slug, doc.path))
},
],
},
fields: [
{
type: 'tabs',
tabs: [
{
label: 'Content',
fields: [
{
type: 'text',
name: 'title',
},
blocksField(),
],
},
],
},
slugField(),
pathField(),
createBreadcrumbsField(COLLECTION_SLUG_PAGE, {
name: 'breadcrumbs',
admin: {
disabled: true,
},
}),
],
}
export default Pages

View File

@ -0,0 +1,84 @@
import { CollectionConfig } from 'payload/types'
import { isEditor } from '@payload/access/isEditor'
import { isUser } from '@payload/access/isUser'
import { revalidateTag } from 'next/cache'
import { generateDocumentCacheKey } from '@/utils/getDocument'
import { slugField, pathField } from '@payload/fields/'
import { blocksField } from '@payload/fields/blocks'
import { COLLECTION_SLUG_POST } from './config'
const Posts: CollectionConfig = {
slug: COLLECTION_SLUG_POST,
versions: {
drafts: {
autosave: false,
},
maxPerDoc: 10,
},
admin: {
defaultColumns: ['title', 'author', 'status'],
useAsTitle: 'title',
},
access: {
//TODO: Author can CRUD own post
create: isUser,
read: () => true,
update: isEditor,
delete: isEditor,
},
hooks: {
afterChange: [
async ({ doc, collection }) => {
revalidateTag(generateDocumentCacheKey(collection.slug, doc.path))
},
],
},
fields: [
{
name: 'title',
type: 'text',
required: true,
},
{
name: 'summary',
type: 'text',
required: false,
},
{
name: 'publishedDate',
type: 'date',
required: false,
admin: {
position: 'sidebar',
},
},
{
name: 'thumbnail',
type: 'upload',
relationTo: 'media',
required: true,
},
{
type: 'tabs',
tabs: [
{
label: 'Content',
fields: [blocksField()],
},
],
},
//TODO: Add author as block?
{
name: 'author',
type: 'relationship',
relationTo: 'authors',
admin: {
position: 'sidebar',
},
},
slugField(),
pathField(),
],
}
export default Posts

View File

@ -0,0 +1,41 @@
import { CollectionConfig } from 'payload/types'
import { isAdmin, isAdminOrSelf } from '@payload/access/isAdmin'
import { COLLECTION_SLUG_USER } from './config'
const Users: CollectionConfig = {
slug: COLLECTION_SLUG_USER,
auth: true,
admin: {
defaultColumns: ['roles', 'email'],
useAsTitle: 'email',
},
access: {
create: isAdmin,
read: isAdminOrSelf,
update: isAdminOrSelf,
delete: isAdmin,
},
fields: [
{
name: 'roles',
type: 'select',
options: [
{ label: 'Admin', value: 'admin' }, //CRUD, role creation
{ label: 'Editor', value: 'editor' }, //CRUD
{ label: 'User', value: 'user' }, //cRud, CRUD own entries
],
required: true,
defaultValue: 'user',
// JWT so that role is accessible from 'req.user'
saveToJWT: true,
hasMany: true,
access: {
create: isAdmin,
read: () => true,
update: isAdmin,
},
},
],
}
export default Users

View File

@ -0,0 +1,5 @@
export const COLLECTION_SLUG_USER = 'users' as const
export const COLLECTION_SLUG_PAGE = 'pages' as const
export const COLLECTION_SLUG_POST = 'posts' as const
export const COLLECTION_SLUG_MEDIA = 'media' as const
export const COLLECTION_SLUG_AUTHOR = 'authors' as const

View File

@ -0,0 +1,16 @@
import type { Field } from 'payload/types'
import blocks from '@payload/blocks'
import deepMerge from 'deepmerge'
type BlocksField = (overrides?: Partial<Field>) => Field
export const blocksField: BlocksField = (overrides) => {
return deepMerge<Field, Partial<Field>>(
{
name: 'blocks',
type: 'blocks',
blocks,
},
overrides || {},
)
}

View File

@ -0,0 +1,2 @@
export { default as pathField } from "./path";
export { default as slugField } from "./slug";

View File

@ -0,0 +1,122 @@
import { COLLECTION_SLUG_PAGE } from '@payload/collections/config'
import generateBreadcrumbsUrl from '@/utils/generateBreadcrumbsUrl'
import { getParents } from '@payloadcms/plugin-nested-docs'
import deepmerge from 'deepmerge'
import { APIError } from 'payload/errors'
import type { Field, Payload, Where } from 'payload/types'
import type { Config } from 'types/payload-types'
import generateRandomString from '@/utils/generateRandomString'
type Collection = keyof Config['collections']
type WillPathConflictParams = {
payload: Payload
path: string
originalDoc?: { id?: string }
collection: Collection
uniquePathFieldCollections?: Collection[]
}
export const willPathConflict = async ({
payload,
path,
originalDoc,
collection,
uniquePathFieldCollections = [],
}: WillPathConflictParams): Promise<boolean> => {
if (!payload || !uniquePathFieldCollections.includes(collection)) return false
const queries = uniquePathFieldCollections.map((targetCollection) => {
const whereCondition: Where = {
path: { equals: path },
}
if (originalDoc?.id && collection === targetCollection) {
whereCondition.id = { not_equals: originalDoc.id }
}
return payload.find({
collection: targetCollection,
where: whereCondition,
limit: 1,
pagination: false,
})
})
const results = await Promise.allSettled(queries)
return results.some((result) => result.status === 'fulfilled' && result.value.docs.length > 0)
}
type GetNewPathParams = {
req: any
collection: Collection
currentDoc: any
operation?: string
}
export async function getNewPath({
req,
collection,
currentDoc,
operation,
}: GetNewPathParams): Promise<string> {
const isAutoSave = operation === 'create' && currentDoc?._status === 'draft'
if (isAutoSave || currentDoc?.slug == null || !collection)
return `/${currentDoc?.id || generateRandomString(20)}`
const newPath = currentDoc?.breadcrumbs?.at(-1)?.url
if (newPath) return newPath
const docs = await getParents(
req,
{ parentFieldSlug: 'parent' } as any,
collection as any,
currentDoc,
[currentDoc],
)
return generateBreadcrumbsUrl(docs, currentDoc)
}
const pathField = (overrides?: Partial<Field>): Field =>
deepmerge<Field, Partial<Field>>(
{
type: 'text',
name: 'path',
unique: true,
index: true,
hooks: {
beforeChange: [
async ({ collection, req, siblingData, originalDoc, operation }) => {
const currentDoc = { ...originalDoc, ...siblingData }
const newPath = await getNewPath({
req,
collection: collection?.slug as Collection,
currentDoc,
operation,
})
const isNewPathConflicting = await willPathConflict({
payload: req.payload,
path: newPath,
originalDoc,
collection: collection ? (collection.slug as Collection) : COLLECTION_SLUG_PAGE,
uniquePathFieldCollections: [COLLECTION_SLUG_PAGE], // Add more collections as needed
})
if (isNewPathConflicting) {
const error = new APIError(
'This will create a conflict with an existing path.',
400,
[{ field: 'slug', message: 'This will create a conflict with an existing path.' }],
false,
)
throw error
}
return newPath
},
],
},
admin: {
position: 'sidebar',
readOnly: true,
},
},
overrides || {},
)
export default pathField

View File

@ -0,0 +1,49 @@
import type { Field, FieldHook } from 'payload/types'
import deepMerge from 'deepmerge'
const format = (val: string): string =>
val
.replace(/ /g, '-')
.replace(/[^\w-]+/g, '')
.toLowerCase()
const formatSlug =
(fallback: string): FieldHook =>
({ operation, value, originalDoc, data }) => {
if (typeof value === 'string' && value.length > 0) {
return format(value)
}
if (operation === 'create') {
const fallbackData = (data && data[fallback]) || (originalDoc && originalDoc[fallback])
if (fallbackData && typeof fallbackData === 'string') {
return format(fallbackData)
}
}
return value
}
type Slug = (fieldToUse?: string, overrides?: Partial<Field>) => Field
const slugField: Slug = (fieldToUse = 'title', overrides = {}) => {
return deepMerge<Field, Partial<Field>>(
{
name: 'slug',
label: 'Slug',
type: 'text',
index: true,
required: false, // Need to be false so that we can use beforeValidate hook to set slug.
admin: {
position: 'sidebar',
},
hooks: {
beforeValidate: [formatSlug(fieldToUse)],
},
},
overrides,
)
}
export default slugField

View File

@ -1,14 +0,0 @@
import { getPayloadHMR } from '@payloadcms/next/utilities'
import configPromise from '@payload-config'
export const GET = async () => {
const payload = await getPayloadHMR({
config: configPromise,
})
const data = await payload.find({
collection: 'users',
})
return Response.json(data)
}

View File

@ -1,8 +0,0 @@
export const Background = () => {
return (
<div className="background">
<div className="blur" />
<div className="gradient" />
</div>
)
}

View File

@ -1,20 +0,0 @@
export const Badge = () => {
return (
<span className="badge">
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9.35899 1.59033L16.956 6.233V15.0452L11.2355 18.4097V9.59751L3.62941 4.96095L9.35899 1.59033Z"
fill="#ffffff"
/>
<path d="M8.77667 17.9211V11.0447L3.04407 14.4153L8.77667 17.9211Z" fill="#ffffff" />
</svg>
Beta
</span>
)
}

View File

@ -0,0 +1,33 @@
import React from 'react'
import Image from 'next/image'
import type { Author } from 'types/payload-types'
interface Props {
author: Author
}
const AuthorComponent: React.FC<Props> = ({ author }) => {
return (
<>
{author && (
<div className="flex gap-6 border-t-2 border-secondary py-4 text-secondary">
{typeof author.avatar === 'object' && (
<Image
src={author.avatar.url || ''}
width={100}
height={10}
alt={author.avatar.alt || ''}
layout="fixed"
/>
)}
<div className="flex flex-col">
<h3 className="">{author.name}</h3>
<p className="text-sm font-light">{author.bio}</p>
</div>
</div>
)}
</>
)
}
export default AuthorComponent

View File

@ -0,0 +1,30 @@
import { Post } from 'types/payload-types'
import PostEntry from '@/components/PostEntry'
import { getPayloadHMR } from '@payloadcms/next/utilities'
import configPromise from '@payload-config'
import { PaginatedDocs } from 'payload/database'
import { COLLECTION_SLUG_POST } from '@payload/collections/config'
import { GetServerSideProps } from 'next'
type Props = {
posts: Post[]
}
const PostList = ({ posts }: Props) => {
return (
<div className="flex flex-col">
<h2 className="text-secondary mb-4">Posts</h2>
<div className="border-y-2 flex flex-col divide-y-2 border-secondary divide-secondary">
{posts.length > 0 ? (
posts.map(
(post, index) => typeof post === 'object' && <PostEntry key={index} post={post} />,
)
) : (
<p>No posts available</p>
)}
</div>
</div>
)
}
export default PostList

View File

@ -0,0 +1,22 @@
import type { AdditionalBlockProps } from '@/components/Blocks'
import Container from '@/components/Container'
import LexicalContent from '@/components/LexicalContent'
import type { RichTextBlock } from 'types/payload-types'
export default function RichText({ content, locale }: RichTextBlock & AdditionalBlockProps) {
if (content?.root?.children?.length === 0) return null
return (
<section className="py-10 first:mt-16">
<Container>
<div className="prose dark:prose-invert md:prose-lg">
<LexicalContent
// @ts-ignore
childrenNodes={content?.root?.children}
locale={locale}
lazyLoadImages={false}
/>
</div>
</Container>
</section>
)
}

View File

@ -0,0 +1,33 @@
import Author from './Author'
import RichText from './RichText'
export type AdditionalBlockProps = {
blockIndex: number
locale: string
}
const blockComponents = {
Author: Author,
RichText: RichText,
}
const Blocks = ({ blocks, locale }: any) => {
return (
<>
{blocks
?.filter(
(block: any) =>
block && block.blockType && blockComponents.hasOwnProperty(block.blockType),
)
.map((block: any, ix: number) => {
// @ts-ignore
const BlockComponent = blockComponents[block.blockType] ?? null
return BlockComponent ? (
<BlockComponent key={ix} {...block} blockIndex={ix} locale={locale} />
) : null
})}
</>
)
}
export default Blocks

10
src/components/Container.tsx Executable file
View File

@ -0,0 +1,10 @@
import cn from '@/utils/cn'
import type { ComponentPropsWithoutRef } from 'react'
const Container = ({ children, className }: ComponentPropsWithoutRef<'div'>) => {
return (
<div className={cn('container mx-auto w-full max-w-screen-lg px-3', className)}>{children}</div>
)
}
export default Container

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

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

39
src/components/Header.tsx Normal file
View File

@ -0,0 +1,39 @@
import React from 'react'
type Props = {}
export default function Header({}: Props) {
const links = [
{
label: 'About',
link: '/about',
},
{
label: 'Services',
link: '/services',
},
{
label: 'Contact',
link: '/contact',
},
]
return (
<header className="flex justify-between py-6">
<div>
<a href="/" className="decoration-transparent">
<span className="text-2xl font-extrabold ">Logo</span>
</a>
</div>
<nav>
<ul className="flex gap-4 font-bold list-none">
{links.map((item, index) => (
<li className="decoration-transparent" key={`link-${index}`}>
<a href={item.link}>{item.label}</a>
</li>
))}
</ul>
</nav>
</header>
)
}

View File

@ -0,0 +1,51 @@
//This copy-and-pasted from somewhere in lexical here: https://github.com/facebook/lexical/blob/c2ceee223f46543d12c574e62155e619f9a18a5d/packages/lexical/src/LexicalConstants.ts
// DOM
export const DOM_ELEMENT_TYPE = 1
export const DOM_TEXT_TYPE = 3
// Reconciling
export const NO_DIRTY_NODES = 0
export const HAS_DIRTY_NODES = 1
export const FULL_RECONCILE = 2
// Text node modes
export const IS_NORMAL = 0
export const IS_TOKEN = 1
export const IS_SEGMENTED = 2
// IS_INERT = 3
// Text node formatting
export const IS_BOLD = 1
export const IS_ITALIC = 1 << 1
export const IS_STRIKETHROUGH = 1 << 2
export const IS_UNDERLINE = 1 << 3
export const IS_CODE = 1 << 4
export const IS_SUBSCRIPT = 1 << 5
export const IS_SUPERSCRIPT = 1 << 6
export const IS_HIGHLIGHT = 1 << 7
export const IS_ALL_FORMATTING = IS_BOLD | IS_ITALIC | IS_STRIKETHROUGH | IS_UNDERLINE | IS_CODE | IS_SUBSCRIPT | IS_SUPERSCRIPT | IS_HIGHLIGHT
export const IS_DIRECTIONLESS = 1
export const IS_UNMERGEABLE = 1 << 1
// Element node formatting
export const IS_ALIGN_LEFT = 1
export const IS_ALIGN_CENTER = 2
export const IS_ALIGN_RIGHT = 3
export const IS_ALIGN_JUSTIFY = 4
export const IS_ALIGN_START = 5
export const IS_ALIGN_END = 6
export const TEXT_TYPE_TO_FORMAT: Record<TextFormatType | string, number> = {
bold: IS_BOLD,
code: IS_CODE,
italic: IS_ITALIC,
strikethrough: IS_STRIKETHROUGH,
subscript: IS_SUBSCRIPT,
superscript: IS_SUPERSCRIPT,
underline: IS_UNDERLINE
}
export type TextFormatType = 'bold' | 'underline' | 'strikethrough' | 'italic' | 'code' | 'subscript' | 'superscript'

View File

@ -0,0 +1,213 @@
/* eslint-disable react/no-children-prop */
import normalizePath from '@/utils/normalizePath'
import clsx from 'clsx'
import Link from 'next/link'
import React, { CSSProperties, type FC, type ReactElement } from 'react'
import Image from 'next/image'
import {
IS_BOLD,
IS_CODE,
IS_ITALIC,
IS_STRIKETHROUGH,
IS_SUBSCRIPT,
IS_SUPERSCRIPT,
IS_UNDERLINE,
} from './RichTextNodeFormat'
type SerializedLexicalNode = {
children?: SerializedLexicalNode[]
direction: string
format: number
indent?: string | number
type: string
version: number
style?: string
mode?: string
text?: string
[other: string]: any
}
type TextComponentProps = {
children: ReactElement | string
format: number
}
const getLinkForDocument = (doc: any, locale?: string): string => {
let path = doc?.path
if (!path || path.startsWith('/home') || path === '/' || path === '') path = '/'
return normalizePath(`/${locale}${path}`)
}
function gcd(a: number, b: number): number {
return b === 0 ? a : gcd(b, a % b)
}
function calculateAspectRatio(width: number, height: number): string {
const divisor = gcd(width, height)
const simplifiedWidth = width / divisor
const simplifiedHeight = height / divisor
return `${simplifiedWidth}x${simplifiedHeight}`
}
const TextComponent: FC<TextComponentProps> = ({ children, format }) => {
const formatFunctions: { [key: number]: (child: ReactElement | string) => ReactElement } = {
[IS_BOLD]: (child) => <strong>{child}</strong>,
[IS_ITALIC]: (child) => <em>{child}</em>,
[IS_STRIKETHROUGH]: (child) => <del>{child}</del>,
[IS_UNDERLINE]: (child) => <u>{child}</u>,
[IS_CODE]: (child) => <code>{child}</code>,
[IS_SUBSCRIPT]: (child) => <sub>{child}</sub>,
[IS_SUPERSCRIPT]: (child) => <sup>{child}</sup>,
}
const formattedText = Object.entries(formatFunctions).reduce(
(formattedText, [key, formatter]) => {
return format & Number(key) ? formatter(formattedText) : formattedText
},
children,
)
return <>{formattedText}</>
}
const SerializedLink: React.FC<{
node: SerializedLexicalNode
locale: string
children: React.JSX.Element | null
}> = ({ node, locale, children }) => {
const { doc, url, newTab, linkType } = node.fields as any
const document = doc?.value
const href = linkType === 'custom' ? url : getLinkForDocument(document, locale)
const target = newTab ? '_blank' : undefined
return (
<Link href={href} target={target}>
{children}
</Link>
)
}
const getNodeClassNames = (node: SerializedLexicalNode) => {
const attributes: Record<string, any> = {}
if (!node) return attributes
let classNames = ''
if (String(node?.format).toString()?.includes('left') && node.direction !== 'ltr')
classNames += 'text-left '
if (String(node?.format).toString()?.includes('center')) classNames += 'text-center '
if (String(node?.format).toString()?.includes('right') && node.direction !== 'rtl')
classNames += 'text-right '
if (classNames.length > 0) attributes.className = classNames.trim()
const indent = parseInt(`${node?.indent || 0}`)
if (!isNaN(indent) && indent !== 0) {
attributes.style = { '--indent': `${indent * 10}px` } as CSSProperties
attributes.className = `${attributes.className ?? ''} ml-[--indent]`.trim()
}
return attributes
}
const LexicalContent: React.FC<{
childrenNodes: SerializedLexicalNode[]
locale: string
className?: string
lazyLoadImages: boolean
}> = ({ childrenNodes, locale, lazyLoadImages = false }) => {
if (!Array.isArray(childrenNodes)) return null
const renderedChildren = childrenNodes.map((node, ix) => {
if (!node) return null
const attributes = getNodeClassNames(node || '')
if (node.type === 'text') {
return (
<TextComponent key={ix} format={node.format}>
<>
{Object.keys(attributes).length > 0 && <span {...attributes}>{node?.text || ''}</span>}
{(Object.keys(attributes).length === 0 && node?.text) || ''}
</>
</TextComponent>
)
}
const serializedChildren = node.children ? (
<LexicalContent
key={ix}
childrenNodes={node.children}
locale={locale}
lazyLoadImages={lazyLoadImages}
/>
) : null
switch (node.type) {
case 'linebreak':
return <br key={ix} />
case 'link':
return <SerializedLink key={ix} node={node} locale={locale} children={serializedChildren} />
case 'list':
const ListTag = node.listType === 'bullet' ? 'ul' : 'ol'
attributes.className = clsx(
attributes.className,
'mb-4 pl-8',
ListTag === 'ol' ? 'list-decimal' : 'list-disc',
)
return (
<ListTag key={ix} {...attributes}>
{serializedChildren}
</ListTag>
)
case 'listitem':
return (
<li key={ix} {...attributes}>
{serializedChildren}
</li>
)
case 'heading':
const HeadingTag = node.tag as keyof React.JSX.IntrinsicElements
return (
<HeadingTag key={ix} {...attributes}>
{serializedChildren}
</HeadingTag>
)
case 'quote':
return (
<blockquote key={ix} {...attributes}>
{serializedChildren}
</blockquote>
)
case 'upload':
const upload = node?.value
if (!upload) return null
const imageAspectRatio = calculateAspectRatio(upload.width, upload.height)
return (
<Image
key={ix}
width={upload.width}
height={upload.height}
src={upload?.url}
loading={lazyLoadImages ? 'lazy' : 'eager'}
fetchPriority={lazyLoadImages ? 'low' : 'high'}
sizes="(max-width: 768px) 65ch, 100vw"
className="max-w-[calc(100%+40px)] translate-x-[-20px]"
alt={upload?.alt || upload.filename}
/>
)
default:
if (
Array.isArray(serializedChildren?.props?.childrenNodes) &&
serializedChildren?.props?.childrenNodes.length === 0
)
return <br key={ix} />
return (
<p key={ix} {...attributes}>
{serializedChildren}
</p>
)
}
})
return <>{renderedChildren.filter((node) => node !== null)}</>
}
export default LexicalContent

View File

@ -0,0 +1,45 @@
import { Post } from 'types/payload-types'
import Image from 'next/image'
import { COLLECTION_SLUG_POST } from '@/app/(payload)/collections/config'
interface Props {
post: Post
}
export default function PostEntry(props: Props) {
if (typeof props.post.thumbnail === 'string') return
return (
<a
className="py-4 border-secondary decoration-transparent"
href={`/${COLLECTION_SLUG_POST}${props.post.path}`}
>
<article className="flex px-5 py-3 gap-8">
<Image
src={props.post.thumbnail.url || ''}
width={150}
height={150}
alt={props.post.thumbnail.alt || ''}
layout="fixed"
/>
<div className="flex flex-col gap-4 w-full">
<div className="flex justify-between w-full">
<h3 className="">{props.post.title}</h3>
{props.post.publishedDate && (
<p className="font-light">
{new Date(props.post.publishedDate).toLocaleDateString('de-DE')}
</p>
)}
</div>
{props.post.summary && <p className="max-w-prose">{props.post.summary}</p>}
</div>
{/* {props.post.author.name && (
<p>
{props.post.author.name}
</p>
)} */}
</article>
</a>
)
}

View File

@ -0,0 +1,20 @@
import React from 'react'
import { Post } from 'types/payload-types'
import Blocks from './Blocks'
import Author from './Blocks/Author'
interface Props {
post: Post
locale: string
}
export default function PostPage(props: Props) {
return (
<article className="leading-relaxed">
<Blocks blocks={props?.post.blocks} locale="" />
{props?.post.author && typeof props?.post.author !== 'string' && (
<Author author={props?.post.author} />
)}
</article>
)
}

View File

@ -0,0 +1,23 @@
'use client'
import Blocks from '@/components/Blocks'
import type { Page } from 'types/payload-types'
import { useLivePreview } from '@payloadcms/live-preview-react'
export default function PreviewBlocks({
initialData,
locale,
url,
}: {
initialData?: Page | null
locale: string
url: string
}) {
const { data } = useLivePreview({
serverURL: url,
depth: 3,
initialData: initialData,
})
return <Blocks blocks={data?.blocks || []} locale={locale} />
}

View File

@ -0,0 +1,36 @@
'use client'
import type { Post } from 'types/payload-types'
import { useLivePreview } from '@payloadcms/live-preview-react'
import PostPage from './PostPage'
const defaultPost: Post = {
id: 'default-id',
title: 'Title',
thumbnail: '',
updatedAt: '',
createdAt: '',
}
export default function PreviewBlocks({
initialData,
locale,
url,
}: {
initialData?: Post | null
locale: string
url: string
}) {
const { data } = useLivePreview({
serverURL: url,
depth: 3,
initialData: initialData,
})
const post = {
...defaultPost,
...data,
}
return <PostPage post={post} locale={locale} />
}

View File

@ -0,0 +1,17 @@
'use client'
import React from 'react'
import { handleThemeChange, switchTheme, initializeThemeSelector } from '@/utils/theme'
interface Props {}
export default function ThemeSwitcher({}: Props) {
initializeThemeSelector()
return (
<select id="theme-selector">
<option value="autonomic">Autonomic</option>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
)
}

View File

@ -0,0 +1,19 @@
/* 'use server'
import { SESSION_STRATEGY } from '@/lib/auth/config'
import { COLLECTION_SLUG_SESSIONS } from '@/payload/collections/config'
import { revalidateTag } from 'next/cache'
import type { Payload } from 'payload'
import type { User } from '~/payload-types'
export const revalidateUser = async (user: User, payload: Payload) => {
revalidateTag(`payload-user-${user.id}`)
revalidateTag(`payload-user-email-${user.email}`)
if (SESSION_STRATEGY === 'database') {
const { docs: sessions } = await (await payload).find({ where: { user: { equals: user.id } }, collection: COLLECTION_SLUG_SESSIONS })
sessions.forEach((session) => {
revalidateTag(`payload-user-session-${session.sessionToken}`)
})
}
}
*/

21
src/lib/payload/index.ts Normal file
View File

@ -0,0 +1,21 @@
import configPromise from '@payload-config'
import { getPayloadHMR as getPayloadInstance } from '@payloadcms/next/utilities'
import { headers as getHeaders } from 'next/headers'
import type { User } from 'types/payload-types'
export async function getPayload(): ReturnType<typeof getPayloadInstance> {
return getPayloadInstance({ config: await configPromise })
}
/**
* Get the current user with out needing to import the payload instance & headers.
*
* @description The difference between this function and the one in the auth/edge.ts file is that here we get
* payload instance, just to make other parts of you code cleaner. We can't get the payload instance in the
* auth/edge.ts file because that could cause a import loop.
*/
export async function getCurrentUser(): Promise<User | null> {
const headers = getHeaders()
const payload = await getPayload()
return (await payload.auth({ headers })).user
}

6
src/utils/cn.ts Executable file
View File

@ -0,0 +1,6 @@
import { clsx, type ClassValue } from 'clsx'
import { twMerge } from 'tailwind-merge'
export default function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}

4
src/utils/date.ts Normal file
View File

@ -0,0 +1,4 @@
export const getCurrentYear = (): number => {
const currentYear = new Date().getFullYear()
return currentYear
}

View File

@ -0,0 +1,7 @@
export default function generateBreadcrumbsUrl(docs: any, lastDoc: any) {
let prefix = ''
// You might want different prefixes for different collections.
switch (lastDoc._collection) {
}
return docs.reduce((url: any, doc: any) => `${url}/${doc.slug ?? ''}`, prefix)
}

37
src/utils/generateMeta.ts Normal file
View File

@ -0,0 +1,37 @@
import type { Metadata } from 'next'
import _get from 'lodash/get'
import deepmerge from 'deepmerge'
import { getDocument } from '@/utils/getDocument'
import normalizePath from './normalizePath'
const defaultTitle = 'Nextload'
const defaultDescription = 'An open-source website built with Payload and Next.js.'
const siteName = 'Nextload'
const defaultOpenGraph: Metadata['openGraph'] = {
type: 'website',
description: defaultDescription,
siteName,
title: defaultTitle,
}
export const generateMeta = async (path: string | string[]): Promise<Metadata> => {
const doc = await getDocument({
collection: 'pages',
path,
depth: 1,
})
const metaTitle = _get(doc, 'meta.title', null)
const title = metaTitle ?? (_get(doc, 'title', defaultTitle) as string)
const description = _get(doc, 'meta.description', defaultDescription)
const ogImage = _get(doc, 'meta.image.url', `/api/og${normalizePath(path, true)}image.jpg`)
return {
description,
openGraph: deepmerge(defaultOpenGraph, {
description,
images: ogImage ? [{ url: ogImage }] : undefined,
}),
title: { absolute: title },
}
}

View File

@ -0,0 +1,13 @@
export default function generateRandomString(
length: number,
characters: string = 'abcdefghijklmnopqrstuvwxyz0123456789',
): string {
let result = ''
const charactersLength = characters.length
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength))
}
return result
}

80
src/utils/getDocument.ts Normal file
View File

@ -0,0 +1,80 @@
import type { Config } from 'types/payload-types'
import { unstable_cache } from 'next/cache'
import normalizePath from '@/utils/normalizePath'
import { getCurrentUser, getPayload } from '@/lib/payload'
import { draftMode } from 'next/headers'
type Collection = keyof Config['collections'] | string
type Path = string | string[]
type CacheOption = 'noDraft' | true | false
export const generateDocumentCacheKey = (collection: Collection, path?: Path): string => {
return `${collection}_path_${normalizePath(path, false)}`
}
export const generateDocumentCacheParams = (collection: Collection, path?: Path): string[] => {
return [collection, normalizePath(path)]
}
export const conditionalCache = async <T>(
fetchFunction: () => Promise<T>,
cacheKey: string,
cache: boolean | undefined,
revalidate: false | number | undefined,
): Promise<T> => {
if (!cache) {
return fetchFunction()
}
return unstable_cache(fetchFunction, [cacheKey], { revalidate, tags: [cacheKey] })()
}
interface GetDocumentParams<K extends keyof Config['collections']> {
collection: K
path?: Path
depth?: number
cache?: CacheOption
revalidate?: false | number | undefined
}
export const getDocument = async <K extends keyof Config['collections']>({
collection,
path,
depth = 0,
cache = 'noDraft',
revalidate = false,
}: GetDocumentParams<K>): Promise<Config['collections'][K] | null> => {
const { isEnabled: draft } = draftMode()
const payload = await getPayload()
const user = draft ? await getCurrentUser() : null
const normalizedPath = normalizePath(path, false)
const where = { path: { equals: normalizedPath } }
const cacheKey = generateDocumentCacheKey(collection, path)
const shouldCache = draft ? false : !!cache
return conditionalCache(
async () => {
return await payload
.find({
collection,
draft,
depth,
limit: 1,
overrideAccess: false,
user,
where,
})
.catch((error) => {
console.error('Error fetching document:', error)
return null
})
.then((result) => {
if (!result || !result.docs || result.docs.length === 0) return null
return result.docs.at(0) as Config['collections'][K] | null
})
},
cacheKey,
shouldCache,
revalidate,
)
}

View File

@ -0,0 +1,9 @@
const normalizePath = (path?: string | string[], keepTrailingSlash: boolean = false): string => {
if (!path) return '/'
if (Array.isArray(path)) path = path.join('/')
path = `/${path}/`.replace(/\/+/g, '/')
path = path !== '/' && !keepTrailingSlash ? path.replace(/\/$/, '') : path
return path
}
export default normalizePath

21
src/utils/theme.ts Normal file
View File

@ -0,0 +1,21 @@
export function switchTheme(themeClass: string): void {
const bodyElement = document.body
const classesToRemove = ['light', 'dark', 'autonomic']
bodyElement.classList.remove(...classesToRemove)
bodyElement.classList.add(themeClass)
}
export function handleThemeChange(event: Event): void {
const selectElement = event.target as HTMLSelectElement
const selectedTheme = selectElement.value
switchTheme(selectedTheme)
}
export function initializeThemeSelector(): void {
const themeSelector = document.getElementById('theme-selector')
if (themeSelector) {
themeSelector.addEventListener('change', handleThemeChange)
}
}

19
tailwind.config.js Normal file
View File

@ -0,0 +1,19 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./src/app/(app)/**/*.{html,tsx}', './src/components/**/*'],
theme: {
extend: {
colors: {
primary: 'rgba(var(--primary))',
secondary: 'rgba(var(--secondary))',
tertiary: 'rgba(var(--tertiary))',
text: 'rgba(var(--text))',
textInverted: 'rgba(var(--textInverted))',
link: 'rgba(var(--link))',
background: 'rgba(var(--background))',
},
},
},
safelist: [],
plugins: [],
}

View File

@ -26,6 +26,12 @@
"@/*": [
"./src/*"
],
"@payload/*": [
"./src/app/(payload)/*"
],
"@app/*": [
"./src/app/(app)/*"
],
"@payload-config": [
"./payload.config.ts"
]

View File

@ -9,8 +9,10 @@
export interface Config {
collections: {
users: User;
pages: Page;
posts: Post;
authors: Author;
media: Media;
pages: Page;
'payload-preferences': PayloadPreference;
'payload-migrations': PayloadMigration;
};
@ -26,6 +28,7 @@ export interface Config {
*/
export interface User {
id: string;
roles: ('admin' | 'editor' | 'user')[];
updatedAt: string;
createdAt: string;
email: string;
@ -39,11 +42,57 @@ export interface User {
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "pages".
* via the `definition` "posts".
*/
export interface Page {
export interface Post {
id: string;
title?: string | null;
title: string;
summary?: string | null;
publishedDate?: string | null;
thumbnail: string | Media;
'content-title'?: string | null;
blocks?:
| (
| RichTextBlock
| {
author?: (string | null) | Author;
id?: string | null;
blockName?: string | null;
blockType: 'Author';
}
)[]
| null;
author?: (string | null) | Author;
slug?: string | null;
path?: string | null;
updatedAt: string;
createdAt: string;
_status?: ('draft' | 'published') | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "media".
*/
export interface Media {
id: string;
alt: string;
updatedAt: string;
createdAt: string;
url?: string | null;
thumbnailURL?: string | null;
filename?: string | null;
mimeType?: string | null;
filesize?: number | null;
width?: number | null;
height?: number | null;
focalX?: number | null;
focalY?: number | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "RichTextBlock".
*/
export interface RichTextBlock {
content?: {
root: {
type: string;
@ -59,25 +108,54 @@ export interface Page {
};
[k: string]: unknown;
} | null;
id?: string | null;
blockName?: string | null;
blockType: 'RichText';
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "authors".
*/
export interface Author {
id: string;
avatar: string | Media;
name: string;
bio?: string | null;
user?: string | User | null;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "media".
* via the `definition` "pages".
*/
export interface Media {
export interface Page {
id: string;
text?: string | null;
title?: string | null;
blocks?:
| (
| RichTextBlock
| {
author?: (string | null) | Author;
id?: string | null;
blockName?: string | null;
blockType: 'Author';
}
)[]
| null;
slug?: string | null;
path?: string | null;
breadcrumbs?:
| {
doc?: (string | null) | Page;
url?: string | null;
label?: string | null;
id?: string | null;
}[]
| null;
updatedAt: string;
createdAt: string;
url?: string | null;
thumbnailURL?: string | null;
filename?: string | null;
mimeType?: string | null;
filesize?: number | null;
width?: number | null;
height?: number | null;
_status?: ('draft' | 'published') | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema