Compare commits

..

508 Commits

Author SHA1 Message Date
Sasha
73148ca819 templates: add script to regenerate types in all templates 2024-12-11 21:04:25 +02:00
Alessio Gravili
00d438e91f refactor(ui): export TableColumnsProvider, documentDrawerBaseClass and SelectMany (#9899) 2024-12-11 18:34:58 +00:00
Patrik
b1d92c2bad feat: allows excluding entities from the nav sidebar / dashboard without disabling its routes (#9897)
### What?

Previously, the `admin.group` property on `collection` / `global`
configs allowed for a custom group and the `admin.hidden` property would
not only hide the entity from the nav sidebar / dashboard but also
disable its routes.

### Why?

There was not a simple way to hide an entity from the nav sidebar /
dashboard but still keep the entities routes.

### How?

Now - we've added the `false` type to the `admin.group` field to account
for this.

Passing `false` to `admin.group` will hide the entity from the sidebar
nav and dashboard but keep the routes available to navigate.

I.e

```
admin: {
  group: false,
},
```
2024-12-11 13:31:12 -05:00
Elliot DeNolf
5c2f72d70e templates: bump for v3.6.0 (#9900)
🤖 Automated bump of templates for v3.6.0

Triggered by user: @denolfe

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-12-11 12:27:46 -06:00
Joachim Damsgaard
e78b542ebc docs: fix typo (#9886)
Unfinished value string in documentation example
2024-12-11 13:12:48 -05:00
Elliot DeNolf
45d20643df chore(release): v3.6.0 [skip ci] 2024-12-11 13:05:47 -05:00
Alessio Gravili
91e8acc871 fix: cannot pass function to client error when defining server-only props in custom field components (#9898)
Fixes https://github.com/payloadcms/payload/issues/9895

We were still including field custom components in the ClientConfig,
which will throw an error if actual server-only properties were passed
to `PayloadComponent.serverProps`. This PR removes them from the
ClientConfig
2024-12-11 18:00:09 +00:00
Alessio Gravili
b83ea8494e refactor(richtext-lexical): export useBlockComponentContext and useInlineBlockComponentContext (#9896) 2024-12-11 10:46:57 -07:00
Sasha
b73fc586b8 feat: expose session, db in migrations to use the active transaction with the database directly (#9849)
This PR adds a feature which fixes another issue with migrations in
Postgres and does few refactors that significantly reduce code
duplication.

Previously, if you needed to use the underlying database directly in
migrations with the active transaction (for example to execute raw SQL),
created from `payload create:migration`, as `req` doesn't work there you
had to do something like this:
```ts
// Postgres
export async function up({ payload, req }: MigrateUpArgs): Promise<void> {
  const db = payload.db.sessions?.[await req.transactionID!].db ?? payload.db
  const { rows: posts } = await db.execute(sql`SELECT * from posts`)
}

// MongoDB
export async function up({ payload, req }: MigrateUpArgs): Promise<void> {
  const session = payload.db.sessions?.[await req.transactionID!]
  const posts = await payload.db.collections.posts.collection.find({ session }).toArray()
}
```

Which was:
1. Awkward to write
2. Not documented anywhere

Now, we expose `session` and `db` to `up` and `down` functions for you:

#### MongoDB:
```ts
import { type MigrateUpArgs } from '@payloadcms/db-mongodb'

export async function up({ session, payload, req }: MigrateUpArgs): Promise<void> {
  const posts = await payload.db.collections.posts.collection.find({ session }).toArray()
}
```
#### Postgres:
```ts
import { type MigrateUpArgs, sql } from '@payloadcms/db-postgres'

export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
  const { rows: posts } = await db.execute(sql`SELECT * from posts`)
}
```

#### SQLite:
```ts
import { type MigrateUpArgs, sql } from '@payloadcms/db-sqlite'

export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
  const { rows: posts } = await db.run(sql`SELECT * from posts`)
}
```
This actually was a thing with Postgres migrations, we already were
passing `db`, but:
1. Only for `up` and when running `payload migrate`, not for example
with `payload migrate:fresh`
2. Not documented neither in TypeScript or docs.

By ensuring we use `db`, this also fixes an issue that affects all
Postgres/SQLite migrations:

Currently, if we run `payload migration:create` with the postgres
adapter we get a file like this:

```ts
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres'

export async function up({ payload, req }: MigrateUpArgs): Promise<void> {
  await payload.db.drizzle.execute(sql`
   CREATE TABLE IF NOT EXISTS "users" (
  	"id" serial PRIMARY KEY NOT NULL,
  );
```
Looks good?
Not exactly!
`payload.db.drizzle.execute()` doesn't really use the current
transaction which can lead to some problems.
Instead, it should use the `db` from `payload.db.sessions?.[await
req.transactionID!].db` because that's where we store our Drizzle
instance with the transaction.

But now, if we generate `payload migrate:create` we get:
```ts
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres'

export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
  await db.execute(sql`
   CREATE TABLE IF NOT EXISTS "users" (
  	"id" serial PRIMARY KEY NOT NULL,
  );
```

Which is what we want, as the `db` is passed correctly here:

76428373e4/packages/drizzle/src/migrate.ts (L88-L90)

```ts
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
  const dbWithTransaction = payload.db.sessions?.[await req.transactionID!].db
  payload.logger.info({ one: db === dbWithTransaction })
  payload.logger.info({ two: db === payload.db.drizzle })
```
<img width="336" alt="image"
src="https://github.com/user-attachments/assets/f9fab5a9-44c2-44a9-95dd-8e5cf267f027">

Additionally, this PR refactors:
* `createMigration` with Drizzle - now we have sharable
`buildCreateMigration` in `@payloadcms/drizzle` to reduce copy-pasting
of the same logic.
* the `v2-v3` relationships migration for Postgres is now shared between
`db-postgres` and `db-vercel-postgres`, again to reduce copy-paste.
2024-12-11 12:23:12 -05:00
Said Akhrarov
0303b78d62 fix(next): thread default ServerProps to view actions and other components that were missing (#9868)
This PR threads default `serverProps` to Edit and List view action slots, as well as other various components that were missing them.

---------

Co-authored-by: Alessio Gravili <alessio@gravili.de>
2024-12-11 17:19:37 +00:00
James Mikrut
a0f0316534 fix: ensures autosave only runs sequentially (#9892)
Previously, Autosave could trigger 2 parallel fetches where the second
could outpace the first, leading to inconsistent results.

Now, we use a simple queue-based system where we can push multiple
autosave events into a queue, and only the latest autosave will be
performed.

This also prevents multiple autosaves from ever running in parallel.
2024-12-11 09:33:48 -06:00
Patrik
522399095c feat(next): adds suppressHydrationWarning property to payload config admin options (#9867)
### What?

There are scenarios where the server-rendered HTML might intentionally
differ from the client-rendered DOM causing `Hydration` errors in the
DOM.

### How?

Added a new prop to the payload config `admin` object called
`suppressHydrationWarning` that allows control to display these warnings
or not.

If you set `suppressHydrationWarning` to `true`, React will not warn you
about mismatches in the attributes and the content of that element.

Defaults to `false` - so if there is a mismatch and this prop is not
defined in your config, the hydration errors will show.

```
admin: {
  suppressHydrationWarning: true // will suppress the errors if there is a mismatch
}
```
2024-12-11 10:24:56 -05:00
Dan Ribbens
306b5d2300 fix: forgotPassword set expiration time (#9871)
The logic for creating a timestamp for use in resetPassword was not
correctly returning a valid date.

---------

Co-authored-by: Patrik Kozak <patrik@payloadcms.com>
2024-12-11 08:43:22 -05:00
Jarrod Flesch
ca52a50dd9 feat: consolidates create and duplicate operations (#9866) 2024-12-10 21:44:47 -05:00
Alessio Gravili
5bfc92d71d fix(next): next.js 15.1.0 compatibility by not importing isRedirectError from next/dist (#9878)
Fixes https://github.com/payloadcms/payload/issues/9876

The import path has changed in Next.js 15.1.0
2024-12-11 01:00:53 +00:00
Alessio Gravili
b1ef28dd39 feat: allow where in payload.jobs.run (#9877)
Example:

```ts
  await payload.jobs.queue({
        task: 'MyTask',
        input: {
          message: `secret`,
        },
 })

await payload.jobs.run({ where: { 'input.message':  { equals: 'secret' } } })
```
2024-12-11 00:33:53 +00:00
Alessio Gravili
09246a45e0 feat: add payload.jobs.runByID (#9875) 2024-12-10 23:37:06 +00:00
Jacob Fletcher
da6bc55b19 fix(ui): ensures admin.disableListFilter is disabled despite url search params (#9874)
Continuation of #9846 and partial fix for #9774. When setting
`admin.disableListFilter` retroactively, it remains active within the
list filter controls. Same for when the URL search query contains one of
these fields, except this will actually display the _wrong_ field,
falling back to the _first_ field from the config. The fix is to
properly disable the condition for this field if it's an active filter,
while still preventing it from ever rendering as an option within the
field selector itself.
2024-12-10 17:37:52 -05:00
Jacob Fletcher
f7172b5b2c fix(ui): refreshes column state during hmr and respects admin.disableListColumn despite preferences (#9846)
Partial fix for #9774. When `admin.disableListColumn` is set
retroactively, it continues to appear in column state, but shouldn't.
This was because the table column context was not refreshing after HMR
runs, and would instead hold onto these stale columns until the page
itself refreshes. Similarly, this was also a problem when the user had
saved any of these columns to their list preferences, where those prefs
would take precedence despite these properties being set on the
underlying fields. The fix is to filter these columns from all requests
that send them, and ensure local component state properly refreshes
itself.
2024-12-10 15:11:44 -05:00
Patrik
563694d930 fix(ui): prevents unwanted data overrides when bulk editing (#9842)
### What?

It became possible for fields to reset to a defined `defaultValue` when
bulk editing from the `edit-many` drawer.

### Why?

The form-state of all fields were being considered during a bulk edit -
this also meant using their initial states - this meant any fields with
default values or nested fields (`arrays`) would be overwritten with
their initial states

I.e. empty values or default values.

### How?

Now - we only send through the form data of the fields specifically
being edited in the edit-many drawer and ignore all other fields.

Leaving all other fields stay their current values.

Fixes #9590

---------

Co-authored-by: Dan Ribbens <dan.ribbens@gmail.com>
2024-12-10 11:39:15 -05:00
Jarrod Flesch
fee17448e7 chore: better default for useAsTitle with custom auth collections (#9841)
### What?
Custom auth collections default `useAsTitle` to `id`.

### Why?
It is more expected for auth collections to search on email or username.

### How?
Defaults useAsTitle to `username` if loginWithUsername is used, else
`email`. Can still be overridden by setting a custom `admin.useAsTitle`
property.
2024-12-10 08:42:23 -05:00
Alessio Gravili
76428373e4 docs: properly capitalize SQLite and Next.js (#9848) 2024-12-10 02:11:51 +00:00
Alessio Gravili
254d888b73 docs: add missing types, prefer pnpm, fix various typos, discourage using payload from import (#9847)
- Adds missing types, especially the `Where` type. Will be helpful for
people to see that they can type their queries like that
- Mention pnpm first and prefer pnpm > npm > yarn throughout docs
- Add `payload` to function arguments in examples to discourage people
from doing `import payload from 'payload'`
- PNPM => pnpm, NPM => npm
- Fix some typos
2024-12-09 19:05:09 -07:00
Alessio Gravili
36c2714251 docs: fix typo (#9845) 2024-12-10 01:24:28 +00:00
Elliot DeNolf
43a0ce7445 templates: bump for v3.5.0 (#9844)
🤖 Automated bump of templates for v3.5.0

Triggered by user: @paulpopus

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-12-10 00:14:17 +00:00
Paul
d3b8d0c263 fix(templates): website infinite reload bug with 404s in production mode (#9843)
Fixes https://github.com/payloadcms/payload/issues/9839 infinite reload
bug caused by live preview listener being in the layout — only happens
in production mode when hitting 404 pages.
2024-12-10 00:03:21 +00:00
Jayce Pulsipher
1e5364ff34 fix: upgrade pg snapshot during v3 upgrade if needed (#9837)
### What?

Upgrade PG snapshot if needed during v3 migration

### Why?

I'm unable to run the v3 migration as my PG snapshot is on version 5
instead the required 7

### How?

Copy code from typical migrations to upgrade PG snapshot as needed:
https://github.com/payloadcms/payload/blob/main/packages/drizzle/src/postgres/createMigration.ts#L64

Co-authored-by: Jayce Pulsipher <jpulsipher@nav.com>
2024-12-09 15:46:54 -05:00
Jacob Fletcher
e095222a9c fix(next): does not format top-level domains within admin.preview or livePreview.url functions (#9831)
Fixes #9830. Continuation of #9755 and #9746. Instead of automatically
appending TLDs to the `admin.preview` and the `livePreview.url` URLs, we
should instead ensure that `req` is passed through these functions, so
that you can have full control over the format of this URL without
Payload imposing any of its own formatting.
2024-12-09 11:54:20 -05:00
Paul
84abfdf84a ci: add missing tests to all-green dependency array (#9825)
Adds the missing tests to the `needs:` dependency array for `all-green`
step in CI so that all-green doesn't pass if these tests fail or are in
progress

```
- build-templates
- tests-types
- tests-type-generation
```
2024-12-09 10:36:58 -05:00
Elliot DeNolf
e746d7afd8 test: properly mock nodemailer verify in unit test (#9832)
If nodemailer was down, this unit test would fail. Verify function
should now be properly mock and not make a network call.
2024-12-09 14:58:18 +00:00
Riley Pearce
d791db24bd fix(richtext-lexical): lexical-html export (#9793)
### What?

`@lexical-html` is missing from the `lexical-proxy` exports.

### Why?

To allow `@lexical-html` functionality to be used without needing to
install the package separately.

### How?

Adds `@lexical-html` to the `lexical-proxy` exports.

Fixes #9792
2024-12-09 08:51:38 -03:00
Paul
b0c9b41a5a templates: website template added changes for seed script, relative live preview URLs and fixed endpoint status code (#9808) 2024-12-09 05:12:36 +00:00
Alessio Gravili
60ceeb0dc1 fix(richtext-*): field errors and descriptions were not displayed (#9824) 2024-12-08 23:46:26 +00:00
Alessio Gravili
dc741bb140 chore(deps): upgrade dataloader dependency from 2.2.2 to 2.2.3 (#9823) 2024-12-08 21:03:59 +00:00
Alessio Gravili
7599ede41d refactor(richtext-lexical): export JSXConverter type (#9815) 2024-12-08 07:33:57 +00:00
Elliot DeNolf
e236c28b9c ci: add PR co-authors to contributors section of release notes 2024-12-07 14:45:43 -05:00
Sasha
f09ee0b84b ci: add types testing with tstyche (#9803)
As proposed here
https://github.com/payloadcms/payload/pull/9782#issuecomment-2522090135
with additional testing of our types we can be more sure that we don't
break them between updates.

This PR already adds types testing for most Local API methods
6beb921c2e/test/types/types.spec.ts
but new tests for types can be easily added, either to that same file or
you can create `types.spec.ts` in any other test folder.

The new test folder uses `strict: true` to ensure our types do not break
with it.

---------

Co-authored-by: Tom Mrazauskas <tom@mrazauskas.de>
2024-12-07 16:38:25 +02:00
Elliot DeNolf
1fdc7cc70d templates: bump for v3.5.0 (#9804)
🤖 Automated bump of templates for v3.5.0

Triggered by user: @denolfe

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-12-06 20:35:48 +00:00
Elliot DeNolf
2c0bea8c3e chore: post-release-templates fetch tags 2024-12-06 15:28:19 -05:00
Elliot DeNolf
67a35d35e5 ci: adjust tag detection for post-release-templates 2024-12-06 15:18:12 -05:00
Elliot DeNolf
a80de3f0c0 chore: update template lockfiles 2024-12-06 15:11:02 -05:00
Elliot DeNolf
ff62017cf2 chore(release): v3.5.0 [skip ci] 2024-12-06 14:58:41 -05:00
Alessio Gravili
8f3f44922e feat: upgrade to React 19 stable and Next.js 15.0.4 (#9801)
No need for those ugly @types/react overrides anymore!
2024-12-06 14:53:36 -05:00
Patrik
1aa23d3ea3 chore(cpa): updates .env.example env vars along side .env vars based on selected DB (#9757)
### What?

Previously, the create-payload-app install would properly update the env
vars in the new created `.env` file but kept outdated env vars in the
`.env.example` file.

I.e

If selecting a `postgres` DB for the blank or website template:

`.env` --> `DATABASE_URI` -->
`postgres://postgres:<password>@127.0.0.1:5432/test-cpa`

`.env.example` --> `DATABASE_URI` -->
`mongodb://127.0.0.1/payload-template-blank-3-0`

### Now

`.env` --> `DATABASE_URI` -->
`postgres://postgres:<password>@127.0.0.1:5432/test-cpa`

`.env.example` --> `DATABASE_URI` -->
`postgres://postgres:<password>@127.0.0.1:5432/test-cpa`
2024-12-06 14:52:11 -05:00
Elliot DeNolf
a1a0a07ff0 ci: add nightly stale cron (#9802)
Add nightly cron to label/close stale issues.
2024-12-06 13:53:56 -05:00
Sasha
afd0b54d1b feat: export sanitizeSelectParam, sanitizePopulateParam, senitizeJoinParams utils (#9777)
### What?
* Exposes to `payload` these functions: `sanitizeSelectParam`,
`sanitizePopulateParam`, `senitizeJoinParams`.
* Refactors `sanitizeSelect` and `sanitizePopulate` to
`sanitizeSelectParam` and `sanitizePopulateParam` for clarity.
* Moves them from `@payloadcms/next` to `payload` as they aren't related
to next.

### Why?
To use these functions externally, for example in custom endpoints.
2024-12-06 13:30:04 -05:00
Patrik
62fc2f552f fix(ui): collapsed array state on input change (#9800)
### What?

Previously, `initCollapsed: true` `array` fields would auto collapse
when typing in their respective inputs while in the create new view.

### Why?

This was due to the fact that we were only checking if `preferences`
existed in `form state` to handle the current state of the array row and
then falling back on the `initCollapsed` prop if `preferences` didn't
exist.

This was a problem because during create - `preferences` do not exist
yet. As a result, the state of the array row would keep falling back to
collapsed if `initCollapsed` was set to `true`.

### How?

To fix this, we now check the actual form state first before falling
back to preferences and then falling back to the initCollapsed prop
value.

Fixes #9775
2024-12-06 13:22:41 -05:00
Nikola Spalevic
10eab87653 chore(translations): improved serbian translations for the lexical editor (#9795)
### What?

Enhanced Serbian translations for the lexical editor have been
implemented. The updates correct inaccuracies in the Serbian Cyrillic
translations and address various errors in the previous versions.


### Why?

- Incorrect use of Latin script in place of Cyrillic.
- Contextual errors in translations.
2024-12-06 09:10:17 -05:00
Elliot DeNolf
bbf35a62d8 ci: explicitly use ubuntu-24.04 instead of latest to ensure compat (#9786)
The runner image `ubuntu-latest` image will be switching from Ubuntu
22.04 to Ubuntu 24.04 as specified in
https://github.com/actions/runner-images/issues/10636.

> Rollout will begin on December 5th and will complete on January 17th,
2025.
Breaking changes 
Ubuntu 24.04 is ready to be the default version for the "ubuntu-latest"
label in GitHub Actions and Azure DevOps.

This PR moves us to explicitly use `ubuntu-24.04` to ensure
compatibility and to allow explicit upgrades in the future.
2024-12-06 02:27:10 -05:00
Elliot DeNolf
a108986f1b ci: fetch-depth 0 needed for lint job 2024-12-06 02:12:06 -05:00
Elliot DeNolf
4cc6f4cee4 ci: main workflow improvements (#9784)
Speed up and refactor main workflow:

- Take advantage of re-usable node/pnpm setup action
- Remove explicit fetch-depth
- Clean up timeout-minutes
2024-12-06 02:03:45 -05:00
Paul
cb691e0642 ci: only run tests when needed via needs_tests filter (#9781)
Only run tests when needed via `needs_tests` filter. Useful to avoid
running test suite with template changes.
2024-12-05 23:19:34 -05:00
Said Akhrarov
c9ce3501a0 docs(plugin-search): add info on collection reindexing (#9764)
Adds documentation for the feature introduced with [plugin-search
collection reindexing](https://github.com/payloadcms/payload/pull/9391).
This also fixes an invalid scss import in one of the examples.

Credit to @rilrom for the invalid css import find!
2024-12-05 17:13:51 -05:00
Elliot DeNolf
ef8d3c9bf4 ci: post-release-templates assign PR to user that triggered 2024-12-05 16:34:54 -05:00
Elliot DeNolf
d3232b947d templates: bump for v3.4.0 (#9780)
Automated bump of templates for v3.4.0

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-12-05 15:33:33 -06:00
Elliot DeNolf
28c6b2a66b ci: post-release-templates always use latest tag with workflow_dispatch 2024-12-05 16:26:26 -05:00
Dan Ribbens
a11243e288 fix(ui): join field ignoring defaultSort and defaultLimit (#9766)
The join field was not respecting the defaultSort or defaultLimit of the
field configuration.

### Why?

This was never implemented.

### How?

This fix applies these correct limit and sort properties to the query,
first based on the field config and as a fallback, the collection
configuration.
2024-12-05 21:23:19 +00:00
Paul
19ddd3cfc6 templates: improvements to seed speed on website template and updated hero and collapsible fields (#9779)
- Improvements to seed speed on the website template
- Update hero on mobile
- Fields are collapsed by default where possible now
- Add rowlabel components for nav items
2024-12-05 15:08:02 -06:00
Said Akhrarov
1ab3be65f8 fix(ui): disable doc submenu when parent button is disabled (#9750) 2024-12-05 15:56:00 -05:00
Sasha
de53f2a826 docs: adds missing "to" in jobs-queue/overview (#9778)
Adds missing "to":
"offload these tasks a separate compute resource"
 ->
 "offload these tasks **_to_** a separate compute resource".
2024-12-05 19:38:16 +00:00
Sasha
7def6b7ddf fix: defaultPopulate and populate with nested to arrays/blocks properties (#9751)
Fixes https://github.com/payloadcms/payload/issues/9718

### What?
`defaultPopulate` and `populate` didn't work properly when defining
nested to arrays and blocks properties:
```ts
import type { CollectionConfig } from 'payload'
export const Pages: CollectionConfig<'pages'> = {
  slug: 'pages',
  defaultPopulate: {
    slug: true,
    array: {
      title: true,
    },
    blocks: {
      some: {
        title: true,
      },
    },
  },
  access: { read: () => true },
  fields: [
    {
      name: 'slug',
      type: 'text',
      required: true,
    },
    {
      name: 'additional',
      type: 'text',
    },
    {
      name: 'array',
      type: 'array',
      fields: [
        {
          name: 'title',
          type: 'text',
        },
        {
          name: 'other',
          type: 'text',
        },
      ],
    },
    {
      name: 'blocks',
      type: 'blocks',
      blocks: [
        {
          slug: 'some',
          fields: [
            {
              name: 'title',
              type: 'text',
            },
            {
              name: 'other',
              type: 'text',
            },
          ],
        },
      ],
    },
  ],
}

``` 

### Why?
This should work

### How?
Turns out, it wasn't a great idea to mutate passed `select` directly in
`afterRead/promise.ts` to force select some properties `id` ,
`blockType`. Now we do shallow copies when needed.
2024-12-05 12:29:22 -05:00
Sasha
840dde2b17 fix(db-mongodb): bump mongoose to 8.8.3 (#9747)
Fixes https://github.com/payloadcms/payload/issues/9729. The current
version has vulnerability
https://avd.aquasec.com/nvd/2024/cve-2024-53900/. Technically, Payload
doesn't use described in the report
[`$where`](https://www.mongodb.com/docs/manual/reference/operator/query/where/#op._S_where)
property in its queries at all, but it may affect those who access
mongoose via `payload.db.collections` directly
2024-12-05 18:43:14 +02:00
Elliot DeNolf
c2ff9b1dd8 ci: use PAT for post-release-templates 2024-12-05 10:51:12 -05:00
github-actions[bot]
97aca5fde7 templates: bump for v3.4.0 (#9765)
Automated bump of templates for v3.4.0

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-12-05 05:12:46 +00:00
Paul
97d3bb1c11 templates(website): add next sitemap robots disallow config for /admin (#9761)
Sets robots disallow to /admin
2024-12-05 01:30:40 +00:00
Paul
8f785e1fde chore(ui): expose onInputChange from react-select in SelectInput component (#9728) 2024-12-04 19:27:14 -06:00
Germán Jabloñski
89db8fb7f2 chore(templates): migrate to new richtext component in website template (#9615)
In addition to requiring fewer files, it supports more nodes. If you
currently initialize a website template and want to use features such as
images or tables, they are not rendered. With this change that happens
automatically.

Credits to @AlessioGr for the [JSX
serializer](https://github.com/payloadcms/payload/pull/8795).

---------

Co-authored-by: Paul Popus <paul@nouance.io>
2024-12-05 00:38:49 +00:00
Paul
3d1305de5c templates: fixes the seeding for the website template when using postgres (#9758) 2024-12-04 23:58:57 +00:00
Sasha
0829a350ce feat: allow to define global label as function (#9759) 2024-12-04 18:39:35 -05:00
Jacob Fletcher
1fc9c47f20 feat(next): supports relative preview URLs (#9755)
Similar to #9746. When deploying to Vercel, preview deployment URLs are
dynamically generated. This breaks `admin.preview` within those
deployments because there is no mechanism by which we can detect and set
that URL within Payload. Although Vercel provides various environment
variables at our disposal, they provide no concrete identifier for
exactly which URL is being currently previewed (you can access the same
deployment from a number of different URLs).

The fix is to support relative `admin.preview` URLs, that way Payload
can prepend the application's top-level domain dynamically at
render-time in order to create a fully qualified URL. So when you visit
a Vercel preview deployment, for example, that deployment's unique URL
is used as the preview redirect, instead of the application's
root/production domain. Note: this does not fix multi-tenancy
single-domain setups, as those still require a static top-level domain
for each tenant.
2024-12-04 17:01:09 -05:00
Alessio Gravili
61a4656ef5 chore(richtext-lexical): remove outdated custom block component examples (#9754) 2024-12-04 19:58:50 +00:00
Sasha
d8f7034ab8 fix: getPayload generate import map only when used in Payload Admin Panel (#9371)
After the change with removing `getPayloadHMR`, we do generate import
map even outside of Next.js, which leads to errors when using in a
project without it:

![image](https://github.com/user-attachments/assets/e8dc2af6-566b-443c-a6d8-8b02e719bd30)
2024-12-04 19:46:45 +00:00
github-actions[bot]
fa39b37a44 templates: bump for v3.4.0 (#9752)
Automated bump of templates for v3.4.0

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-12-04 19:46:32 +00:00
Jarrod Flesch
fa7ed3f621 fix(ui): stale locale value from useLocale (#9582)
### What?
Fixes issue with stale locale from searchParams

### Why?
Bad use of useEffect/useState inside our useSearchParams provider.

### How?
Memoize the locale instead of relying on the useEffect which was causing
unnecessary renders with stale values.
2024-12-04 14:00:17 -05:00
Paul
2321970fcc templates: improve speed of seed script (#9748)
Improves the speed of the seed script by moving operations to be async
and disabling revalidation on them
2024-12-04 12:38:18 -06:00
Alessio Gravili
84a5b4066d ci: ensure clean all script does not error after retrying step, by installing globby and chalk globally (#9745) 2024-12-04 18:37:46 +00:00
Jacob Fletcher
f12b4dc6b0 feat(live-preview): supports relative urls for dynamic preview deployments (#9746)
When deploying to Vercel, preview deployment URLs are dynamically
generated. This breaks Live Preview within those deployments because
there is no mechanism by which we can detect and set that URL within
Payload. Although Vercel provides various environment variables at our
disposal, they provide no concrete identifier for exactly _which_ URL is
being currently previewed (you an access the same deployment from a
number of different URLs).

The fix is to support _relative_ live preview URLs, that way Payload can
prepend the application's top-level domain dynamically at render-time in
order to create a fully qualified URL. So when you visit a Vercel
preview deployment, for example, that deployment's unique URL is used to
load the iframe of the preview window, instead of the application's
root/production domain. Note: this does not fix multi-tenancy
single-domain setups, as those still require a static top-level domain
for each tenant.
2024-12-04 13:31:43 -05:00
Jarrod Flesch
8e26824bf8 fix(ui): only render header dom node if needed (#9742)
### What?
The `<header>` dom node was rendering even if empty for group fields.
Causing extra margin to be added even if no label/description were
provided.

### Why?
If the field had no label, description or errors it would still render.

### How?
Wraps the header node in an additional condition that checks for label,
description or errors before rendering the node.
2024-12-04 13:29:45 -05:00
Elliot DeNolf
12a8bba852 ci: ensure triage actions work for PRs from forks 2024-12-04 11:35:38 -05:00
Sasha
dff71eedaa fix(db-postgres): handle select query on select fields (#9607)
Fixes https://github.com/payloadcms/payload/issues/9606

With Postgres / SQLite, select fields (non `hasMany: true`) weren't
properly handled in the `traverseFields.ts` function for `select` query.
2024-12-04 11:27:03 -05:00
Jarrod Flesch
4a324a9de7 fix(ui): incorrect label size for group field (#9740)
### What?
The group label was incorrectly sized.

![CleanShot 2024-12-04 at 10 33
13](https://github.com/user-attachments/assets/c94cb8b9-4453-4d57-a483-ea35a63f04a9)

### Why?
It should be rendered as a span not a label.

### How?
Added `as="span"` to the FieldLabel for the group field like we do for
the array field.

![CleanShot 2024-12-04 at 10 35
13](https://github.com/user-attachments/assets/99bdec33-39ee-4bd0-a4d1-ed3635e2bea7)
2024-12-04 11:13:53 -05:00
github-actions[bot]
5fec816ad6 templates: bump for v3.4.0 (#9741)
Automated bump of templates for v3.4.0

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-12-04 11:10:20 -05:00
Elliot DeNolf
0ca473e6ca chore(release): v3.4.0 [skip ci] 2024-12-04 10:32:41 -05:00
Patrik
c7218c0bd7 chore: adds jsdocs for auth.forgotPassword.expiration prop (#9739)
### What

Updates auth.forgotPassword.expiration prop type to include JSDocs

I.e

```
/**
  * The number of milliseconds that the forgot password token should be valid for.
  * @default 3600000 // 1 hour
  */
```
2024-12-04 10:21:55 -05:00
Elliot DeNolf
340bc85560 docs: add output standalone in docker deployment section (#9738)
Adds details about `output: 'standalone'` to Docker deployment section.
This is required in order for Next.js to be dockerized.

```
const nextConfig = {
  output: 'standalone',
}
```
2024-12-04 14:57:02 +00:00
Patrik
9bffa098b9 feat: adds configurable expiration prop for password reset tokens (#9710)
### What?

Unable to configure expiration time for the password reset tokens.

### Why?

Prior to this change, the expiration time for password reset tokens were
defaulted.

### How?

Adds new `expiration` prop to `auth.forgotPassword` object which allows
for the option to configure the expiration time of password reset
tokens.
2024-12-04 09:43:14 -05:00
DracoBlue
d118544b44 docs: sqlite is also supported (#9690)
Sqlite is also supported.
2024-12-04 08:59:34 -05:00
Alessio Gravili
50823be9e5 fix(richtext-lexical): ensure hooks from sub-fields receive document data and doc props, and not node data and doc props (#9406)
This also adds a new `previousNode` prop to afterChange node hooks, and
makes sure that sub-fields receive the correct previousSiblingDoc.
2024-12-04 08:47:05 -05:00
Elliot DeNolf
f5aad49ba7 ci: template bump workflow (#9733)
This create a workflow that will trigger upon every release and do the
following:

- Re-generate all template lockfiles as needed (only blank and website
need them for payload cloud)
- Re-generate all postgres migrations for any pg-based template
- Commit changes
- Create PR
2024-12-03 23:06:32 -05:00
Paul
32f0f340ac fix(templates): vercel migrations (#9730) 2024-12-04 02:57:49 +00:00
Nate
2ef7de57a9 chore: fix broken links in collections.mdx (#9697)
Fix broken links.

<!--

Thank you for the PR! Please go through the checklist below and make
sure you've completed all the steps.

Please review the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository if you haven't already.

The following items will ensure that your PR is handled as smoothly as
possible:

- PR Title must follow conventional commits format. For example, `feat:
my new feature`, `fix(plugin-seo): my fix`.
- Minimal description explained as if explained to someone not
immediately familiar with the code.
- Provide before/after screenshots or code diffs if applicable.
- Link any related issues/discussions from GitHub or Discord.
- Add review comments if necessary to explain to the reviewer the logic
behind a change

### What?

Fix broken links to collections and access control overview.

### Why?

### How?

Fixes #

-->
2024-12-03 20:54:47 -06:00
Nate
0f2feacc19 docs: fix typo in Collections.mdx (#9696)
fixed small typo

<!--

Thank you for the PR! Please go through the checklist below and make
sure you've completed all the steps.

Please review the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository if you haven't already.

The following items will ensure that your PR is handled as smoothly as
possible:

- PR Title must follow conventional commits format. For example, `feat:
my new feature`, `fix(plugin-seo): my fix`.
- Minimal description explained as if explained to someone not
immediately familiar with the code.
- Provide before/after screenshots or code diffs if applicable.
- Link any related issues/discussions from GitHub or Discord.
- Add review comments if necessary to explain to the reviewer the logic
behind a change

### What?

Small typo fix in Docs

### Why?

### How?

Fixes #

-->
2024-12-03 20:54:19 -06:00
Jacob Fletcher
a53c1d5517 fix: hidden and disabled fields cause incorrect field paths (#9680) 2024-12-03 21:22:28 -05:00
Paul
fce210b9b1 feat(templates): add sitemap support to website template (#9727)
Adds sitemap support for website templates using `next-sitemap` and
dynamic sitemaps for pages and posts.
2024-12-04 02:08:55 +00:00
Paul
1bd689b7e1 feat(templates): add new favicons and og images to website template (#9716)
Add new favicons to match payload's new logo and new OG images and OG
image sizes to the website template.
2024-12-03 23:52:36 +00:00
DesertShadow
fd9a0073f1 fix(templates): website template copy code button darkmode behavior (#9715)
### What?
Makes copybutton's CopyIcon behave as expected in darkmode.

### Why?
CopyIcon inverts on its own now and has a preset size.
Therefore this wrapper div creates unwanted behavior and is not needed
anymore.
```tsx
<div className="w-6 h-6 dark:invert">
    <CopyIcon />
</div>
 ```
With div
![image](https://github.com/user-attachments/assets/9ff84539-022a-4b2a-93b8-3b0733b7a915)
Without div
![image](https://github.com/user-attachments/assets/b3a2c1e2-5690-4824-9394-fee704170f5c)
Light mode is unaffected.

### How?
Remove the wrapper div above, leaving only the CopyIcon.
2024-12-03 23:39:20 +00:00
Dan Ribbens
67179a7fb8 fix: join field description, custom components and loading state (#9703)
- [fix: join field shows loading when creating a
document](9f7a2e7936)
- [fix: join field
descriptions](90e8cdb464)
- [feat(ui): adds before & after inputs to join
field](19d43329ad)

---------

Co-authored-by: Patrik <patrik@payloadcms.com>
2024-12-03 15:58:42 -05:00
Jarrod Flesch
fbb59bab0a fix: error after view is left idle on edit views (#9709)
### What?
![CleanShot 2024-12-03 at 15 00
13](https://github.com/user-attachments/assets/63afb1cf-59ae-4ae7-8741-c98cca0134df)

### Why?
`user.id` was being used as a dependency is callbacks and when the user
was logged out due to inactivity the above error would throw.

### How?
Added optional chaining to the dependency.
2024-12-03 15:49:07 -05:00
Paul
01d574624c fix(templates): vercel website template migrations wrong adapter import (#9708) 2024-12-03 14:25:29 -06:00
Paul
4bb5e4ecbc fix(templates): vercel website template migrations wrong adapter import (#9708) 2024-12-03 19:38:27 +00:00
Paul
45bc33ba5f fix(templates): vercel website template migrations wrong adapter import (#9708) 2024-12-03 19:38:11 +00:00
Paul
8beb7c1636 fix(templates): vercel website template migrations wrong adapter import (#9708) 2024-12-03 19:37:56 +00:00
Elliot DeNolf
188466d842 chore: workflow rename 2024-12-03 14:09:58 -05:00
Paul
3f3222404f chore(templates): add initial migrations for vercel with website template (#9706)
Adds the initial migration files needed for vercel deployments to work
and updated .env examples
2024-12-03 19:09:34 +00:00
Elliot DeNolf
0c34f27291 ci: create separate post-release-templates workflow 2024-12-03 14:08:44 -05:00
Sasha
18f9f35692 fix(ui): copy to locale incorrect label when locale label is defined as object (#9705)
Fixes https://github.com/payloadcms/payload/issues/9701

### What?
Previously, when defining `localization.locales` like this:
```ts
localization: {
  defaultLocale: 'en',
  locales: [{ label: { en: 'ESLocale' }, code: 'es' },{ label: { en: 'ESLocale' }, code: 'en' }],
},
```

The title in "copy to locale" modal was displayed incorrectly
```
Copy to locale: Copying from [object Object] to [object Object]
```

### How?
Uses the `getTranslation` function to handle this behavior properly
2024-12-03 21:06:51 +02:00
Elliot DeNolf
7bd1a1632b ci: pin specific github-releases-to-discord version 2024-12-03 14:01:03 -05:00
Paul
d2007b1670 fix(templates): website remove maxDepth from references in link field (#9702)
Removes maxDepth from link fields which can cause issues sometimes
depending on how deep the reference is

Also removes bottom border on website header.
2024-12-03 18:55:20 +00:00
Paul
643c92dde7 chore: change deprecation warning for getPayloadHMR to warning (#9700)
Changes the deprecation warning to be actually a warning
2024-12-03 18:53:45 +00:00
Francisco Lourenço
92a01c26e1 chore(next): remove unused sanitizeEditViewProps fn (#9569) 2024-12-03 13:32:36 -05:00
Dan Ribbens
d047f01bfa fix: copy data from locale transaction error (#9673)
### What?
Write conflict errors can arise when collections have hooks.

### Why?
The copyDataFromLocale local API calls to update were not passsing req
so changes could be made on different transcations.

### How?
Fixed the error by passing `req` and also introduced some perf
optimizations using `depth` and disabling `joins` since that data isn't
needed for this operation.
2024-12-03 13:02:44 -05:00
Elliot DeNolf
a6ba9e3a41 chore(release): v3.3.0 [skip ci] 2024-12-03 12:53:42 -05:00
Paul
d1de106944 fix(ui): data disappearing when form state updates in globals (#9682)
Fixes a bug where data would visually disappear from the form when
merging new form stats when saving globals.
2024-12-03 15:41:21 +00:00
Dan Ribbens
6104fe5011 feat: disableLocalStrategy with auth fields still enabled (#9579)
Adds configuration options to `auth.disableLocalStrategy` to allow
customization of how payload treats an auth enabled collection.

Two new properties have been added to `disableLocalStrategy`:

- `enableFields` Include auth fields on the collection even though the
local strategy is disabled. Useful when you do not want the database or
types to vary depending on the auth configuration used.
- `optionalPassword`: makes the password field not required
2024-12-03 09:52:23 -05:00
Patrik
40f5c72e8d chore(examples): migrates form-builder example to 3.0 (#9681)
### What?

Migrates the `form-builder` example to payload `3.0`.

`Updates`:

- Now has a next app directly along side payload.

- Removes `form-builder/next-app` & `form-builder/next-pages` example
front-ends and only uses new recommended approach (i.e admin panel &
front-end on the same port - `3000`)
2024-12-03 09:47:08 -05:00
Harley Salas
cf34d3aec4 docs: add payload.auth to local api (#9632)
### What?
https://payloadcms.com/docs does not document the `payload.auth`
feature.

### Why?
With custom components, there is no explanation as to how you can get
the current user on the server side. It is also something useful to know
for many other scenarios. A Discord user even mentioned today, that they
spent quite some time, trying to figure out how to get the current user,
while on the server.

While I don't think it's the cleanest "place" to document it, I think
its what makes the most sense, given the current state of the
documentation. I tried to follow the existing format as close as
possible. The comments are longer, but I feel the information is
absolutely necessary to provide.

Confirmed that formatting works as expected and there are no errors
parsing the addition:

![image](https://github.com/user-attachments/assets/c8e203cd-3c9c-482c-850b-2a16f3958344)


Fixes #9631
2024-12-02 19:14:06 -06:00
Paul
5b3079a88e chore(scripts): fix generate template variations script (#9671) 2024-12-02 23:30:52 +00:00
Alessio Gravili
0dbfc237d3 fix: throw proper error if import handler paths are not able to be imported, improve import handler path docs (#9679)
Fixes https://github.com/payloadcms/payload/issues/9453
2024-12-02 23:26:43 +00:00
Alessio Gravili
a89d54454a fix: ensure jobs do not retry indefinitely by default, fix undefined values in error messages (#9605)
## Fix default retries

By default, if no `retries` property has been set, jobs / tasks should
not be retried. This was not the case previously, as the `maxRetries`
variable was `undefined`, causing jobs to retry endlessly. This PR sets
them to `0` by default.

Additionally, this fixes some undesirable behavior of the workflow
retries property. Workflow retries now act as **maximum**,
workflow-level retries. Only tasks that do not have a retry property set
will inherit the workflow-level retries.

## Fix error messages

Previously, you were able to encounter error messages with undefined
values like these:

![CleanShot 2024-11-28 at 15 23
37@2x](https://github.com/user-attachments/assets/81617ca8-11de-4d35-b9bf-cc6c5bc515be)

Reason is that it was always using `job.workflowSlug` for the error
messages. However, if you queue a task directly, without a workflow,
`job.workflowSlug` is undefined and `job.taskSlug` should be used
instead.

This PR then gets rid of the second undefined value by ensuring that
`maxRetries´ is never undefined
2024-12-02 22:05:48 +00:00
Elliot DeNolf
e4c3c5b1d2 ci: allow more commit types in release notes (#9677)
More commit types will now show in the release notes. The full list of
allowable types are the following and will show in order:

```ts
const commitTypesForChangelog = [
  'feat',
  'fix',
  'perf',
  'refactor',
  'docs',
  'style',
  'test',
  'templates',
  'examples',
  'build',
  'ci',
  'chore',
]
  ```
2024-12-02 15:59:52 -05:00
Patrik
58b7415385 feat(ui): adds beforeInput & afterInput props for arrays, blocks, collapsible, group, radio, & relationship fields. (#9674)
The following fields did not have the ability to add beforeInput &
afterInput:
- Arrays
- Blocks
- Collapsibles
- Groups
- Radios
- Relationships

### How?

Adds the ability to define and add before and after inputs to these
fields in your config.

Here are examples of the above fields with beforeInputs & afterInputs
defined (I.e. `#before-input` & `#after-input`):

`Arrays`:
![Screenshot 2024-12-02 at 1 22
15 PM](https://github.com/user-attachments/assets/8a2d5351-acab-4b28-b9bd-b19e1942da7e)


`Blocks`:
![Screenshot 2024-12-02 at 1 35
53 PM](https://github.com/user-attachments/assets/e9b5f22b-2671-4efb-8ee4-b4de3a1addc5)


`Collapsible`:
![Screenshot 2024-12-02 at 1 46
34 PM](https://github.com/user-attachments/assets/38f7a016-8a79-4ece-90d2-0b1761db19cd)


`Groups`:
![Screenshot 2024-12-02 at 1 52
34 PM](https://github.com/user-attachments/assets/dc6a123b-ee01-4021-bbbf-ac1b8c086072)


`Radios`:
![Screenshot 2024-12-02 at 1 59
35 PM](https://github.com/user-attachments/assets/4ddd16a1-2ad2-44f5-a7e9-86aa6e4e947b)


`Relationships`:
![Screenshot 2024-12-02 at 1 21
55 PM](https://github.com/user-attachments/assets/b4679baf-6157-41c9-8485-ca570be979d2)
2024-12-02 19:37:16 +00:00
Dan Ribbens
631edd4c17 fix: latest: true version disappear on parallel writes (#9032)
What?
Fixes issue when on parallel writes in result you can have 0 latest:
true versions.

Why?
There must be always a version with latest: true

How?
Ensures that we always have a version with latest: true by adding a
filter on createdAt < createdVersion.createdAt.
Instead, this ponentially can lead to a situation where we have 2
versions with latest: true, if they were created at the exact same time,
but this shouldn't happen in a real world scenario and it's much less
problematic than not having a version with latest: true.

Fixes https://github.com/payloadcms/payload/issues/5895

Changes from #8986

---------

Co-authored-by: Sasha <64744993+r1tsuu@users.noreply.github.com>
2024-12-02 14:34:45 -05:00
Tobias Arends
c5fe021570 fix: duplicate afterRead collection hook call on loginOperation (#9664)
Hi there,

i noticed that the `afterRead` hook is triggered twice on
`loginOperation`.

This just removes the duplicated code.

-Tobi
2024-12-02 14:07:15 -05:00
Jacob Fletcher
edc04aec56 refactor: deprecates params and search params contexts (#9581)
As described in #9576, the `SearchParamsProvider` can become stale when
navigating routes and relying on search params during initial render.
This is because this context, along with the `ParamsProvider`, is
duplicative to the internal lifecycle of `useSearchParams` and
`useParams` from `next/navigation`– but always one render behind.
Instead, we need to use the hooks directly from `next/navigation` as
described in the jsdocs. This will also remove any abstraction over top
the web standard for `URLSearchParams`.

For this reason, these providers and their corresponding hooks have been
marked with the deprecated flag and will continue to behave as they do
now, but will be removed in the next major release. This PR replaces all
internal reliance on these hooks with `next/navigation` as suggested,
except for the `useParams` hook, which was never used in the first
place.

```diff
'use client'
- import { useSearchParams } from '@payloadcms/ui'
+ import { useSearchParams } from 'next/navigation'
+ import { parseSearchParams } from '@payloadcms/ui'

export function MyClientComponent() {
- const { searchParams } = useSearchParams()
+ const searchParams = useSearchParams()
+ const parsedParams = parseSearchParams(searchParams)

  // ...
}
```
_MyClientComponent.tsx_
2024-12-02 13:57:59 -05:00
Jacob Fletcher
d04cea123c fix(ui): properly animates height for dynamically rendered children (#9665)
Fixes https://github.com/payloadcms/payload/issues/9567. When using the
`AnimateHeight` component on a patched browser such as Webkit,
components with dynamically rendered children are not properly animating
in, such as blocks with rich text. This is because the height of that
content is unable to be calculated before it's rendered, preventing the
component from acquiring a target height to animate toward. This change
was originally introduced in #9456 in effort to remove unnecessary
dependencies.

The fix is to setup a ResizeObserver during animation to watch for
changes to the content's height. This way, as components dynamically
render in based on the "open" state, the hook will simply increment the
target height accordingly.
2024-12-02 13:18:16 -05:00
Alessio Gravili
24c75b09c0 fix(ui): upgrade sonner from 1.5.0 to 1.7.0 (#9666)
This fixes React 19 peer dependency warnings for sonner
2024-12-02 18:08:22 +00:00
Alessio Gravili
877b89962e chore: upgrade all dependencies used to build payload (swc, esbuild, react compiler, babel) (#9658) 2024-12-02 09:07:48 -07:00
Alessio Gravili
71ba4a8bce chore: upgrade all react and next-related packages (#9655)
This PR updates all react and next-related packages to the latest
version in our test directory and in our templates, while still allowing
older versions to be used.

Additionally, this ensures that the "scheduler" package version we
install matches the version installed by react-dom
2024-12-02 00:48:39 -07:00
Alessio Gravili
9d724086c7 feat(ui): upgrade react-datepicker dependency from 6.2.0 to 7.5.0 (#9654)
The new version comes with included types (we can uninstall
`@types/react-datepicker`!), removes 1 dependency and adds official
support for React 19
2024-12-02 05:53:12 +00:00
Alessio Gravili
0b445c8013 ci: add back CI-level automatic retrying for failing int and e2e tests (#9652)
We should fix all the flaky tests in the future - in the meantime this
PR will reduce collectively wasted engineer hours, as we now don't have
to manually open the awkward GH actions UI and press the retry button -
often multiple times for each PR.

It may not be enough to simply retry the test:int / test:e2e commands to
get the tests not to flake for the next run, but let's see how this goes
2024-12-01 22:46:26 -07:00
Alessio Gravili
73e0e25e14 fix(ui): upgrade react-select, fixes type issues with select input (#9653)
React-select fixed React 19 type compatibility here:
https://github.com/JedWatson/react-select/pull/5974
2024-12-01 21:35:03 -07:00
Alessio Gravili
f07679745a fix: job task output data was not fetched and restored, if task failed previously (#9651)
Assume you had the following workflow:

```ts
 handler: async ({ job, inlineTask, req }) => {
          const { customerData } = await inlineTask('Fetch Customer Data', {
            task: ({ req }) => {
              if (Math.random() < 0.2) {
                throw new Error('Failed on purpose')
              }
              return {
                output: {
                  customerData: 'test',
                },
              }
            },
            retries: {
              attempts: 40,
            },
          })
          
          console.log('customer Data', customerData)

          await inlineTask('Analyze Segments', {
          // Rest of task...
```

It was possible for the following to happen:

Run attempt 1:

- Task "Fetch Customer Data" fails
- Task is added to job log without output data and state "failed"

Run attempt 2:

- Task "Fetch Customer Data" succeeds
- Task is added to job log with correct output data and state
"succeeded"
- Task "Analyze Segments" fails
- Task is added to job log without output data and state "failed"

Run attempt 3:

- Task "Fetch Customer Data" has already run successfully => restore
from DB
- Task "Analyze Segments" fails because input data is undefined.

The restoration of the already-succeeded "Fetch Customer Data" task did
not fetch and restore the correct output data, as it was taking the
output data from the previously failed task that did not save any, even
though it should have been taking and restoring the output data of the
last-run, successful task run.

This PR fixed that
2024-12-02 03:49:31 +00:00
Alessio Gravili
963387f7b0 feat(ui): export CodeEditor (#9650)
Requirement for https://github.com/payloadcms/payload/pull/9645
2024-12-02 03:19:26 +00:00
Alessio Gravili
4cd31ce343 fix: prevent workflow destructuring errors for failing tasks (#9649)
When destructuring the output of task in a job workflow handler, there
is a chance of a destructuring error being thrown if the task fails
2024-12-02 02:31:17 +00:00
Alessio Gravili
f151723745 feat(ui): export TabsProvider and TabComponent (#9647)
Requirement for https://github.com/payloadcms/payload/pull/9645
2024-12-02 02:28:13 +00:00
Said Akhrarov
2b4522b1db fix(translations): changes rs and rs-Latin dateFNSkey to proper locale instead of en-US (#9621)
### What?
Adds Serbian `rs` and `rs-Latin` to `importDateFNSLocale` as well as
changes their `dateFNSKey` in the language definition to the appropriate
key instead of `en-US`

### Why?
To support Serbian language with appropriately localized dates.

### How?
Minor changes in translations package.
2024-12-01 21:09:56 +00:00
Alessio Gravili
e6ea68e848 fix: ensure job errors are saved in payload (#9644)
Previously, job errors were not saved in payload
2024-12-01 20:57:14 +00:00
Elliot DeNolf
1e8c9d3a09 docs: add upgrade from previous beta section to migration guide (#9642) 2024-12-01 11:25:44 -05:00
Elliot DeNolf
fdfa07b863 feat(examples): add custom server example [skip-lint] (#9641)
Example using [Next.js Custom
Server](https://nextjs.org/docs/pages/building-your-application/configuring/custom-server)
with express.

Made from official
[examples/custom-server](https://github.com/vercel/next.js/tree/canary/examples/custom-server)
from Next.js repository.
2024-12-01 11:11:47 -05:00
Elliot DeNolf
519752f152 feat: export built-in field validations (#9622)
Export the built-in field validations that Payload uses.
2024-11-29 23:13:42 -05:00
Riley Pearce
2d2e7d50f0 fix(next): remove keyboard focus for closed nav items (#9558)
Fixes: #9610.

### What?

Currently some links inside the main nav are still focusable with a
keyboard when the main nav is closed.

### Why?

This leads to the active keyboard focus getting lost until it eventually
finds its way to the hamburger menu button. It can also lead to links
that are not currently visible being selected accidentally.

### How?

When the [inert
attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/inert)
is set to `true`, we can prevent focus on any child elements
automatically. We simply toggle the attribute on or off based on whether
the nav is open or closed.

The inert attribute has [great
compatibility](https://caniuse.com/mdn-html_global_attributes_inert)
with modern browsers these days, making it a solid choice to resolve
this issue.

### Recordings

#### Before

You can see down the bottom left of the screen that links available in
the main nav are still focusable even when the main nav is closed.


https://github.com/user-attachments/assets/e16d5336-7d2b-42f1-886b-cfa3ed82dbb1

#### After

You can see that focus is immediately moved to the hamburger menu when
the main nav is closed.


https://github.com/user-attachments/assets/8c81197a-53aa-4af1-8e5c-f6835ba955a5
2024-11-29 13:58:40 -07:00
Alessio Gravili
030b759529 fix(richtext-lexical): ensure lexical doesn't break if used without react installed (#9620) 2024-11-29 20:54:38 +00:00
Jacob Fletcher
3d1a0656d2 fix(live-preview): populates localized relationships in client-side live preview (#9617)
Fixes #5026. When using client-side Live Preview, switching locale would
not populate relationships in that locale, and would use the default
locale instead. This was because locale was simply not being handled.
Now, we pass the locale through the event, and use it to make localized
queries when populating those relationships.
2024-11-29 15:41:39 -05:00
Yunsup Sim
9892303f1f fix(richtext-lexical): add form id to drawSlug (#9613)
Fixes #9532 

The error occurs because the drawerSlug declaration in
`blocks/client/component` contains `formData.id`, while
`blocks/client/componentInline` does not. So I add `formData.id`.

### Before



https://github.com/user-attachments/assets/14247634-ab60-437f-8986-c76cafec5c09


### After


https://github.com/user-attachments/assets/b26bc1ca-b071-4bf5-a9f0-fda4e9282538
2024-11-29 20:40:35 +00:00
Elliot DeNolf
0716128b3b chore: add contributor count badge to readme 2024-11-29 14:40:34 -05:00
Sasha
bc2d7c91e0 docs: fix links to /access-control/overview (#9619)
Fixes links to **Access Control** like here
https://payloadcms.com/docs/access-control/fields
2024-11-29 21:40:03 +02:00
Sasha
b8965077b8 fix(plugin-search): delete proper search document when doc has the same value but different relationTo (#9616)
Fixes https://github.com/payloadcms/payload/issues/9612

Previously, the plugin search with different collections but the same
IDs could delete a wrong search document on synchronization, because we
queried the search document only by `doc.value`. Instead, we should also
query by `doc.relationTo`.
2024-11-29 21:25:26 +02:00
Vasileios Drosatos
aa5dd8a77f docs: adds missing comma to example config in localization.mdx (#9618) 2024-11-29 19:02:39 +00:00
Elliot DeNolf
702df1f5da ci: add github-releases-to-discord 2024-11-29 13:57:37 -05:00
Elliot DeNolf
f150a68f56 ci: small tweaks to post-release 2024-11-29 13:57:25 -05:00
Elliot DeNolf
07e40d37ac chore(release): v3.2.2 [skip ci] 2024-11-29 12:59:59 -05:00
Alessio Gravili
c4327f2294 fix(ui): ensure UI is reactive to HMR changes, without having to refresh the page (#9602)
**Before:**

https://github.com/user-attachments/assets/c4ad9184-4629-419d-a30e-68dd802da387


**After:**

https://github.com/user-attachments/assets/4f7bcd4a-e1a7-464e-8036-55f48c1d5da1
2024-11-28 14:55:41 -07:00
Sasha
61a51ca02a fix(plugin-sentry): capture non APIError errors to sentry (#9595)
Previously, non Payload errors (that aren't `APIError`, for example from
`throw new Error()`) weren't captured to Sentry
2024-11-28 23:53:32 +02:00
Marwin Hormiz
27eeac2568 fix: add generic to LabelFunction to prevent type error for custom translation keys (#9335)
When creating custom translations and merging the custom translation
keys with the default translation keys, and then pass it to the generic
TFunction type, Typescript complains that the function does not satisfy
the LabelFunction type in a label field.

The reason for this is that the LabelFunction type is not generic, and
it's always using the default TFunction which itself uses the
DefaultTranslationKey if no type is passed to it.

This is solved by making the LabelFunction generic and forward the
TTranslationKeys to the TFunction type.

Following this documentation:
https://payloadcms.com/docs/configuration/i18n#typescript 

Example:

![image](https://github.com/user-attachments/assets/583ae9ca-63e7-4f49-9ded-0fca0a4a3d80)
2024-11-28 21:48:49 +00:00
Chris Aprea
3e4f7dbae2 docs: update the "More details" URLs in email.mdx (#9479)
Updates the "More details" link URLs in the generateEmailHTML and
generateEmailSubject rows to link to the correct element.

The links current use camelcase but the corresponding element IDs are
lowercase.

See this page: https://payloadcms.com/docs/authentication/email
2024-11-28 21:17:30 +00:00
Said Akhrarov
36c9a19b0e docs: fix names in examples for formBuilder and nestedDocs plugins (#9600)
### What?
The examples in nestedDocs and formBuilder plugins were referencing the
plugins incorrectly.

### Why?
To prevent confusion for readers.

### How?
Changes to `docs/plugins/form-builder.mdx` and
`docs/plugins/nested-docs.mdx`.
2024-11-28 14:15:36 -07:00
Arne Wiese
b5ddc328ef docs: fix typo in postgres docs (#9585)
This PR fixes a typo in the postgres docs

```
- disale
+ disable
```
2024-11-28 21:13:28 +00:00
Alessio Gravili
36a6a19de8 fix(ui): css is not defined error in production build (#9603)
Related: https://github.com/payloadcms/payload/pull/9456
Fixes https://github.com/payloadcms/payload/issues/9598

This ensures that the `CSS` property is accessed safely, preventing the
"CSS is not defined" error
2024-11-28 21:00:23 +00:00
Alessio Gravili
3da9be00bc fix: do not send admin dependencies to client (#9583)
We were sending unrendered `PayloadComponent`s to the client, which is a
remnant of old betas where those were actually rendered.

There is no point sending them to the client as they are useless there
and cannot be rendered without the server-only importMap. Additionally,
this could have potentially caused server-only modules to be sent to the
client (e.g. if serverProps was used), which would have lead to a
webpack error.

The types were also incorrect, as admin.dependencies on the ClientConfig
did not contain the React nodes.
2024-11-27 23:51:57 +00:00
Jarrod Flesch
4b302f22a0 fix: incorrect formState after doc save (#9573)
### What?
When the document is saved the formState was not being reset from the
server.

### Why?
getFormState was not being called onSuccess of the form submission

### How?
The `Form` onSuccess function now allows for an optional return type of
`FormState` if the functions returns formState then we check to see if
that differs from the current formState on the client. If it does then
we dispatch the `REPLACE_STATE` action with the newState.

Fixes https://github.com/payloadcms/payload/issues/9423
2024-11-27 16:45:10 -05:00
Alessio Gravili
c7138b9aab chore: update generated types for all test suites (#9577) 2024-11-27 20:36:37 +00:00
Riley Pearce
3c35d81fe5 fix(richtext-lexical): allow exiting the RTE with the keyboard in Firefox (#8654)
Closes #8653.

Originally this PR was for making the `IndentFeature` opt-in instead of
opt-out, which would have been a breaking change. After some discussion
it was determined it would be better if we could keep the
`IndentFeature` by default and instead come up with a custom escape key
solution to prevent keyboard users from becoming trapped in the editor.

These changes are my interpretation of how we can solve this problem in
a way that feels natural for a keyboard user. When a keyboard user
becomes trapped, the usual approach is to press the escape key (e.g.
modals) to be able to leave the current context and continue navigating.
These changes allow that to happen while minimising the cognitive load
by not needing to remember whether the `IndentFeature` is toggled on or
off.

I've also ensured the `IndentFeature` can actually be turned off if
consciously removed from the lexical editor features (previously it was
still enabled even if it was removed).

Ideally this should be handled on the lexical side in the
`TabIndentationPlugin` itself (I will begin to look into the feasibility
of this), but for now this should be suitable to ensure the experience
for keyboard users isn't completely blocked (there are a number of other
improvements that could be made but I will create more specific issues
for those).

Open to discussion and amendments. Once we're aligned on the approach
I'm happy to implement tests as needed.

### Before


https://github.com/user-attachments/assets/95183bb6-f36e-4b44-8c3b-d880c822d315

### After


https://github.com/user-attachments/assets/d34be50a-8f31-4b81-83d1-236d5ce9d8b5

---------

Co-authored-by: Germán Jabloñski <43938777+GermanJablo@users.noreply.github.com>
2024-11-27 16:53:19 -03:00
Jacob Fletcher
3961223cc1 fix(ui): retains search params when navigating back (#9576)
Closes #9132. When query params are present in the URL, such as after
searching or filtering in the list view, they are not being retained
after navigating back to that view via `history.back()` (i.e. the back
button). This makes it difficult to quickly navigate in and out of
documents from the list view when an underlying search exists. This was
because the `SearchParamsProvider` is stale when the new view renders,
which then replaces the URL with these stale params. The fix here is to
_not_ use the `SearchParamsProvider` at all, and instead use
`next/navigation` directly. Ultimately, this provider should likely be
marked deprecated and then removed in the next major release for this
very reason.
2024-11-27 14:49:53 -05:00
Sasha
303227363b chore(deps): bump react-error-boundary to 4.1.2 (#9575)
Bumps `react-error-boundary` as the current version breaks `pnpm != 9`
versions
8f48596c67
2024-11-27 18:42:24 +00:00
Alessio Gravili
ca07c9f0d7 fix(ui): ensure Form submit button only uses onClick handler when needed, as that was causing issues with password manager extensions (#9572) 2024-11-27 17:50:26 +00:00
Alessio Gravili
17c79454e3 fix(richtext-lexical): various JSX converter issues (#9570) 2024-11-27 10:19:34 -07:00
Sasha
4a854f86ae chore(examples): multi-tenant compatible with postgres ID check (#9568)
Fixes https://github.com/payloadcms/payload/issues/9565 multi-tenant
compatibility with Postgres (and custom IDs)

Adds a useful `extractID` utility:
```ts
import { Config } from '@/payload-types'
import type { CollectionSlug } from 'payload'

export const extractID = <T extends Config['collections'][CollectionSlug]>(
  objectOrID: T | T['id'],
): T['id'] => {
  if (objectOrID && typeof objectOrID === 'object') return objectOrID.id

  return objectOrID
}
```
2024-11-27 11:27:44 -05:00
Alessio Gravili
519bb790d8 feat(richtext-lexical): fully-typed blocks in JSX serializer (#9554)
This allows for full type-safety when using our official JSX converter
with blocks:

![CleanShot 2024-11-26 at 21 35
18@2x](https://github.com/user-attachments/assets/70ceb3e9-d5d1-4074-a5dd-bb9d514dc229)

![CleanShot 2024-11-26 at 21 35
25@2x](https://github.com/user-attachments/assets/5100133c-8a91-4cfe-8e44-c091b2d86ffa)
2024-11-26 22:56:22 -07:00
Elliot DeNolf
b47ebb6550 chore(templates): bump lock 3.2.1 (#9553) 2024-11-26 22:51:28 -05:00
Elliot DeNolf
be59d52ac7 chore(release): v3.2.1 [skip ci] 2024-11-26 22:29:29 -05:00
Elliot DeNolf
6af4deefc2 fix(plugin-search): missing copyfiles script, incorrect scss import (#9552)
.tsx files were introduced into the plugin-search package, but an
appropriate `copyfiles` script was not introduced to get these files
into the dist output.

This was causing a `Module not found: Can't resolve './index.scss'`
error on build.

---------

Co-authored-by: Alessio Gravili <alessio@gravili.de>
2024-11-26 20:28:07 -07:00
Elliot DeNolf
072f1efb41 chore(templates): bump lock 3.2 (#9551) 2024-11-26 21:22:29 -05:00
Elliot DeNolf
c8bee29920 chore(release): v3.2.0 [skip ci] 2024-11-26 20:39:41 -05:00
Jarrod Flesch
2248be4a2e fix: utilizes override access false (#9550)
### What?
overrideAccess was missing on local operations inside the server
function for copy locale.

### How?
Adds overrideAccess: false
2024-11-26 20:29:16 -05:00
Anthony Bouch
21db058e2e feat(richtext-lexical): export hasText helper (#9484)
### What?

Extracted `hasText` helper method in `richTextValidateHOC`

### Why?

The new exported `hasText` helper method can now also be used during
front-end serialization - for example, to check whether a caption
element should be rendered when text is optional and therefore possibly
empty (which would allow us to prevent rendering an empty caption
element).
2024-11-26 18:22:16 -07:00
Germán Jabloñski
90f893a558 fix(richtext-lexical): prevent use of text formats whose features were not enabled (#9507)
Fixes #8811

Before this PR, even if you did not include text formatting features
(such as BoldFeature, ItalicFeature, etc), it was possible to apply that
formatting by (a) pasting content from the clipboard and (b) using
keyboard shortcuts.

This PR fixes that by requiring the formatting features to be registered
so that they can be inserted in the editor.
2024-11-26 23:50:24 +00:00
Jacob Fletcher
5d18a5288e fix: overrides entity visibility within drawers (#9546)
When using the `admin.hidden: true` property on a collection, it
rightfully removes all navigation and routing for that particular
collection. However, this also affects the expected behavior of hidden
entities when they are rendered within a drawer, such as the document
drawer or list drawer. For example, when creating a new _admin.hidden_
document through the relationship or join field, the drawer should still
render the view, despite the underlying route for that view being
disabled. This change was a result of the introduction of on-demand
server components in #8364, where we now make a server roundtrip to
render the view in its entirety, which include the logic that redirects
these hidden entities.

Now, we pass a new `overrideEntityVisibility` argument through the
server function that, when true, skips this step. This way documents can
continue to respect `admin.hidden` while also having the ability to
override on a case-by-case basis throughout the UI.
2024-11-26 18:22:04 -05:00
Said Akhrarov
defa13e4fe feat(plugin-search): added support for reindexing collections on demand (#9391)
### What?
This PR aims to add reindexing capabilities to `plugin-search` to allow
users to reindex entire searchable collections on demand.

### Why?
As it stands, end users must either perform document reindexing manually
one-by-one or via bulk operations. Both of these approaches are
undesirable because they result in new versions being published on
existing documents. Consider the case when `plugin-search` is only added
_after_ the project has started and documents have been added to
existing collections. It would be nice if users could simply click a
button, choose the searchable collections to reindex, and have the
custom endpoint handle the rest.

### How?
This PR adds on to the existing plugin configuration, creating a custom
endpoint and a custom `beforeListTable` component in the form of a popup
button. Upon clicking the button, a dropdown/popup is opened with
options to select which collection to reindex, as well as a useful `All
Collections` option to run reindexing on all configured search
collections. It also adds a `reindexBatchSize` option in the config to
allow users to specify in what quantity to batch documents to sync with
search.

Big shoutout to @paulpopus & @r1tsuu for the triple-A level support on
this one!

Fixes #8902 

See it in action:


https://github.com/user-attachments/assets/ee8dd68c-ea89-49cd-adc3-151973eea28b

Notes:
- Traditionally these kinds of long-running tasks would be better suited
for a job. However, given how many users enjoy deploying to serverless
environments, it would be problematic to offer this feature exclusive to
jobs queues. I thought a significant amount about this and decided it
would be best to ship the feature as-is with the intention of creating
an opt-in method to use job queues in the future if/when this gets
merged.
- In my testing, the collection description somehow started to appear in
the document views after the on-demand RSC merge. I haven't reproduced
this, but this PR has an example of that problem. Super strange.

---------

Co-authored-by: Sasha <64744993+r1tsuu@users.noreply.github.com>
Co-authored-by: Paul Popus <paul@nouance.io>
2024-11-26 23:14:31 +00:00
Alessio Gravili
bffd98f019 feat(richtext-lexical): lexical => JSX converter (#8795)
Example:

```tsx
import React from 'react'
import {
  type JSXConvertersFunction,
  RichText,
} from '@payloadcms/richtext-lexical/react'

const jsxConverters: JSXConvertersFunction = ({ defaultConverters }) => ({
  ...defaultConverters,
  blocks: {
      // myTextBlock is the slug of the block
      myTextBlock: ({ node }) => <div style={{ backgroundColor: 'red' }}>{node.fields.text}</div>,
   },
})

export const MyComponent = ({ lexicalContent }) => {
  return (
    <RichText
      converters={jsxConverters}
      data={data.lexicalWithBlocks as SerializedEditorState}
    />
  )
}
```
2024-11-26 22:40:24 +00:00
Jessica Chowdhury
601759d967 feat: adds ability to copy data from across locales (#8203)
### What?

Adds ability to copy data from one locale to another at a document
level.

### How?

For any localized collection, you will find a new option in the document
controls called `Copy to Locale`.

This option will open a drawer, from here you can select your origin and
destination locales.

If data already exists in the destination locale, you can choose to: 
1. Overwrite this data (this will copy any empty fields in your origin
locale)
2. Not overwrite existing data (this will only copy data into empty
fields in the destination locale)

---------

Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com>
2024-11-26 17:06:30 -05:00
Elliot DeNolf
61d6614746 chore(eslint): ignore examples dir 2024-11-26 17:02:07 -05:00
Patrik
f8b7e3eb14 chore(examples): removes external draft-preview examples (#9545)
In an effort to keep the Examples Directory as easy to navigate as
possible, and to keep the Payload Monorepo only as verbose as it needs
to be, we need to remove all alternatives from the Examples Directory.
This includes setups that interact with Payload from a standalone
server, keeping only the Payload recommended "combined" Next.js +
Payload setups.
2024-11-26 16:59:16 -05:00
Patrik
1a8ac74df7 chore(examples): migrates live-preview example to 3.0 (#9538)
### What?

Migrates the `live-preview` example to payload `3.0`.

`Updates`:

- `live-preview/payload` now has a next app directly along side payload.

- Removes `live-preview/next-app` & `live-preview/next-pages` example
front-ends and only uses new recommended approach (i.e admin panel &
front-end on the same port - `3000`)
2024-11-26 16:59:06 -05:00
Alessio Gravili
fd0ff51296 perf: faster page navigation by speeding up createClientConfig, speed up version fetching, speed up lexical init. Up to 100x faster (#9457)
If you had a lot of fields and collections, createClientConfig would be
extremely slow, as it was copying a lot of memory. In my test config
with a lot of fields and collections, it took 4 seconds(!!).

And not only that, it also ran between every single page navigation.

This PR significantly speeds up the createClientConfig function. In my
test config, its execution speed went from 4 seconds to 50 ms.
Additionally, createClientConfig is now properly cached in both dev &
prod. It no longer runs between every single page navigation. Even if
you trigger a full page reload, createClientConfig will be cached and
not run again. Despite that, HMR remains fully-functional.

This will make payload feel noticeably faster for large configs -
especially if it contains a lot of richtext fields, as it was previously
deep-copying the relatively large richText editor configs over and over
again.

## Before - 40 sec navigation speed

https://github.com/user-attachments/assets/fe6b707a-459b-44c6-982a-b277f6cbb73f

## After - 1 sec navigation speed

https://github.com/user-attachments/assets/384fba63-dc32-4396-b3c2-0353fcac6639

## Todo

- [x] Implement ClientSchemaMap and cache it, to remove
createClientField call in our form state endpoint
- [x] Enable schemaMap caching for dev
- [x] Cache lexical clientField generation, or add it to the parent
clientConfig

## Lexical changes

Red: old / removed
Green: new

![CleanShot 2024-11-22 at 21 07
41@2x](https://github.com/user-attachments/assets/f8321218-763c-4120-9353-076c381f33fb)

### Speed up version queries

This PR comes with performance optimizations for fetching versions
before a document is loaded. Not only does it use the new select API to
limit the fields it queries, it also completely skips a database query
if the current document is published.

### Speed up lexical init

Removes a bunch of unnecessary deep copying of lexical objects which
caused higher memory usage and slower load times. Additionally, the
lexical default config sanitization now happens less often.
2024-11-26 14:31:14 -07:00
Jarrod Flesch
67a9d669b6 fix: allows for emails to be non unique when allowEmailLogin is false (#9541)
### What?
When you prevent users from authenticating with their email, we should
not enforce uniqueness on the email field.

### Why?
We never set the unique property to false.

### How?
Set the unique property to false if `loginWithUsername.allowEmailLogin`
is `false`.
2024-11-26 16:18:10 -05:00
Jacob Fletcher
f19053e049 fix(next): properly threads field permissions through versions diff (#9543)
The version diff view at
`/admin/collections/:collection/:id/versions/:version` was not properly
displaying diffs for iterable fields, such as blocks. There were two
main things wrong here:

1. Fields not properly inheriting parent permissions based on the new
sanitized permissions pattern in #7335
1. The diff components were expecting `permissions` but receiving
`fieldPermissions`. This was not picked up by TS because of our use of
dynamic keys when choosing which component to render for that particular
field. We should change this in the future to use a switch case that
explicitly renders each diff component. This way props are strictly
typed.
2024-11-26 19:33:10 +00:00
Jacob Fletcher
b61622019e chore(examples): removes external auth examples (#8605)
In effort to keep the Examples Directory as easy to navigate as
possible, and to keep the Payload Monorepo only as verbose as it needs
to be, we need to remove all alternatives from the Examples Directory.
This includes setups that interact with Payload from a standalone
server, keeping only the Payload recommended "combined" Next.js +
Payload setups. This will also be applied to all other examples that use
this setup, i.e. draft preview, live preview, etc.
2024-11-26 19:07:56 +00:00
Elliot DeNolf
bb648b3b5f chore(eslint): disable no-direct-set-state-in-use-effect (#9542)
Disables
[`@eslint-react/hooks-extra/no-direct-set-state-in-use-effect`](https://eslint-react.xyz/docs/rules/hooks-extra-no-direct-set-state-in-use-effect)
2024-11-26 13:25:37 -05:00
Paul
dac42ff1bd fix(templates): website priority with loading lazy on medium hero error (#9537)
Fixes a minor error thrown by Next
2024-11-26 17:07:49 +00:00
Sasha
a9f511d540 fix: skip validation of where query paths from access result (#9349)
### What?

Previously, `payload.findByID` with `overrideAccess: false` and this
collection config
```ts
{
  slug: 'fields-and-top-access',
  access: {
    read: () => ({
      secret: {
        equals: '12345',
      },
    }),
  },
  fields: [
    {
      type: 'text',
      name: 'secret',
      access: { read: () => false },
    },
  ],
},
```

Led to the `The following path cannot be queried: secret` error because
`where` input to `validateQueryPaths` also includes the result from
access control, which shouldn't be.

This works when using `payload.find`.

The same applies to find with drafts / joins `where`. We need to
validate only user `where` input, not access control that we defined in
our config.

Also, this exact logic seems be used in `find` without drafts - we don't
use `fullWhere` here but `where`, that's why this error isn't being
thrown with `find` but only `findByID`.

d9c6288cb2/packages/payload/src/collections/operations/find.ts (L134)

d9c6288cb2/packages/payload/src/collections/operations/find.ts (L166-L171)

Fixes https://github.com/payloadcms/payload/issues/9210
2024-11-26 19:02:45 +02:00
Jarrod Flesch
44c0cdb75f fix(plugin-seo): enforce readonly on the client (#9536)
### What?
Plugin-seo fields were not set to disabled when a user did not have
permissions.

### Why?
The `readOnly` property was not being used.

### How?
Uses the `readOnly` property to disable buttons and set inputs to
readOnly.
2024-11-26 10:50:21 -05:00
Harley Salas
910b68154e fix: corrects localizer positioning in RTL mode (#9494)
### What?
Localizer's RTL code was not being applied, to shift it's position to
the left side of the screen when the user's account/admin locale was set
to a RTL language.

Before:

![before](https://github.com/user-attachments/assets/54a96087-2b40-46ed-a55b-2b08438e9b84)

After:

![after](https://github.com/user-attachments/assets/4ad6e3f0-b229-4446-8d61-c0a4034bd520)


### How?
Moved css class to ensure existing css code did not face issues with
inheritance.

Fixes #9482
2024-11-26 09:39:25 -05:00
Elliot DeNolf
4c4eb2ae2b chore: some strictNullChecks mitigation (#9528) 2024-11-26 09:04:06 -05:00
Paul
71c2f63722 fix(plugin-form-builder): allow overrides to the payment fields group (#9522)
Co-authored-by: mikecebul <mikecebul@gmail.com>
2024-11-25 22:40:34 -06:00
Said Akhrarov
4e3be4414b docs: fix link for storage-uploadthing (#9527)
### What?
Fixes a link to the `storage-uploadthing` adapter in Github.

### Why?
To link readers to the correct package location.

### How?
Change to `docs/upload/storage-adapters.mdx`.

Credit to rik in Discord for the catch.
2024-11-26 03:46:25 +00:00
Patrik
d0af8e8d06 chore(examples): migrates draft-preview example to 3.0 (#9362) 2024-11-25 22:13:15 -05:00
Paul
82145f7bb0 fix(ui): remove overflow hidden from app-header wrappers since it breaks any popout elements (#9525)
Unblocks https://github.com/payloadcms/payload/pull/9391

It was previously impossible to create popout, dropdown or other menus
from buttons added to the app-header component
2024-11-25 18:26:54 -06:00
Jacob Fletcher
0757e06e71 feat: deprecates react-animate-height in favor of native css (#9456)
Deprecates `react-animate-height` in favor of native CSS, specifically
the `interpolate-size: allow-keywords;` property which can be used to
animate to `height: auto`—the primary reason this package exists. This
is one less dependency in our `node_modules`. Tried to replicate the
current DOM structure, class names, and API of `react-animate-height`
for best compatibility.

Note that this CSS property is experimental BUT this PR includes a patch
for browsers without native support. Once full support is reached, the
patch can be safely removed.
2024-11-25 17:48:16 -05:00
Elliot DeNolf
058bd02ebd chore(release): v3.1.1 [skip ci] 2024-11-25 16:42:21 -05:00
Tylan Davis
aa26312b96 docs: adds custom anchor tags to docs with duplicate headings (#9521)
### What?
Adds custom anchor tags to docs where duplicate headings exist.

### Why?
Anchor links would not correctly navigate to the proper point on the
page if there were multiple headings with the same string.

### How?
The website now supports adding custom `#anchor` to a heading in
markdown that will attach to the headings and table of content list
items. This PR adds custom anchors to the docs that have duplicate
headings.

**Example:**
```md
/docs/upload/storage-adapters.mdx

### Usage#vercel-blob-installation
```
Generates the path:
`/docs/upload/storage-adapters#vercel-blob-installation`
2024-11-25 16:31:08 -05:00
Sasha
b5f89d5199 fix(db-mongodb): sanitizeRelationshipIDs named tabs within tabs (#9400)
Ensures `sanitizeRelationshipIDs` works properly in any case
Updates predefinedMigration to work with new globals
Skips ObjectID creation errors to not fail with outdated data to the
schema.
2024-11-25 16:27:47 -05:00
dependabot[bot]
c8589a640c chore(deps): bump eslint-plugin-playwright from 1.7.0 to 2.1.0 (#9474)
Bumps
[eslint-plugin-playwright](https://github.com/playwright-community/eslint-plugin-playwright)
from 1.7.0 to 2.1.0.
2024-11-25 15:29:52 -05:00
Elliot DeNolf
da788413eb ci: only security PRs from dependabot 2024-11-25 15:20:14 -05:00
Said Akhrarov
0c7e418dbc fix(translations): add sl to acceptedLanguages (#9506)
<!--

Thank you for the PR! Please go through the checklist below and make
sure you've completed all the steps.

Please review the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository if you haven't already.

The following items will ensure that your PR is handled as smoothly as
possible:

- PR Title must follow conventional commits format. For example, `feat:
my new feature`, `fix(plugin-seo): my fix`.
- Minimal description explained as if explained to someone not
immediately familiar with the code.
- Provide before/after screenshots or code diffs if applicable.
- Link any related issues/discussions from GitHub or Discord.
- Add review comments if necessary to explain to the reviewer the logic
behind a change

### What?

### Why?

### How?

Fixes #

-->
### What?
This PR adds `'sl'` to list of accepted languages, dedupes languages not
implemented from langs that have been supported, and adjusts the string
match for `'sl'` in importDateFNSLocale to the correct locale.

### Why?
To fix TS errors and runtime errors encountered while adding Slovenian
language to config, and then selecting it in `/account` view.

### How?
- Addition of `'sl'` to `acceptLanguages` array
- Change from `'sl'` to `'sl-SI'` in `importDateFNSLocale.ts`

Fixes #9504

---------

Co-authored-by: Jessica Chowdhury <jessica@trbl.design>
2024-11-25 20:17:05 +00:00
Jacob Fletcher
8383426313 fix(ui): prevents column reset on sort (#9517)
Fixes #9492. Sorting columns would unintentionally clear column
preferences.
2024-11-25 15:15:07 -05:00
Francisco Lourenço
af096a374a fix(ui): z-index on react-select menu (#9512)
The [previous fix](https://github.com/payloadcms/payload/pull/8735)
worked but was a breaking change because it set a `z-index` in the
`.react-select` wrapper instead of the `.rs__menu`, creating a new
stacking-context, therefore making any existing customizations to the
menu's `z-index` not work. This was a way to fix a regression introduced
by the css-layers, in which Payload's custom `z-index: 4` no longer took
precedence over react-select's default `z-index: 1`.

With this PR we remove the default `z-index: 1` applied by react-select,
so that the `z-index: 4` set in the "payload-default" css layer can take
effect. An alternative to this fix would be to use `z-index: 4
!important`, but this has the advantage of allowing the `z-index` to be
easily customized by the consumers of the CMS, as with all the other
styles.

### Screenshots

![2024-11-25 18 21
22](https://github.com/user-attachments/assets/3dc9a067-901a-41ea-b04e-c811788f8415)
2024-11-25 14:10:20 -06:00
Jarrod Flesch
6ffd4c7825 fix: incorrect locale registration in datepicker (#9516)
### What?
This log was appearing when the DatePicker loaded without a registered
locale:
```
A locale object was not found for the provided string ["enUS"].
```
Also fixes css misalignment of icons inside date picker field

### Why?
If i`18n.dateFNS` had not loaded, we were registering the locale with an
undefined value.

### How?
Only register the locale for react-datepicker if i18n.dateFNS is
present.
2024-11-25 15:05:15 -05:00
Elliot DeNolf
08270426ba ci: update release-canary name 2024-11-25 14:08:18 -05:00
Jacob Fletcher
f136a7db2a fix: improper spread of list preferences (#9510)
List preferences were improperly saving their own records onto
themselves when building table state through the server function. This
was happening because the entire preference document was being spread
onto the new preferences, as opposed to just the value itself:

```diff
const mergedPrefs = {
-  ...(preferencesResult || {}),
+  ...(preferencesResult?.value || {}),
   columns,
}
```

This PR also swaps `dequal` out for `dequal/lite`.
2024-11-25 13:56:35 -05:00
Sasha
e176b8b764 fix: correct migrations sorting in getMigrations (#9330)
### What?
We sorted migrations by `-name` in `getMigrations` as by assumption from
generated file names, however, it may be not true as the improved (+
unflaked, previously it failed sometimes) test for `migrate:down` can
reproduce. As in result, `migrateDown` / `migrateRefresh` may execute in
order different from `migrate`.

Unflakes the 'should commit multiple operations async'  test.
We shouldn't pass the same `req` that doesn't contain a transaction to
different operations that execute in parallel (via `Promise.all`)
without either creating a transaction before or using
`isolateObjectProperty(req, 'transactionID')`. It leads to a race
condition because operation can commit a wrong transaction, different
from inited
2024-11-25 13:53:44 -05:00
Elliot DeNolf
3c8f042d1d ci: update release-canary workflow 2024-11-25 13:38:38 -05:00
Sasha
e5cc9153aa fix(db-postgres): allow to clear select fields with hasMany: true (#9464)
### What?
Previously, using Postgres, select fields with `hasMany: true` weren't
clearable.
Meaning, you couldn't pass an empty array:
```ts
const updatedDoc = await payload.update({
  id,
  collection: 'select-fields',
  data: {
    selectHasMany: [],
  },
})
```

### Why?
To achieve the same behavior with MongoDB.

### How?
Modifies logic in `packages/drizzle/src/upsertRow/index.ts` to include
empty arrays.
2024-11-25 11:15:19 -05:00
Sasha
b96475b7b9 fix: run queues via the /payload-jobs/run endpoint without workflows (#9509)
Fixes https://github.com/payloadcms/payload/discussions/9418 (the
`/api/payload-jobs/run` endpoint) when the config doesn't have any
`workflows` but only `tasks`
2024-11-25 17:57:51 +02:00
Sasha
cae300e8e3 perf: flattenedFields collection/global property, remove deep copying in validateQueryPaths (#9299)
### What?
Improves querying performance of the Local API, reduces copying overhead

### How?

Adds `flattenedFields` property for sanitized collection/global config
which contains fields in database schema structure.
For example, It removes rows / collapsible / unnamed tabs and merges
them to the parent `fields`, ignores UI fields, named tabs are added as
`type: 'tab'`.
This simplifies code in places like Drizzle `transform/traverseFields`.
Also, now we can avoid calling `flattenTopLevelFields` which adds some
overhead and use `collection.flattenedFields` / `field.flattenedFields`.

By refactoring `configToJSONSchema.ts` with `flattenedFields` it also
1. Fixes https://github.com/payloadcms/payload/issues/9467
2. Fixes types for UI fields were generated for `select`



Removes this deep copying for each `where` query constraint
58ac784425/packages/payload/src/database/queryValidation/validateQueryPaths.ts (L69-L73)
which potentially can add overhead if you have a large collection/global
config

UPD:
The overhead is even much more than in the benchmark below if you have
Lexical with blocks.

Benchmark in `relationships/int.spec.ts`:
```ts
const now = Date.now()
for (let i = 0; i < 10; i++) {
  const now = Date.now()
  for (let i = 0; i < 300; i++) {
    const query = await payload.find({
      collection: 'chained',
      where: {
        'relation.relation.name': {
          equals: 'third',
        },
        and: [
          {
            'relation.relation.name': {
              equals: 'third',
            },
          },
          {
            'relation.relation.name': {
              equals: 'third',
            },
          },
          {
            'relation.relation.name': {
              equals: 'third',
            },
          },
          {
            'relation.relation.name': {
              equals: 'third',
            },
          },
          {
            'relation.relation.name': {
              equals: 'third',
            },
          },
          {
            'relation.relation.name': {
              equals: 'third',
            },
          },
          {
            'relation.relation.name': {
              equals: 'third',
            },
          },
          {
            'relation.relation.name': {
              equals: 'third',
            },
          },
          {
            'relation.relation.name': {
              equals: 'third',
            },
          },
        ],
      },
    })
  }

  payload.logger.info(`#${i + 1} ${Date.now() - now}`)
}
payload.logger.info(`Total ${Date.now() - now}`)
```

Before:
```
[02:11:48] INFO: #1 3682
[02:11:50] INFO: #2 2199
[02:11:54] INFO: #3 3483
[02:11:56] INFO: #4 2516
[02:11:59] INFO: #5 2467
[02:12:01] INFO: #6 1987
[02:12:03] INFO: #7 1986
[02:12:05] INFO: #8 2375
[02:12:07] INFO: #9 2040
[02:12:09] INFO: #10 1920
    [PASS] Relationships > Querying > Nested Querying > should allow querying two levels deep (24667ms)
[02:12:09] INFO: Total 24657
```

After:
```
[02:12:36] INFO: #1 2113
[02:12:38] INFO: #2 1854
[02:12:40] INFO: #3 1700
[02:12:42] INFO: #4 1797
[02:12:44] INFO: #5 2121
[02:12:46] INFO: #6 1774
[02:12:47] INFO: #7 1670
[02:12:49] INFO: #8 1610
[02:12:50] INFO: #9 1596
[02:12:52] INFO: #10 1576
    [PASS] Relationships > Querying > Nested Querying > should allow querying two levels deep (17828ms)
[02:12:52] INFO: Total 17818
```
2024-11-25 10:28:07 -05:00
Elliot DeNolf
8658945d7b chore(templates): remove unneeded lock files, add hook (#9508)
- Update lock files for blank, website
- Delete unneeded lock files
- Adds git hook to ensure no new lockfiles are added for _other than_
blank and website.
2024-11-25 10:04:41 -05:00
Sasha
aa1d300062 feat(templates): website template performance improvements (#9466)
- Uses `pagination: false` where we don't need `totalDocs`.
- in `preview/route.ts` uses `depth: 0`, select of only ID to improve
performance
- in `search` uses `select` to select only needed properties
- adds type safety best practices to collection configs with
`defaultPopulate`
- uses `payload.count` to resolve SSG `pageNumber`s
2024-11-25 10:02:27 -05:00
Sasha
150c55de79 chore(next): remove deep copying of docPermissions in the Version View (#9491)
Removes unnecessary `deepCopyObject(docPermissions)` in the Version View
which slows down loading speed.
The comment seems to be resolved, I'm not getting this error and here
for example in the same case
3c0e832a9a/packages/next/src/views/Document/index.tsx (L327)
we don't do deep copying.
2024-11-25 09:20:22 -05:00
Jeffrey Arts
4b4cfbeca7 docs: add migration-guide note public dir changes (#9498)
The location of public directory has changed, this addition highlights
this change and how to resolve it
2024-11-25 09:17:34 -05:00
Sasha
7eb388d403 fix: ensure deleteJobOnComplete property for jobs works (#9283)
Ensures that the `deleteJobOnComplete` (which is `true` by default)
property works properly
2024-11-25 09:11:15 -05:00
Harley Salas
07c76aa3b9 chore(translations): improve russian translation for noResults (#9496)
### What?
The "noResults" translation key, for Russian, which is displayed when
searching a collection list and receiving no results.

![image](https://github.com/user-attachments/assets/b83204c9-0467-42e4-9f38-5a38548e5459)


### Why?
Unlike English, Slavic languages like Russian have the concept of
genders and depending on the ending of a particular word, the endings of
adjectives can be different, to correspond with those genders. The
current version only works with feminine words, directly translating to
"No {{label}} found. Either {{label}} doesn't exist yet, or none of them
match the filters you specified above."
The new version translates to "Nothing found. {{label}} may not exist
yet or doesn't match the specified filters.", which is a more loose
translation, but holds the same meaning, while being grammatically
correct in all scenarios, regardless of the gender.
2024-11-25 12:37:34 +02:00
Said Akhrarov
9c59359da6 docs: fix invalid links (#9500)
### What?
This PR fixes a variety of links around the docs.

### Why?
To link readers to the correct location in the docs

### How?
Changes and fixes to a number of doc links.
2024-11-24 19:18:33 -07:00
Indy
3c0e832a9a docs: spelling error in README.md (#9478)
## Title

fix(docs): correct "kayout" typo to "layout" in README.md

### What?
- Corrected a typo in the `README.md` file where "kayout" was
incorrectly written instead of "layout".

### Why?
- To improve the documentation's readability and professionalism.

### How?
- Located the typo in `README.md` and replaced "kayout" with "layout".

Fixes #9472

----


### Before
<img width="1148" alt="before"
src="https://github.com/user-attachments/assets/232d0e01-ea06-4568-b283-afad09719f0c">

### After
<img width="1208" alt="after"
src="https://github.com/user-attachments/assets/f96f3b6c-6ccc-4d2e-8de3-88b9057a6cab">

---------

Co-authored-by: Indy S <billabong@Mac.attlocal.net>
2024-11-24 08:30:18 +00:00
Alessio Gravili
13fc94dc4d chore: upgrade to TypeScript 5.7, ensure tsconfig targed and lib properties match the APIs we support (#9473)
TS 5.7 added support for ES2024. By keeping target: “esnext”, we would
have accidentally set our minimum supported ES version to ES2024.

This sets it to ES2022, which is the version supported by Node 18
2024-11-23 16:35:27 -07:00
Paul
23d54a76c1 fix(templates): website template firefox logo fix and images fix on vercel (#9459)
Fixes logo not showing up on firefox and images not loading properly
when hosting with Vercel
2024-11-22 23:53:06 +00:00
Elliot DeNolf
f155e00f00 docs: proper readme (#9458)
Updated `packages/payload` README by copying down the top-level README.
We incorrectly thought this was some sort of caching issue on NPM.

Closes #9452
2024-11-22 18:01:50 -05:00
Paul
09abebd58c fix(templates): vercel website template payload config regression (#9455) 2024-11-22 20:18:04 +00:00
Paul
90fedbc16b fix(templates): fixes imports in website template from @payloadcms/ui to be direct (#9451) 2024-11-22 19:08:38 +00:00
Paul
322738fca3 chore(templates): copy over improvements to the vercel website template and add vscode debug config (#9450)
Copied over the improvements from the website template into the vercel
website template.

Add vscode debug config to both templates.
2024-11-22 18:42:28 +00:00
Paul
be8cd7f4d9 feat(templates): website optimisations for image sizes and loading (#9447)
Adds defaultPopulate on pages and posts
Further optimises images and fetching staticParams
2024-11-22 18:21:56 +00:00
Jacob Fletcher
b9d02aa3a8 chore: removes leftover references to beta (#9445)
Still a few leftover references to beta in various JSDocs and other
places.
2024-11-22 13:19:20 -05:00
Elliot DeNolf
65ac739da9 chore(release): v3.1.0 [skip ci] 2024-11-22 11:54:08 -05:00
Jacob Fletcher
ae6c71b3b5 fix(live-preview): client-side live preview cannot clear all array rows (#9439)
When using Client-side Live Preview, array fields are unable to clear
all their rows. This is because `reduceFieldsToValues` sets the array's
value as 0 within form-state when no rows exist, as opposed to an empty
array as one might expect. For now, we can simply handle this data shape
within Live Preview's merge logic. In the future we may want to take to
consider changing the behavior of empty arrays within form-state itself.
2024-11-22 16:43:48 +00:00
Riley Pearce
27acdaee7a fix(ui): add disabled attribute to blocks drawer toggler (#9424)
### What?

When `update` access control is set to `false`, the blocks drawer can
still be accessed. Blocks still cannot be saved to the document, making
this more of a QoL improvement.

### Why?

The blocks `DrawerToggler` is missing the `disabled` attribute.

### How?

Adding the `disabled` attribute to the blocks `DrawerToggler`.

### Reproduction steps

https://github.com/rilrom/payload/tree/issue/disabled-add-block-button

1. Navigate to the posts collection.
2. Click the 'Add blocks' button.
3. Blocks can be selected (however they are still readonly in the
document and cannot be saved).

### Recordings

Before


https://github.com/user-attachments/assets/da12829d-5b27-4da9-bbd1-067d679a89ba

After


https://github.com/user-attachments/assets/67166477-b83a-4495-98ae-2d272542103b
2024-11-22 08:59:38 -07:00
Jessica Chowdhury
fb3242df0a feat: add skip and force accept flags to migration commands (#8843)
1. Adds flag `--skip-empty` to `migrate:create` to bypass the empty
migration file prompt.
    - Blank migration file will not be created if this flag is passed.
3. Adds flag `--force-accept-warning` to `migrate:fresh` to bypass the
drop database prompt
2024-11-22 10:37:54 -05:00
Germán Jabloñski
251c4c30d7 chore: fix path and samples for lexical-mdx tests (#9433)
Later we could add better examples that test complex use cases, but at
least this allows the code to compile.
2024-11-22 14:55:39 +00:00
Alessio Gravili
9e31e17e31 feat(richtext-lexical): more powerful custom Block RSCs, improved selection handling (#9422)
Now, custom Lexical block & inline block components are re-rendered if
the fields drawer is saved. This ensures that RSCs receive the updated
values, without having to resort to a client component that utilizes the
`useForm` hook.

Additionally, this PRs fixes the lexical selection jumping around after
opening a Block or InlineBlock drawer and clicking inside of it.
2024-11-22 02:34:29 +00:00
Alessio Gravili
b9cc4d4083 fix(richtext-lexical): error when changing block collapsed state in rare cases (#9421) 2024-11-22 01:13:03 +00:00
Alessio Gravili
a891e98bfc fix(richtext-lexical): slash menu from + button did not trigger item select action (#9420)
Fixes a bug I introduced through
https://github.com/payloadcms/payload/pull/9396 and adds an e2e test
2024-11-22 00:48:45 +00:00
Alessio Gravili
38de760ca5 feat(richtext-lexical): allow customizing maxActiveItems for toolbar groups (#9417)
Previously, they were hardcoded to 1. This is useful for lexical toolbar
groups where multiple items can be active at the same time.
2024-11-21 23:53:29 +00:00
Paul
18b139b9de fix(templates): website template breaking change by renaming hero image to jpg (#9416) 2024-11-21 21:42:29 +00:00
Alessio Gravili
3b68671348 feat(richtext-lexical): export lexical drawer hooks (#9415) 2024-11-21 21:12:17 +00:00
Sasha
91dcf6dc08 fix: invalid payload.collections type (#9414)
### What?
`payload.collections` was improperly typed.
This doesn't seem to work: (the type is `{}`)
```
collections: {
  [slug: CollectionSlug]: Collection
} = {}
```
<img width="794" alt="image"
src="https://github.com/user-attachments/assets/7daceab9-8f43-433b-9201-1bf8c48fb8ca">

However, this does:
```ts
collections: Record<CollectionSlug, Collection> = {}
```
<img width="540" alt="image"
src="https://github.com/user-attachments/assets/e37d595d-f5b4-4b02-b190-bb5d4063787d">


Additionally, the same fix applied to `Permissions`,
`PolymorphicUploadField['admin']['sortOptions']`,
`PolymorphicRelationshipField['admin']['sortOptions']`
2024-11-21 22:58:04 +02:00
Jacob Fletcher
f5683b0a64 fix(next): threads server props to custom providers (#9412)
When defining custom providers as server components, they currently do
not receive any of the server props that custom components expect to
receive, like `payload`, `i18n`, `user`, and so on.
2024-11-21 15:44:32 -05:00
Paul
2036a566fd feat(templates): update website styles and new home hero image (#9413) 2024-11-21 20:39:56 +00:00
Friggo
0b4e5a6ece chore(templates): updates github urls in seed data (#9376)
Update the GitHub URLs to website template to point to main instead of beta
2024-11-21 15:35:07 -05:00
Alessio Gravili
e3866c4035 fix(ui): stale server components when rows are moved (#9410)
We need to trigger server component re-rendering for moving rows, just
like we do for adding or deleting rows.

Video of the issue:


https://github.com/user-attachments/assets/32fb31c5-f304-4082-8028-59a6ad723fbe
2024-11-21 20:14:57 +00:00
Patrik
f338c5c40c chore(examples): updates custom-components,email & multi-tenant to latest payload version (#9411)
### What?

Although the following examples:

- `custom-components`
- `email`
- `multi-tenant`

were recently migrated to 3.0 - they were still using the latest `beta`
version instead of latest payload (i.e `3.0`)
2024-11-21 14:56:34 -05:00
Jacob Fletcher
118b442883 docs: custom server providers and filter components (#9303)
- Removes mention of custom providers needing to be client components
- Documents custom field `Filter` components
- Adjusts language and other misc. grammar and spelling
2024-11-21 14:25:37 -05:00
Jarrod Flesch
ee1a91ee7c fix: unable to load documents with non-standard ids (#9407)
### What?
Non-standard ids caused an issue when finding the document on the
server.

This is an odd regression, in 2.0 we were fetching the document on the
client so the request would handle decoding the url. Now we are fetching
the document on the server and need to do this manually when reading id
from route params.

### Why?
The slug pulled out of the url for an id of `id 1` would equate to
`id%201` which would fail in the `payload.find` call since there is not
an id stored as `id%201` but instead `id 1`.

### How?
Wherever we are calling payload.find in the views and querying by `id`
it gets ran through a helper function that decodes it properly.

Fixes #9373
2024-11-21 14:16:01 -05:00
Patrik
304ecd29ac docs: removes public demo references (#9408)
### What?

Removes all references to the `public-demo` from the docs
2024-11-21 14:09:08 -05:00
Jarrod Flesch
204b08bc25 chore: update issue templates (#9409)
Removes mentions of `beta` in issue templates now that 3.0 has been
released.
2024-11-21 13:56:16 -05:00
Sasha
f2205d1694 fix(next): next@15.0.3 compatibillity for turbopack warnings patch (#9341)
Added patch to `withPayload` for hiding turbopack external deps warnings
from this PR https://github.com/payloadcms/payload/pull/9147 didn't work
on `next@15.0.3`, now it works on both `15.0.0` and `15.0.3`.
2024-11-21 20:46:09 +02:00
Germán Jabloñski
082e546159 chore: fix test/lexical-mdx imports (#9405)
missing from #9229
2024-11-21 18:38:16 +00:00
Jarrod Flesch
d7ad1733f2 chore: type getViewFromConfig, add viewActions to serverProps (#9404)
### What?
`viewActions` are not easily accessible in custom views.

### Why?
We extract view actions when we call `getViewFromConfig`, but never pass
them to the custom views.

### How?
Properly types return type for serverProps inside `getViewFromConfig`
and adds viewActions to serverProps so they are spread into props when
we build the custom view components.

Now custom server views will get the viewActions as a prop.

Fixes #9338
2024-11-21 11:58:05 -05:00
Sasha
d499de1e0f fix(db-postgres): joins with versions and hasMany relationship (#9370)
Fixes errors when having joins with versions +drafts on `hasMany: true`
relationships.
Removes `joinQuery` overhead if we don't need it for the current
operation. Right now, in all adapters we support joins only for `find`,
`findOne`, and `queryDrafts`.


Fixes https://github.com/payloadcms/payload/issues/9369
2024-11-21 10:42:05 -05:00
Alessio Gravili
3d0424bc77 fix(richtext-lexical): slash menu query doesn't disappear after selecting slash menu item (#9396)
This happened only for certain slash menu items like inline blocks.

Fixes https://github.com/payloadcms/payload/issues/9326
2024-11-21 06:15:22 +00:00
Germán Jabloñski
0960290558 fix(richtext-lexical): preserve indent and text-align when converting Lexical <-> HTML (#9165)
Fixes #5146 

This had been solved in https://github.com/facebook/lexical/pull/6693
but we are using another serialization. I open
https://github.com/payloadcms/payload/discussions/9166 to discuss/track
how we can improve this in the future
2024-11-20 22:24:45 -07:00
Alessio Gravili
48b60fc905 chore(richtext-lexical): enable strict: true (#9394)
Thanks to @GermanJablo for doing most of the work by enabling
`noImplicitAny` and `strictNullChecks` in previous PRs
2024-11-21 04:19:18 +00:00
Alessio Gravili
90e37fe78d fix(ui): error when collapsing sidebar groups (#9393)
Fixes
https://discord.com/channels/967097582721572934/1308933812914229368/1308933812914229368

![CleanShot 2024-11-20 at 19 04
45@2x](https://github.com/user-attachments/assets/3c2c8054-9d09-4090-adf8-57aef8635baf)
2024-11-21 02:17:45 +00:00
Alessio Gravili
9470f9b01f fix: error when opening admin panel without any permissions (#9392)
We were getting the following error when opening the create first user
page, as sanitizedPermissions is `{}` if the user has no permissions at
all

![CleanShot 2024-11-20 at 18 48
46@2x](https://github.com/user-attachments/assets/90270a0b-10e5-4f22-a91e-174f82a559b2)
2024-11-21 02:10:40 +00:00
Germán Jabloñski
1f92b5b412 chore(richtext-lexical): add noImplicitAny to the richtext-lexical package (#8763) 2024-11-21 01:46:28 +00:00
Elliot DeNolf
025d917fa0 chore(release): v3.0.2 [skip ci] 2024-11-20 16:06:04 -05:00
Alessio Gravili
c67291d538 fix(ui): invalid permissions passed to group and named tab sub-fields (#9366)
Fixes https://github.com/payloadcms/payload/issues/9363

This fixes the following issues that caused fields to be either hidden,
or incorrectly set to readOnly in certain configurations:
- In some cases, permissions were sanitized incorrectly. This PR
rewrites the sanitizePermissions function and adds new unit tests
- after a document save, the client was receiving unsanitized
permissions. Moving the sanitization logic to the endpoint fixes this
- Various incorrect handling of permissions in our form state endpoints
/ RenderFields
2024-11-20 13:03:35 -07:00
Alessio Gravili
5db7e1e864 fix(richtext-lexical): use copy of @lexical/markdown that does not install @lexical/code (#9382)
Fixes https://github.com/payloadcms/payload/issues/9378

We’ve found out that @lexical/markdown imports cannot be reliably
dynamically imported by Node.js for an unknown reason. Frequently,
Node.js simply exits before the dynamic import is done.

We’re suspecting the reason for this to be its dependency on
@lexical/code that installs prism.

This will not only (hopefully) fix the import issue, but also reduce the
bundle size & compilation speed of richtext-lexical.
2024-11-20 13:02:57 -07:00
Jacob Fletcher
07a9125f9c fix(next): properly sets doc id types when using postgres (#9381)
Fixes #9351. When using Postgres, doc ids were being treated as a string
as opposed to a number within the admin panel. This led to issues for
anything relying on the `docID` from context, such as the join field not
properly populating initial data when creating new documents, etc.
2024-11-20 15:02:08 -05:00
Jacob Fletcher
ef3748319e fix(ui): bulk update and delete ignoring search query (#9377)
Fixes #9374.
2024-11-20 13:22:43 -05:00
Jarrod Flesch
439dcd493e fix: prioritize server rendered Actions in ActionsProvider (#9379)
### What?
Actions were not being re-rendered when router.refresh was called.
[Discord
Link](https://discord.com/channels/967097582721572934/1308636510203154462/1308648116031066173)

### Why?
They were stored in state and the state was persisting.

### How?
Spread the stateful actions and the Actions from props on top of the
client state actions.
2024-11-20 13:18:28 -05:00
Elliot DeNolf
ff0386f276 docs: improve migration guide for running db migrations 2024-11-20 11:34:57 -05:00
Said Akhrarov
568e1a274b docs: fix custom component links in admin (#9368)
### What?
Fixes links for custom components in a few places in admin docs.

### Why?
To link users to the correct location in the docs.

### How?
Changes to `docs/admin/components.mdx` and
`docs/admin/customizing-css.mdx`
2024-11-20 09:05:56 -05:00
Jacob Fletcher
9e85be0006 fix(next): autosave document rendering (#9364)
Closes #9242 and #9365. Autosave-enabled documents rendered within a
drawer were not being properly handled. This was causing multiple draft
documents to be created upon opening the drawer, as well as an empty
document returned from the server function, etc.
2024-11-19 19:01:54 -05:00
Jarrod Flesch
4030e212f5 fix: allow self referencing relationships when adding new collections to config (#9360)
### What?
Unable to add collections to the config dynamically if they reference
their own collection in a relationship field.

This was discovered while working on the folder view feature which
dynamically adds collections to your config if it is enabled per
collection.

### Why?
When `sanitizeCollection` runs, it takes the current config. If you are
sanitizing a collection before adding it to the config, that collection
cannot have any self referencing relationship fields on it otherwise it
fails the validRelationships check.

### How?
Using a reducer we now initialize the validRelationships variable with
the incoming collection slug.
2024-11-19 16:22:23 -05:00
Evan Cusack
646a5345a8 docs: update what-is-payload.mdx (#9353)
### What? 
instead of saying "it", wanted to give more context to what goes into
your Next app folder.

### Why? 
clarification

### How? 
edited docs
2024-11-19 14:53:48 -05:00
Patrik
1dc8094905 chore(templates): adds getURL util to website & with-vercel-website templates (#9336) 2024-11-19 14:43:22 -05:00
Elliot DeNolf
9ea26638e9 chore(release): v3.0.1 [skip ci] 2024-11-19 14:33:41 -05:00
Elliot DeNolf
4f1a4a28a3 chore: remove changelog.md from git staging 2024-11-19 14:33:10 -05:00
Elliot DeNolf
41d2e64a3a ci(scripts): remove updating changelog.md 2024-11-19 14:31:55 -05:00
Jarrod Flesch
661f450c61 fix(ui): client side doc data not updating after save (#9340)
### What?
When a document is saved the data from useDocumentInfo was stale.

### Why?
Previously we would refresh the entire document by calling the
form-state endpoint, we no longer do that.

### How?
Adds a new variable accessible from useDocumentInfo,
`savedDocumentData`, that is updated when the document is successfully
saved and defaults to initialData.
2024-11-19 13:22:23 -05:00
Elliot DeNolf
69b7a11a28 ci: remove v3 tagging, assumed 2024-11-19 13:15:20 -05:00
Said Akhrarov
2c7ea6362a fix(ui): show required indicator for select fields (#9348)
### What?
Select field was not showing required indicator despite being marked
required in config.

### Why?
To give end-user feedback when editting required select fields.

### How?
Replacing hardcoded required prop with required prop passed in from
config.

[See
here.](https://github.com/payloadcms/payload/blob/main/packages/ui/src/fields/Select/Input.tsx#L100)
2024-11-19 12:55:26 -05:00
Jessica Chowdhury
077d3e7d16 chore: adjust api code preview indentation (#9114) 2024-11-19 12:42:36 -05:00
Jacob Fletcher
188baec34c fix(ui): proper permissions within version diff view (#9346)
Fixes #9337. The version view was not able to render its diff because of
an invalid permissions lookup. This was a result of a change to how
access results are returned from the API, which are now sanitized:
https://github.com/payloadcms/payload/pull/7335
2024-11-19 12:39:05 -05:00
Elliot DeNolf
d9c6288cb2 chore: update migration guide link 2024-11-19 12:25:35 -05:00
Jessica Chowdhury
037662d9f5 chore(examples): migrates custom-component example to latest beta [skip-lint] (#9170)
Updates the `Custom Components` example, including packages, readme,
lockfile, types and the custom fields.

---------

Co-authored-by: Patrik Kozak <patrik@payloadcms.com>
2024-11-19 11:40:20 -05:00
Patrik
389ef16a5f chore(examples): migrates whitelabel example to 3.0 (#9316) 2024-11-19 11:36:28 -05:00
Elliot DeNolf
cb3d7b37ef chore: remove v2 changelog 2024-11-19 11:29:15 -05:00
Marc Maceira
d542bd774d docs: fix broken links to release notes in migration guide (#9333) 2024-11-19 11:17:26 -05:00
Paul
7f65c83a98 fix(templates): website logo not showing (#9334) 2024-11-19 15:06:56 +00:00
Jacob Fletcher
0f3f6e73da fix(ui): addFieldRow set modified (#9324)
Fixes #9264. When externally updating array or block rows through the
`addFieldRow` or `replaceFieldRow` methods, nested rich text fields
along with any custom components within them are never rendered. This is
because unless the form is explicitly set to modified, as the default
array and blocks fields currently do, the newly generated form-state
will skip the rendering step. Now, the underlying callbacks themselves
automatically set the form to modified to trigger rendering.
2024-11-19 08:52:50 -05:00
James Mikrut
a50029f659 Update README.md 2024-11-19 08:44:22 -05:00
James Mikrut
197a22fb28 chore: uses payload.auth in website template seed function (#9331)
Uses `payload.auth` in the website template seed function, which is
better than manually verifying the JWT.
2024-11-19 08:36:53 -05:00
garrettgrohman
6d74fbc6cb docs: fixes generic grammar mistakes 2024-11-19 08:28:32 -05:00
Jarrod Flesch
f42b1e1e05 fix: could not save selection from upload has many drawer (#9325)
### What?
Could not finalize selection of `hasMany` uploads inside of the drawer.

### Why?
The Select component was not being rendered in the beforeActions prop of
the ListControls when row selections was enabled.

### How?
Renders the Select component when row selections are enabled and
onBulkSelect is present.
2024-11-19 08:23:02 -05:00
Sean Zubrickas
bb4f69fb0c chore: updates README banner 2024-11-19 07:00:19 -05:00
Paul
26cb1e1546 fix(templates): seeding in website template moved to a separate route so timeout can be customised (#9327) 2024-11-19 00:03:22 -06:00
Alessio Gravili
5d2b0b30b0 perf: significantly reduce HTML we send to the client. Up to 4x smaller (#9321)
The biggest difference comes from calling `RenderServerComponent` as a
function, instead of rendering it by using `<RenderServerComponent`.

This gets rid of wasteful blocks of codes sent to the client that look
like this:

![CleanShot 2024-11-18 at 20 41
20@2x](https://github.com/user-attachments/assets/edb67d72-f4a5-459b-93f4-68dc65aeffb6)


HTML size comparison:

## Admin test suite

| View | Before | After |
|------|---------|--------|
| Dashboard | 331 kB | 83 kB |
| collections/custom-views-one Edit | 285 kB | 76.6 kB |

## Fields test suite

| View | Before | After |
|------|---------|--------|
| collections/lexical Edit | 189 kB | 94.4 kB |
| collections/lexical List | 152 kB | 62.9 kB |

## Community test suite

| View | Before | After |
|------|---------|--------|
| Dashboard | 78.9 kB | 43.1 kB |
2024-11-19 04:30:21 +00:00
Alessio Gravili
1425d58b57 chore: make uploads e2e test suite not flaky (#9323)
The problem was that the uploads test suite was trying to log in to
payload before it was even initialized.

In the rare event where payload started up before the uploads test suite
was trying to log in, our tests passed.
2024-11-19 03:50:35 +00:00
Blain Maguire
d3b0a045be fix: allow setting admin path route from config (#8085) 2024-11-18 21:20:23 -05:00
Elliot DeNolf
7e41f17ec2 chore: update README for 3.0 2024-11-18 20:53:13 -05:00
Elliot DeNolf
763ccdcf00 chore(release): eslint/3.0.0 2024-11-18 20:39:54 -05:00
Elliot DeNolf
76286136ba chore(cpa): update init next to use latest 2024-11-18 20:29:07 -05:00
Elliot DeNolf
6145accb83 chore(templates): bump all templates to 3.0 2024-11-18 20:25:52 -05:00
Elliot DeNolf
6407e577d3 chore(release): v3.0.0 [skip ci] 2024-11-18 20:10:16 -05:00
Elliot DeNolf
7fe207937f ci: update release script for v3 2024-11-18 20:05:55 -05:00
Elliot DeNolf
cc04396a4f chore(cpa): unpin template version tag for release 2024-11-18 20:00:38 -05:00
Dan Ribbens
0d63dc9f9c chore: remove unused transaction in website seed (#9319) 2024-11-18 19:55:27 -05:00
Alessio Gravili
f7fc8a2ea0 perf(richtext-lexical): do not send default lexical editor config to client (#9318)
We can just get the default config from the client, if the server passes
undefined. This wasted bandwidth and unnecessarily increased the html
size
2024-11-18 17:28:55 -07:00
Elliot DeNolf
fade739f77 chore(release): v3.0.0-beta.135 [skip ci] 2024-11-18 19:19:55 -05:00
Said Akhrarov
1a31e63b4d fix(ui): safely accesses field in default filter component (#9276)
### What?
In the WhereBuilder Condition DefaultFilter component, there is a switch
statement that contains components to return based on the built filter
in the admin ui. Having a filter built out then navigating to another
collection list view causes an error to occur due to InternalField being
undefined but the DefaultFilter tries to access the field on it.

### Why?
To fix unexpected `cannot access property field of undefined` errors.

### How?
Adding a conditional chaining operator.

Odd thing here is that the `Text` component where this error originates
from doesn't actually make use of the passed `InternalField`. Might be
worth it to take a closer look at it.

Fixes #9179
2024-11-18 17:52:29 -05:00
Dan Ribbens
a25ddc5448 docs: migration guide more info (#9305) 2024-11-18 17:35:36 -05:00
Elliot DeNolf
ecbafbf181 fix(templates): remove req from seed script 2024-11-18 16:58:16 -05:00
Elliot DeNolf
7314990b0c chore(templates): add .npmrc files 2024-11-18 16:38:36 -05:00
Elliot DeNolf
58eeb6e47c chore(templates): bring back needed lock files 2024-11-18 16:35:56 -05:00
Germán Jabloñski
f1eab5d5d3 chore(richtext-lexical): re-export lexical (#9229)
Co-authored-by: Alessio Gravili <alessio@gravili.de>
2024-11-18 16:27:36 -05:00
James Mikrut
6873b139e2 chore(examples): removes nested docs, redirects, virtual fields, custom server, and hierarchy examples (#9112)
Removes examples that are now duplicative or unnecessary due to new
features in 3.0:

### Custom server

This one can be removed in favor of [Next.js
documentation](https://nextjs.org/docs/pages/building-your-application/configuring/custom-server)

### Hierarchy

The new `join` field can solve for many of the use cases for the
`hierarchy` example. Bi-directional relationships with the `join` field
should be preferred here.

### Nested Docs, Redirects

Our website template showcases how to use the `nested-docs` and
`redirects` plugins in-depth, with real-world examples.

### Virtual Fields

Virtual fields have gotten significantly easier and can now be defined
by specifying `virtual: true`. Not a big need for a full example any
longer.

---------

Co-authored-by: Jacob Fletcher <jacobsfletch@gmail.com>
2024-11-18 16:11:21 -05:00
Sophia Michelle Andren
54379eccf4 feat: new Payload logo (#9240)
Introducing the new Payload logo! 🎉


![Payload_Opengraph](https://github.com/user-attachments/assets/387e0841-9498-43df-bf01-f78adb7cdfcf)
2024-11-18 16:08:59 -05:00
Jarrod Flesch
7989563bc1 chore: updates multi tenant example to use baseListFilter (#9308)
### What?
Implements new `baseListFilter` into the multi-tenant example

---------

Co-authored-by: Patrik Kozak <patrik@payloadcms.com>
2024-11-18 15:59:54 -05:00
Patrik
7babe6bcf5 chore(examples): migrates multi-tenant example to latest beta (#9300) 2024-11-18 15:45:20 -05:00
Nate
bccb7c0ff7 chore: update README.md (#9304)
Fixes url to banner asset.
2024-11-18 15:44:42 -05:00
Elliot DeNolf
333da1bb42 fix(templates): seed from url (#9306)
Update website template seeding to use URLs instead of image assets.
Images are not available when deployed to vercel.
2024-11-18 15:41:20 -05:00
Elliot DeNolf
2b955a4aa3 chore(cpa): remove beta from vercel pg and sqlite 2024-11-18 15:40:18 -05:00
Elliot DeNolf
e796ff2330 chore(templates): add back payload cloud to plugins array 2024-11-18 15:31:45 -05:00
mattddean
0075b99eec fix(graphql): loading of polymorphic hasMany relationships (#9175)
### What?

Bug #9173

### Why?

`collectionSlug` is an array when `isRelatedToManyCollections` is true

### How?

Compare array to array

Fixes #9173

![Screenshot 2024-11-13 at 12 37
52 PM](https://github.com/user-attachments/assets/3eae497d-90d8-474a-afd0-baf69f017459)
2024-11-18 22:25:42 +02:00
Sasha
e40141b559 fix: queues types with strict: true (#9281)
Fixes types for workflows / jobs `input` and `output` when using
`strict: true` or `strictNullChecks: true` by ensuring that all
properties in generates types are requried
2024-11-18 21:49:08 +02:00
Jacob Fletcher
ec95ce8758 fix(next): passes doc through edit view handler (#9302)
The Edit and Live Preview views were duplicately making the same Local
API requests for document data. This is because while the top-level
document view handler makes these requests _before_ rendering the Live
Preview view, it wasn't passing it's data through as props. This has
also led to inconsistencies in the options being passed through the
requests themselves, such as `locale`, `user`, and `overrideAccess:
false`. Everything is now standardized as expected through the existing
`getDocumentData` utility.
2024-11-18 14:27:40 -05:00
Mikkel Wied Frederiksen
54ac8b9cd2 fix(next): passes locale through requests in live preview edit view (#9298)
Co-authored-by: Jacob Fletcher <jacobsfletch@gmail.com>
2024-11-18 13:41:12 -05:00
Alessio Gravili
0a6f530007 fix: nav jumping around if no nav preferences are set (#9301)
If you start a fresh dev server and open payload, the nav will initially
show as closed and then jump to its open state. This is because no
preferences are set, so the server tells the client to initially keep it
closed, despite the default nav state being _open_.
2024-11-18 18:36:56 +00:00
Jarrod Flesch
58ac784425 fix(next): initialize payload with importMap (#9297)
### What?
Custom providers could not be resolved because payload was not
initialized in the Root layout with the importMap passed in from props.

### How?
Pass importMap from props into the getPayload function in the Root
layout.
2024-11-18 11:07:40 -05:00
Jarrod Flesch
5503afdf29 fix: sanitize sub block field permissions correctly (#9296)
Fixes https://github.com/payloadcms/payload/issues/9288

### What?
When a block had a subfield named `blocks`, sanitization would throw an
error.

### Why?
An incorrect check for the key of `"fields"` would then attempt to pass
`data.blocks[key].fields` aka `data.blocks.fields.fields` to the next
call of `areAllPermissionsTrue` which would be undefined. Instead if the
key is `fields` it should pass `data.blocks[key]`.

### How?
Remove the second `.fields` property accessor.
2024-11-18 10:47:57 -05:00
Jacob Fletcher
30947d2173 perf!: removes unnecessary field styles from initial page response (#9286)
Optimizes initial page responses by removing unnecessary inline field
styles that were being sent through the HTML response. The Client Config
contains a large number of duplicates of the string:
`"style\":{\"flex\":\"1 1 auto\"}`, one for every single field within
the entirely of the config. This leads to hundreds or potentially
thousands of instances of this same string, depending on the number of
fields within the config itself. This is regardless of custom field
widths being defined. Instead, we can do this entirely client-side,
preventing this string from ever being transmitted over the network in
the first place.

## Breaking Changes

This only effects those who are importing Payload's field components
into your own Custom Components or front-end application. The `width`
prop no longer exists. It has been consolidated into the existing
`style` prop. To migrate, simply move this prop as follows:

```diff
import { TextInput } from '@payloadcms/ui

export const MyCustomComponent = () => {
  return (
    <TextInput 
-      width="60%"
       style={{
+        width: "60%,
       }}
    />
  )
}
```
2024-11-18 10:03:26 -05:00
Sasha
665b3536d3 fix(db-mongodb): potential errors in sanitizeRelationshipIDs with ref being a non object (#9292)
### What?
Fixes potential errors when passed to `sanitizeRelationships` `ref`
could potentially be a non object (for example `string`) because of
having in the database data in old structure.
```
"Cannot create property 'a' on string 'B'",
``` 

### Why?
Necessary particularly for the migration script, as it migrates
everything including versions that can have outdated data.

### How?
Ensures passed `ref` is an `object`.
2024-11-18 16:10:13 +02:00
Dan Ribbens
488c28c99c fix: getPayload node exits on webpack-hmr websocket failure (#9279)
### What?
When a script attempts to load payload using `getPayload()`, it will end
with: `Error: connect ECONNREFUSED 127.0.0.1:3000` etc...

### Why?

Even though there is a try/catch, it still errors because WebSocket
connection failures happen asynchronously after the ws object is
instantiated.

### How?

Added the error handling function cached.ws.onerror to prevent exit.
2024-11-18 09:03:52 -05:00
Jacob Fletcher
7489c29704 chore: dedupes field description functions and defers rendering static field descriptions to the client (#9277)
Custom field description functions were being duplicately called in both
the Client Config and form state. Static field descriptions were also
being rendered in form state unnecessarily. Now, field description
functions are only executed once within form state, and static
descriptions are deferred to the client for rendering.
2024-11-17 22:37:35 -05:00
Jacob Fletcher
3eb8b5939e chore: extracts dependency checker from root layout (#9272)
Cleans up the Root Layout by extracting the dependency checker and
related code away from component logic.
2024-11-17 21:46:18 -05:00
Alessio Gravili
d4f1add2ab feat(richtext-lexical): mdx support (#9160)
Supports bi-directional import/export between MDX <=> Lexical. JSX will
be mapped to lexical blocks back and forth.

This will allow editing our mdx docs in payload while keeping mdx as the
source of truth

---------

Co-authored-by: Germán Jabloñski <43938777+GermanJablo@users.noreply.github.com>
2024-11-17 15:03:45 -07:00
Elliot DeNolf
324af8a5f9 chore: update all githubusercontent links after branch rename 2024-11-17 16:46:23 -05:00
Elliot DeNolf
c9040b6095 chore(templates): update templates after branch rename 2024-11-17 12:52:18 -05:00
Elliot DeNolf
0107a48374 ci: update dependabot config after branch rename 2024-11-17 12:35:34 -05:00
Elliot DeNolf
a31c29b1ff ci: update workflows after branch rename 2024-11-17 12:13:19 -05:00
Elliot DeNolf
cb6ceaec76 chore(release): v3.0.0-beta.134 [skip ci] 2024-11-17 11:43:43 -05:00
Sasha
ef2475d804 fix(ui): avoid calling getTableState from join field on create (#9256)
### What?
Fixes the issue when visiting the create view with the Join Field and
using postgres adapter
```
invalid input syntax for type integer: "NaN"
```
This happens because we don't have an ID yet and we send to the
database:
`WHERE id = NaN`

### How?
Avoids calling `getTableState` inside of `RelationshipTable` if there's
no ID yet, as it will always lead to the same empty result. While we
_could_ avoid error directly in the database adapter, I don't think we
should do that render request

Fixes https://github.com/payloadcms/payload/issues/9193
2024-11-17 11:32:50 -05:00
Sasha
d21fca9156 feat: support relationship writes using objects instead of IDs (#9253)
### What?
Previously, this code led to a validation error because `movie` is an
object and you needed to use `movie.id` instead.
```ts
const movie = await payload.create({ collection: 'movies', data: {} })
const result = await payload.create({
  collection: 'object-writes',
  data: {
    many: [movie],
    manyPoly: [{ relationTo: 'movies', value: movie }],
    one: movie,
    onePoly: {
      relationTo: 'movies',
      value: movie,
    },
  },
})
```
While it's simple to modify this example, it's more painful when you
have a data with `depth` > 0 and then you want to update that document.

### Why?
Better DX as less checks needed, and TypeScript says that we can pass an
object.

### How?
Sanitizes the field value in the root `beforeValidate` hook
2024-11-17 11:25:32 +02:00
Alessio Gravili
35917c67d7 perf(richtext-lexical)!: significantly reduce lexical rerendering and amount of network requests from blocks (#9255)
The field RSC now provides an initial state for all lexical blocks. This
completely obliterates any flashes and lexical block loading states when
loading or saving a document.

Previously, when a document is loaded or saved, every lexical block was
sending a network request in order to fetch their form state. Now, this
is batched and handled in the lexical server component. All lexical
block form states are sent to the client together with the parent
lexical field, and are thus available immediately.

We also do the same with block collapsed preferences. Thus, there are no
loading states or layout shifts/flashes of blocks anymore.

Additionally, when saving a document while your cursor is inside a
lexical field, the cursor position is preserved. Previously, a document
save would kick your cursor out of the lexical field.

## Look at how nice this is:


https://github.com/user-attachments/assets/21d736d4-8f80-4df0-a782-7509edd993da

**BREAKING:**

This removes the `feature.hooks.load` and `feature.hooks.save`
interfaces from custom lexical features, as they weren't used internally
and added unnecessary, additional overhead.

If you have custom features that use those, you can migrate to using
normal payload hooks that run on the server instead of the client.
2024-11-17 08:31:55 +00:00
Jacob Fletcher
abe4cc87ca docs: updates migration guide (#9251)
Documents more breaking changes within the migration guide, improves
overview, reorganizes everything, adds section headings, table of
contents, and more.
2024-11-16 23:34:14 -05:00
Sasha
8efb926be9 chore: split e2e-admin__e2e__1 test suite (#9254)
Speeds up the longest test suite.
From:
<img width="494" alt="image"
src="https://github.com/user-attachments/assets/76d96ece-696a-4b56-9192-deaa307a0442">
To:
<img width="477" alt="image"
src="https://github.com/user-attachments/assets/a9d46180-a9cc-4ef9-8727-0130b422baf0">
2024-11-17 00:47:38 +02:00
Alessio Gravili
63cc9668df feat(richtext-lexical): allow replacing entire blocks with custom components (#9234)
With this PR, you can now customize the way that `blocks` and
`inlineBlocks` are rendered within Lexical's `BlocksFeature` by passing
your own React components.

This is super helpful when you need to create "previews" or more
accurate UI for your Lexical blocks.

For example, let's say you have a `gallery` block where your admins
select a bunch of images. By default, Lexical would just render a
collapsible with your block's fields in it. But now you can customize
the `admin.components.Block` property on your `block` config by passing
it a custom React component for us to render instead.

So using that, with this `gallery` example, you could make a dynamic
gallery React component that shows the images to your editors - and then
render our built-in `BlockEditButton` to allow your editors to manage
your gallery in a drawer.


Here is an example where the BlockEditButton is added to the default
Block Collapsible/Header:


![image](https://github.com/user-attachments/assets/db8c13f1-2650-4b33-bc11-2582bb937f3d)

---------

Co-authored-by: James <james@trbl.design>
2024-11-16 15:30:18 -07:00
Elliot DeNolf
55d5edda6b ci: update pr action 2024-11-16 17:05:44 -05:00
Elliot DeNolf
da22c6abee ci: dynamic base for lockfile pr 2024-11-16 16:01:14 -05:00
Elliot DeNolf
29c7cc8796 ci: do not run release-commenter on workflow_dispatch 2024-11-16 15:37:34 -05:00
James Mikrut
31b32ef941 feat: deprecates getPayloadHMR in favor of simpler getPayload (#9249)
Deprecates `getPayloadHMR` and simplifies this pattern into a single
`import { getPayload } from 'payload'`.

We will still retain the exported `getPayloadHMR` but it now will throw
a deprecation warning with instructions for how to migrate.
2024-11-16 15:30:05 -05:00
Elliot DeNolf
67ff23a6e2 chore(templates): deprecate vercel-postgres, superceded by with-vercel-postgres 2024-11-16 15:24:10 -05:00
Elliot DeNolf
aacc4745b6 ci: allow workflow_dispatch on post-release 2024-11-16 15:00:17 -05:00
Jacob Fletcher
ed21c1c036 fix!: proper casing for default root views (#9248)
Custom `account` and `dashboard` views now defined as lowercase in the
config. This is to maintain consistency with all other custom views
throughout the config. The underlying reason for this change is that
previously, you could define React Components directly on these
properties. Now, these are strictly _view configuration objects_, and
the property names have been adjusted in order to semantically reflect
that. These two views in particular, however, were never updated
accordingly.

## Breaking Changes

```diff
import { buildConfig } from 'payload'

const config = buildConfig({
  // ...
  admin: {
    components: {
      // ...
      views: {
        // ...
-       Account: ...
-       Dashboard: ...
+       account: ...
+       dashboard: ...
      },
    },
  },
})
```
2024-11-16 14:35:35 -05:00
Elliot DeNolf
447587a01e ci: adjust post-release pr permissions 2024-11-16 14:20:45 -05:00
Jacob Fletcher
c4269d25d3 fix(next): custom default root views (#9247)
Fixes #9246. Custom default root views (account and dashboard) were not
being properly thread to the custom component renderer. Custom account
views were also improperly _stacking_ instead of _replacing_ the default
view.

Tests for this are incoming. To properly test this we need to wrap our
default root views with custom ones, so that out existing `admin` test
suite can continue to work alongside tests specifically for this issue.
2024-11-16 14:05:50 -05:00
Elliot DeNolf
457be31ed2 chore(release): v3.0.0-beta.133 [skip ci] 2024-11-16 13:59:41 -05:00
Elliot DeNolf
bfbee4e604 ci: bump templates post release (#9241)
Automatically create a PR that bumps the lockfiles of the templates post
release.
2024-11-16 13:51:01 -05:00
James Mikrut
9b00b59df0 fix: corrects cases of false positive identification of custom id fields (#9245)
This PR fixes cases where you may have a field called `id` within a
group or a named tab, which would have incorrectly been treated as a
custom ID field for the collection.

However, custom IDs need to be defined at the root level - and now
Payload only respects custom IDs defined at the root level.
2024-11-16 16:29:48 +00:00
Elliot DeNolf
1393d84bca feat(templates): programmatic migration gen (#9238)
Programmatically generate lockfiles and postgres migrations
2024-11-15 21:47:05 -05:00
Elliot DeNolf
ff8e7bb968 chore(release): v3.0.0-beta.132 [skip ci] 2024-11-15 18:13:02 -05:00
James Mikrut
131d1be8fc fix(ui): lexical was incorrectly set to readonly in blocks (#9237)
Fixes a bug introduced in `beta-131` that rendered Lexical fields as
read-only if they were within a block.
2024-11-15 22:47:26 +00:00
Elliot DeNolf
30d66bf601 fix(templates): vercel website migrations (#9235)
Properly generate migrations for with-vercel-website template
2024-11-15 16:44:55 -05:00
Elliot DeNolf
90686fa50a chore(release): v3.0.0-beta.131 [skip ci] 2024-11-15 15:34:50 -05:00
Paul
26ffbca914 feat: sanitise access endpoint (#7335)
Protects the `/api/access` endpoint behind authentication and sanitizes
the result, making it more secure and significantly smaller. To do this:

1. The `permission` keyword is completely omitted from the result
2. Only _truthy_ access results are returned
3. All nested permissions are consolidated when possible

---------

Co-authored-by: Dan Ribbens <dan.ribbens@gmail.com>
Co-authored-by: Jacob Fletcher <jacobsfletch@gmail.com>
Co-authored-by: James <james@trbl.design>
2024-11-15 15:08:06 -05:00
Said Akhrarov
0b9d5a5ae4 docs: fix links in operators table for within and intersects (#9232)
### What?
Fixes links in Queries/Operators table for `within` and `intersects`
operator descriptions.

### Why?
So that they point to the correct destination in the docs.

### How?
Changes to `docs/queries/overview.mdx`

See here:

![image](https://github.com/user-attachments/assets/fc82a6fb-2c7c-4a1e-aa2d-128c9f5e711b)
2024-11-15 21:50:21 +02:00
Patrik
0f7276e3c4 chore: removes examples dir from jobs workflow (#9231) 2024-11-15 14:49:43 -05:00
Patrik
68458787a5 feat!: bumps date-fns to 4.1.0 (#9221) 2024-11-15 14:36:14 -05:00
Sasha
810c29b189 fix!: improve collection / global slugs type-safety in various places (#8311)
**BREAKING:**
Improves type-safety of collection / global slugs by using `CollectionSlug` / `UploadCollectionSlug` and `GlobalSlug` types instead of `string` in these places:
Adds `UploadCollectionSlug` and `TypedUploadCollection` utility types

This also changes how we suggest to add an upload collection to a cloud-storage adapter:
Before:
```ts
azureStorage({
  collections: {
    [Media.slug]: true,
  },
}) 
``` 

After:
```ts
azureStorage({
  collections: {
    media: true,
  },
}) 
```
2024-11-15 19:33:26 +00:00
Dan Ribbens
a5cae077cc fix: duplicate list preferences stored (#9185)
The collection list columns are stored as user preferences to the
payload-preferences collection. Normally one user should never have
duplicate documents with the same key. This is controlled by using an
upsert normally. The collection list does not have a good way to call
upsert and was creating preferences documents every time. This change
makes it so that existing preferences are updated rather than created
with each column change.
2024-11-15 14:22:04 -05:00
Patrik
ba06ce6338 chore(examples): migrates email example to 3.0 [skip-lint] (#9215)
Changes:

- Migrates `email` example project to `3.0` from `2.0`
- Replaces `inline-css` dependency with `juice` package instead.
- Replaces `Handlebars` dependency with `ejs` package instead.

Reason for replacing packages:
- Both `inline-css` & `Handlebars` had issues with Nextjs and its
Webpack bundling i.e does not support `require.extensions`.
- `ejs` & `juice` do not rely on `require.extensions`.
2024-11-15 14:10:24 -05:00
Patrik
7c732bec14 chore: adds email-nodemailer to area-affected dropdown in issue template (#9227) 2024-11-15 12:06:31 -05:00
Dan Ribbens
7c6f41936b feat(db-mongodb)!: update mongoose to 8.8.1 (#9115)
### What?
Upgrades mongoose from 6 to latest `v8.8.1`

Fixes https://github.com/payloadcms/payload/issues/9171

### Why?
Compatibilty with Mongodb Atlas

### How?
- Updates deps
- Changed ObjectId from bson-objectid to use `new Type.ObjectId` from
mongoose for compatibility (only inside of db-mongodb)
- Internal type adjustments

https://github.com/payloadcms/payload/discussions/9088

BREAKING CHANGES:
All projects with existing data having versions enabled, or relationship or upload fields will want to create the predefined migration that converts all strings to ObjectIDs where needed. This can be created using `payload migrate:create --file @payloadcms/mongodb/relationships-v2-v3`.
For projects making use of the exposed Models from mongoose, review the
upgrade guides from [v6 to
v7](https://mongoosejs.com/docs/7.x/docs/migrating_to_7.html) and [v7 to
v8](https://mongoosejs.com/docs/migrating_to_8.html) and make
adjustments as needed.

---------

Co-authored-by: Sasha <64744993+r1tsuu@users.noreply.github.com>
2024-11-15 12:03:56 -05:00
Jacob Fletcher
028153f5a4 docs: removes root endpoints from migration guide (#9224) 2024-11-15 11:30:40 -05:00
Jarrod Flesch
2801c41d91 docs: fixes incorrect useField example (#9222) 2024-11-15 10:01:52 -05:00
Germán Jabloñski
82e72fa7f2 feat(richtext-lexical, ui): add icon if link opens in new tab (#9211)
https://github.com/user-attachments/assets/46eebd2f-3965-40be-a7c6-e68446d32398

---------

Co-authored-by: Tylan Davis <hello@tylandavis.com>
2024-11-15 14:55:43 +00:00
Jarrod Flesch
20c899286e chore: export ListHeaderProps (#9217)
Exports ListHeaderProps so others can use them.
2024-11-15 08:23:47 -05:00
Alessio Gravili
729488028b feat(richtext-lexical): add tooltips to toolbar dropdown items (#9218)
Previously, if the dropdown item text is cut off due to length, there
was no way to view the full text.

Now, you can hover:

![CleanShot 2024-11-14 at 18 55
11@2x](https://github.com/user-attachments/assets/b160c172-c78a-4eb5-9fb3-b4ef8aee7eb5)
2024-11-15 02:29:12 +00:00
Jakob Ortmann
e6e0cc2a63 docs: reflect changes to uploadthing config in docs (#9201)
Updates docs to new config specs changed by #8346
2024-11-14 15:22:02 -05:00
Francisco Lourenço
2d2d020c29 feat(db-mongodb): support query options in db update operations (#9191)
The mongodb adapter `updateOne` method accepts an `options` argument
that allows query options to be passed to mongoose. This parameter was
added in https://github.com/payloadcms/payload/pull/8397 to support the
`upsert` operation.

This `options` parameter can also be useful when using the database
adaptor directly without going through the local api. It is true that
the Mongoose models could be used directly in such situations, but the
adapter methods include a lot of useful functionality, like for instance
the sanitization of document and relationship ids, so it is desirable to
be able to use the adapter functions while still being able to provide
mongoose query options (e.g. `{timestamps: false}`).

This PR adds the same options parameter to the other update methods of
the mongodb adapter.
2024-11-14 15:15:03 -05:00
Paul
315b4e566b fix(ui): jumping hasmany uploads when form is submitting or in readonly mode (#9198) 2024-11-14 14:39:31 -05:00
Jacob Fletcher
2d7626c3e9 perf: removes undefined props from rsc requests (#9195)
This is in effort to reduce overall HTML bloat, undefined props still go
through the request as `$undefined` and must be explicitly omitted.
2024-11-14 18:22:42 +00:00
Jarrod Flesch
e75527b0a1 chore: clean up types for HiddenField and WatchCondition (#9208)
### What?
Aligns types for HiddenField and the WatchCondition component with the
rest of the fields. Since path is required when rendering a Field
component, there is no need to keep it optional in the WatchCondition
component.

### Why?
Hidden fields were requiring the `field` property to be passed, but the
only reason it needed it was to allow the path to fallback to name if
path was not passed. But path is required so there is no need for this
anymore.

This makes using the HiddenField simpler now.

### How?
Adjusts type on the HiddenField and the WatchCondition component.
2024-11-14 12:56:17 -05:00
Jacob Fletcher
5482e7ea15 perf: removes i18n.supportedLanguages from client config (#9209)
Similar to https://github.com/payloadcms/payload/pull/9195 but
specifically removing `i18n.supportedLanguages` from the client config.
This is a potentially large object that does not need to be sent through
the network when making RSC requests.
2024-11-14 12:48:00 -05:00
Jarrod Flesch
77c99c2f49 feat!: re-order DefaultCellComponentProps generics (#9207)
### What?
Changes the order of the `DefaultCellComponentProps` generic type,
allowing us to infer the type of cellData when a ClientField type is
passed as the first generic argument. You can override the cellData type
by passing the second generic.

Previously:
```ts
type DefaultCellComponentProps<TCellData = any, TField extends ClientField = ClientField>
```

New:
```ts
type DefaultCellComponentProps<TField extends ClientField = ClientField, TCellData = undefined>
```

### Why?
Changing the ClientField type to be the first argument allows us to
infer the cellData value type based on the type of field.

I could have kept the same signature but the usage would look like:
```ts
// Not very DX friendly
const MyCellComponent<DefaultCellComponentProps<,ClientField>> = () => null
```

### How?
The changes made
[here](https://github.com/payloadcms/payload/compare/chore/beta/simplify-DefaultCellComponentProps?expand=1#diff-24f3c92e546c2be3fed0bab305236bba83001309a7239c20a3e3dbd6f5f71dc6R29-R73)
allow this. You can override the type by passing in the second argument
to the generic.
2024-11-14 12:31:42 -05:00
Elliot DeNolf
5ff1bb366c chore: misc cleanup (#9206)
- Proper error logger usage
- Some no-fallthrough warning cleanup
2024-11-14 11:14:08 -05:00
James Mikrut
e6d04436a8 fix(ui): fixes layout shift when form is submitted (#9184)
Some fields cause layout shift when you submit the form. This PR reduces
that flicker.
2024-11-14 02:57:01 +00:00
Jarrod Flesch
81099cbb04 chore: improve custom server cell types (#9188)
### What?
Exposes DefaultServerCellComponentProps type for custom server cell
components.

### Why?
So users can type their custom server cell components properly.
2024-11-13 17:03:03 -05:00
Sasha
4509c38f4c docs: add within and intersects operators documentation (#9194)
Adds documentation for `within` and `intersects` operators.

#### Querying - within

In order to do query based on whether points are within a specific area
defined in GeoJSON, you can use the `within` operator.
Example:
```ts
const polygon: Point[] = [
  [9.0, 19.0], // bottom-left
  [9.0, 21.0], // top-left
  [11.0, 21.0], // top-right
  [11.0, 19.0], // bottom-right
  [9.0, 19.0], // back to starting point to close the polygon
]

payload.find({
  collection: "points",
  where: {
    point: {
      within: {
        type: 'Polygon',
        coordinates: [polygon],
      },
    },
  },
})
```


#### Querying - intersects

In order to do query based on whether points intersect a specific area
defined in GeoJSON, you can use the `intersects` operator.
Example:
```ts
const polygon: Point[] = [
  [9.0, 19.0], // bottom-left
  [9.0, 21.0], // top-left
  [11.0, 21.0], // top-right
  [11.0, 19.0], // bottom-right
  [9.0, 19.0], // back to starting point to close the polygon
]

payload.find({
  collection: "points",
  where: {
    point: {
      intersects: {
        type: 'Polygon',
        coordinates: [polygon],
      },
    },
  },
})
```
2024-11-13 21:59:22 +00:00
Jarrod Flesch
90e6a4fcd8 docs: note about passing req to local operations (#9192) 2024-11-13 16:49:50 -05:00
Elliot DeNolf
4690cd819a feat(storage-uploadthing)!: upgrade to v7 (#8346)
Upgrade uploadthing to v7

The `options` that can be passed to the plugin now mirror the
`UTApiOptions` of v7.

The most notable change is to pass `token` with
`process.env.UPLOADTHING_TOKEN` instead of `apiKey` with
`process.env.UPLOADTHING_SECRET`.

```diff
options: {
- apiKey: process.env.UPLOADTHING_SECRET,
+ token: process.env.UPLOADTHING_TOKEN,
  acl: 'public-read',
},
2024-11-13 21:27:02 +00:00
Dan Ribbens
afd69c4d54 chore: fix community e2e test (#9187) 2024-11-13 15:47:45 -05:00
Dan Ribbens
de52490a98 chore: fix community test (#9186) 2024-11-13 15:37:44 -05:00
Jarrod Flesch
129fadfd2c fix: wires up abort controller logic for list columns (#9180)
### What?
List column state could become out of sync if toggling columns happened
in rapid succession as seen in CI. Or when using a spotty connection
where responses could come back out of order.

### Why?
State was not being preserved between toggles. Leading to incorrect
columns being toggled on/off.

### How?
Updates internal column state before making the request to the server so
when a future toggle occurs it has up to date state of all columns. Also
introduces an abort controller to prevent the out of order response
issue.
2024-11-13 14:58:49 -05:00
Jacob Fletcher
cea7d58d96 docs: updates and improves migration guide (#9176)
This is a first pass at updating the 3.0 migration guide. While this
makes significant changes and improvements to the guide, it does not
necessarily reflect _all_ of the migration steps needed in their
entirety quite yet. Those will continue to come in.

Key changes:
- Cleans up outdated examples and removes old ones
- Updates code snippets to latest patterns
- Diffs everything for improved readability
2024-11-13 14:29:50 -05:00
Elliot DeNolf
6baff8a3ba chore(release): v3.0.0-beta.130 [skip ci] 2024-11-13 14:18:00 -05:00
James Mikrut
ced79be591 Chore/clean community (#9181)
Cleans up _community test suite
2024-11-13 14:12:19 -05:00
Sasha
5b9cee67c0 fix(db-postgres): create relationship-v2-v3 migration (#9178)
### What?
This command from here:
https://github.com/payloadcms/payload/pull/6339
```sh
payload migrate:create --file @payloadcms/db-postgres/relationships-v2-v3
```
stopped working after db-postgers and drizzle packages were separated 

### How?
Passes correct `dirname` to `getPredefinedMigration`

Additionally, adds support for `.js` files in `getPredefinedMigration`
2024-11-13 19:02:17 +00:00
Jarrod Flesch
bcbca0e44a chore: improves field types (#9172)
### What?
Ensures `path` is required and only present on the fields that expect it
(all fields except row).

Deprecates `useFieldComponents` and `FieldComponentsProvider` and
instead extends the RenderField component to account for all field
types. This also improves type safety within `RenderField`.

### Why?
`path` being optional just adds DX overhead and annoyance. 

### How?
Added `FieldPaths` type which is added to iterable field types. Placed
`path` back onto the ClientFieldBase type.
2024-11-13 13:53:47 -05:00
Paul
cd95daf029 fix: add inline <head><style> to ensure the order of declared css layers as much as possible (#9123)
Should help alleviate some problems outlined in
https://github.com/payloadcms/payload/issues/8878
2024-11-13 13:49:38 -05:00
James Mikrut
9da85430a5 feat: adds ability to define base filter for list view (#9177)
Adds the ability to define base list view filters, which is super
helpful when you're doing multi-tenant things in Payload.
2024-11-13 18:34:01 +00:00
Paul
f4d526d6e5 fix: fallbackLocale not respecting default settings, locale specific fallbacks and not respecting 'none' or false (#8591)
This PR fixes and improves a few things around localisation and
fallbackLocale:
- For the REST API `fallbackLocale` and `fallback-locale` are treated
the same for consistency with the Local API
- `fallback: false` in config is now respected, by default results will
not fallback to `defaultLocale` unless this config is true, can also be
overridden by providing an explicit `fallbackLocale` in the request
- locale specific fallbacks will now take priority over `defaultLocale`
unless an explicit fallback is provided
- Fixes types on operations to allow `'none'` as a value for
fallbackLocale
- `fallback` is now true by default if unspecified

Closes https://github.com/payloadcms/payload/issues/8443
2024-11-13 12:13:31 -06:00
Patrik
3b55458c0d fix(next): safely check for state when creating first user (#9168)
On createFirstUser, state from form-state was returning null.

![Screenshot 2024-11-13 at 9 58
04 AM](https://github.com/user-attachments/assets/19019e3e-09fc-42e6-9b9a-9198772d9133)

Only return `state` if response from form-state is not null.
2024-11-13 11:19:43 -05:00
Patrik
51dc3f06b1 chore(templates): update lock file for website template (#9169) 2024-11-13 16:15:18 +00:00
Dan Ribbens
d6282221db feat: customize log levels and downgrade common errors to info (#9156)
### What?

Allows configuration of the log level based on the error being thrown
and also downgrades common errors to be info instead of error by
default.

### Why?

Currently all errors result in logger.error being called which can
polute the logs with junk that is normal and doesn't need attention.

### How?

Adds a config property called `loggingLevels` that is used to override
the default log levels based on the name of the error being thrown.
Sanitize config will provide the defaulted 'info' level errors which can
be overriden in the config.

Before
![Screenshot 2024-11-12
144459](https://github.com/user-attachments/assets/47318329-23b7-4627-afc4-a0bcf4dc3d58)

After

![image](https://github.com/user-attachments/assets/85b06be4-0ab8-4ca2-b237-d6a4d54add3a)
2024-11-13 09:24:53 -05:00
Elliot DeNolf
f264c8087a chore: add download/week to README 2024-11-12 21:51:11 -05:00
Elliot DeNolf
1b16730b20 chore: remove useless script, can use HUSKY=0 2024-11-12 20:50:52 -05:00
Jacob Fletcher
f6bdc0aaf6 feat(next): initializes nav group prefs on the server and consolidates records (#9145) 2024-11-12 20:05:12 -05:00
Jarrod Flesch
a8e3095e45 fix: expose server and client props to custom list slot components (#9159)
### What?
Adds `serverProps` and `clientProps` to custom list view slot
components.

### Why?
They were missing and should be exposed.

### How?
Created custom types for list slot components and threads them through
into `renderListSlots` function and passes them through to each
`RenderServerComponent` that renders list view slot components.
2024-11-12 18:18:19 -05:00
Paul
5ac4e73991 feat(templates): update config structure in website template to be more clear (#9161) 2024-11-12 22:56:32 +00:00
Jacob Fletcher
9ee6425761 docs: updates custom components and field props (#9157) 2024-11-12 22:35:29 +00:00
Elliot DeNolf
8c2fc71149 chore(release): v3.0.0-beta.129 [skip ci] 2024-11-12 17:07:25 -05:00
Patrik
88bef2e140 chore: updates flaky uploads tests (#9149)
Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com>
2024-11-12 16:41:08 -05:00
Alessio Gravili
a1c99c8b45 fix(richtext-lexical): inline blocks drawer not rendering any fields due to incorrect schemapath suffix (#9158) 2024-11-12 16:31:20 -05:00
Elliot DeNolf
d3cd9baa9b chore(release): v3.0.0-beta.128 [skip ci] 2024-11-12 15:52:52 -05:00
Sasha
64967e4ca6 fix(next): disable turbopack serverExternalPackages warnings (#9147)
### What?
Disables these annoying warnings when running `pnpm dev --turbo`
<img width="656" alt="image"
src="https://github.com/user-attachments/assets/7d0a2990-b5fd-4f5d-a025-665e8e3a7880">
https://github.com/vercel/next.js/issues/68805

### How?
Patches `console.warn` in `withPayload.js`

Can be disabled with `PAYLOAD_PATCH_TURBOPACK_WARNINGS=false` env
variable
2024-11-12 20:20:11 +00:00
James Mikrut
e0309a1dd0 fix: allow specifying queue (#9151)
Allows user to specify a queue when calling `payload.jobs.queue()`.
Closes #9133
2024-11-12 15:00:32 -05:00
James Mikrut
6bb4067bb3 feat: adds option to mongoose to ensure indexes (#9155)
Adds option `ensureIndexes` to Mongoose adapter, which will ensure
indexes are ready prior to completing connection.
2024-11-12 14:42:25 -05:00
Jarrod Flesch
a3ebf51d6e fix: incorrectly looking for schema paths when upload is not enabled (#9146)
### What?
![CleanShot 2024-11-12 at 12 17
56](https://github.com/user-attachments/assets/74b906a3-7e76-4ee9-8b18-bd24dd7fca82)

### Why?
Should not be attaching fields that it does not need.

### How?
Conditionally render slate upload drawer like we do with the toggler.
2024-11-12 14:02:08 -05:00
Elliot DeNolf
280448dd02 chore: remove e-commerce template from readme 2024-11-12 13:36:12 -05:00
Tobias Odendahl
09c41d5c86 fix(db-mongodb)!: use dbName for mongodb model (#9107)
### What?
Uses the `collection.dbName` property for the Mongoose model, if
defined.

### Why?
Currently, `collection.dbName` is used for the version name but not for
the actual collection name. Additionally, `autoPluralization` modifies
the `dbName` regardless. This behavior is inconsistent and contradicts
the documentation.

### How?
- Utilize `collection.dbName` instead of `collection.slug`.
- Disable `autoPluralization` for collections with a defined `dbName`.

Related: https://github.com/payloadcms/payload/discussions/9058

**BREAKING CHANGES**
If a `dbName` was previously provided, it will now be used as the
MongoDB collection name instead of the collection `slug`.
`autoPluralization` will not be applied to `dbName`.

---------

Co-authored-by: Dan Ribbens <dan.ribbens@gmail.com>
2024-11-12 13:31:23 -05:00
Elliot DeNolf
def595e645 feat(templates): add with-vercel-website (#9144)
Add new `with-vercel-website` that uses the website template as a base.
2024-11-12 13:12:37 -05:00
James Mikrut
8dd7e989ef Chore/next 15 docs (#9148)
Closes #8995
2024-11-12 17:59:52 +00:00
Nate
7619592fb6 chore: update README asset image (#9143) 2024-11-12 12:51:25 -05:00
James Mikrut
432741bca3 chore: docs improvements (#9142)
Improvements to `select` docs and `jobs-queue` docs
2024-11-12 17:00:21 +00:00
Jarrod Flesch
97cffa51f8 chore: improves abort controller logic for server functions (#9131)
### What?
Removes abort controllers that were shared globally inside the server
actions provider.

### Why?
Constructing them in this way will cause different fetches using the
same function to cancel one another accidentally.

These are currently causing issues when two components call server
functions, even different functions, because the global ref inside was
being overwritten and aborting the previous one.

### How?
Standardizes how we construct and destroy abort controllers. This PR is focused around creating them to pass into the exposed serverAction provider functions. There are other places where this pattern can be applied.
2024-11-12 11:20:17 -05:00
Elliot DeNolf
7cd805adb9 fix(cpa): use proper branch tag (#9141)
The pinned git tag was not being threaded all the way through to where
the download was occurring.
2024-11-12 16:13:19 +00:00
Patrik
48d0faecae fix(next, ui): respect access of user for document locking (#9139) 2024-11-12 15:49:58 +00:00
Alessio Gravili
4f6651433c chore: ensure all packages have consistent licenses and package.json metadata (#9079) 2024-11-12 10:27:36 -05:00
Elliot DeNolf
8a67098f6c chore: add lint commit to .git-blame-ignore-revs 2024-11-12 10:20:42 -05:00
Alessio Gravili
03291472d6 chore: bump all eslint dependencies, run lint and prettier (#9128)
This fixes a peer dependency error in our monorepo, as
eslint-plugin-jsx-a11y finally supports eslint v9.

Additionally, this officially adds TypeScript 5.6 support for
typescript-eslint.
2024-11-12 10:18:22 -05:00
Sasha
3298113a93 fix(ui): pass correct relationTo to locked documents creation (#9137)
### What?
Relationships within `payload-locked-documents` collection were stored
incorrectly with `relationTo` as an array:
<img width="316" alt="image"
src="https://github.com/user-attachments/assets/f84b9807-6032-4ea2-8563-5c7d13306a4a">

Example how it should be:
<img width="405" alt="image"
src="https://github.com/user-attachments/assets/80fb54fd-7c2e-4c90-bd2b-dec8e7a06040">


This additionally caused issue that `value` wasn't converted to
`ObjectID`
2024-11-12 16:14:28 +02:00
Sasha
b878daf27a feat(db-postgres): deep querying on json and rich text fields (#9102)
### What?
Allows to query on JSON / Rich Text fields in Postgres the same way as
in Mongodb with any nesting level.

Example:
Data:
```js
{
  json: {
    array: [
      {
        text: 'some-text', // nested to array + object
        object: {
          text: 'deep-text', // nested to array + 2x object
          array: [10], // number is nested to array + 2x object + array
        },
      },
    ],
  }
}
```
Query:
```ts
payload.find({
  collection: 'json-fields',
  where: {
    and: [
      {
        'json.array.text': {
          equals: 'some-text',
        },
      },
      {
        'json.array.object.text': {
          equals: 'deep-text',
        },
      },
      {
        'json.array.object.array': {
          in: [10, 20],
        },
      },
      {
        'json.array.object.array': {
          exists: true,
        },
      },
      {
        'json.array.object.notexists': {
          exists: false,
        },
      },
    ],
  },
})
```

### How?
Utilizes [the `jsonb_path_exists` postgres
function](https://www.postgresql.org/docs/current/functions-json.html)
2024-11-12 09:26:04 +02:00
Germán Jabloñski
23907e432e feat(richtext-lexical): add useAsTitle to the popup links label (#8718)
Now we show not only the collection being linked to, but also the
document title:

![image](https://github.com/user-attachments/assets/5ba5713a-b051-4f11-ae2a-d5b50a25966b)

Previously this example was just displayed as: `Linked to Users`

- I've added a loading state in case the request is slow (verified with
fake slow connection).
- I have verified that if the `useAsTitle` is not defined, it correctly
fallbacks to the id

Please let me know if the same needs to be done with Slate.

---------

Co-authored-by: Alessio Gravili <alessio@gravili.de>
2024-11-12 05:07:50 +00:00
Alessio Gravili
a30eeaf644 feat(richtext-lexical): backport relevant from lexical playground between 0.18.0 and 0.20.0 (#9129) 2024-11-12 04:58:28 +00:00
Jacob Fletcher
df764dbbef docs: improves component paths and import map (#9118) 2024-11-11 22:42:48 -05:00
Alessio Gravili
6899a3cc27 fix(db-mongodb): destructuring error when trying to filter date fields by string query (#9116)
Previously, when filtering the internal link relationship in lexical by
typing in the relationship field, it would throw an error, as that
relationship field has a relation to "date-fields".
2024-11-11 20:41:02 -07:00
Alessio Gravili
7261faac57 perf: upgrade pino-pretty. This reduces bundle size and total amount of dependencies from 94 => 85 (#9127)
Previous:

![CleanShot 2024-11-11 at 19 48
05@2x](https://github.com/user-attachments/assets/e1ab5602-92f0-4221-9e7c-98bbba17db71)

94 Dependencies

Now:

![CleanShot 2024-11-11 at 19 48
43@2x](https://github.com/user-attachments/assets/ce5b7ecb-5128-4173-9109-9ddf3493301b)

85 Dependencies
2024-11-11 20:40:20 -07:00
Germán Jabloñski
7767c94bd8 feat(richtext-lexical)!: upgrade lexical from 0.18.0 to 0.20.0 (#9126)
I'm needing https://github.com/facebook/lexical/pull/6693

I'm going to keep the dependency bump and feature updates in separate
PRs unless they're breaking changes.*

**BREAKING:**

This upgrades our lexical dependencies from 0.18.0 to 0.20.0. If you
have lexical dependencies installed in your project, you will have to
upgrade those.

Additionally, the lexical team may introduce breaking changes in this
upgrade. If you use lexical APIs directly, please consult their
changelog for more information:
https://github.com/facebook/lexical/releases
2024-11-12 03:39:36 +00:00
Alessio Gravili
2ad991759f fix(ui): error in filtered relationship field component while scrolling, if the select option label is a number (#9117)
1. Open fields test suite
2. Type in relationship field, that has a relation to the numbers
collection
3. Scroll

You will get an error, as the label for the entry corresponding to the
numbers collection is of type number, and it attempts to use the
.toString() method on it
2024-11-12 03:37:53 +00:00
Alessio Gravili
9c559d7304 chore: fix live-preview tests against prod (#9122)
Live preview e2e tests had no CSS when tested against prod.

For all our other tests, we have a separate test/app directory that
imports CSS. Otherwise, the root-level /app directory is used.

For live-preview, we currently always run against test/live-preview/app,
that has no CSS import.

This PR adds a new test/live-preview/prod/app directory that imports CSS
and is used when we run tests against prod.

In order for this to work, I had to make import map generation smarter
2024-11-11 19:28:55 -07:00
Paul
d8391389ab feat(docs): add example for customising the filename of an upload via hooks (#9124) 2024-11-12 00:59:27 +00:00
Alessio Gravili
570c610eed docs: fix queue docs examples, link to qs-esm instead of qs (#9120) 2024-11-11 23:35:51 +00:00
Jarrod Flesch
9dbf1b7279 chore: imports reInitializeDB into live-preview test suite 2024-11-11 16:48:43 -05:00
Jarrod Flesch
71db10d68f chore: stabalize live preview test suite 2024-11-11 16:24:45 -05:00
Jacob Fletcher
c96fa613bc feat!: on demand rsc (#8364)
Currently, Payload renders all custom components on initial compile of
the admin panel. This is problematic for two key reasons:
1. Custom components do not receive contextual data, i.e. fields do not
receive their field data, edit views do not receive their document data,
etc.
2. Components are unnecessarily rendered before they are used

This was initially required to support React Server Components within
the Payload Admin Panel for two key reasons:
1. Fields can be dynamically rendered within arrays, blocks, etc.
2. Documents can be recursively rendered within a "drawer" UI, i.e.
relationship fields
3. Payload supports server/client component composition 

In order to achieve this, components need to be rendered on the server
and passed as "slots" to the client. Currently, the pattern for this is
to render custom server components in the "client config". Then when a
view or field is needed to be rendered, we first check the client config
for a "pre-rendered" component, otherwise render our client-side
fallback component.

But for the reasons listed above, this pattern doesn't exactly make
custom server components very useful within the Payload Admin Panel,
which is where this PR comes in. Now, instead of pre-rendering all
components on initial compile, we're able to render custom components
_on demand_, only as they are needed.

To achieve this, we've established [this
pattern](https://github.com/payloadcms/payload/pull/8481) of React
Server Functions in the Payload Admin Panel. With Server Functions, we
can iterate the Payload Config and return JSX through React's
`text/x-component` content-type. This means we're able to pass
contextual props to custom components, such as data for fields and
views.

## Breaking Changes

1. Add the following to your root layout file, typically located at
`(app)/(payload)/layout.tsx`:

    ```diff
    /* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
    /* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
    + import type { ServerFunctionClient } from 'payload'

    import config from '@payload-config'
    import { RootLayout } from '@payloadcms/next/layouts'
    import { handleServerFunctions } from '@payloadcms/next/utilities'
    import React from 'react'

    import { importMap } from './admin/importMap.js'
    import './custom.scss'

    type Args = {
      children: React.ReactNode
    }

+ const serverFunctions: ServerFunctionClient = async function (args) {
    +  'use server'
    +  return handleServerFunctions({
    +    ...args,
    +    config,
    +    importMap,
    +  })
    + }

    const Layout = ({ children }: Args) => (
      <RootLayout
        config={config}
        importMap={importMap}
    +  serverFunctions={serverFunctions}
      >
        {children}
      </RootLayout>
    )

    export default Layout
    ```

2. If you were previously posting to the `/api/form-state` endpoint, it
no longer exists. Instead, you'll need to invoke the `form-state` Server
Function, which can be done through the _new_ `getFormState` utility:

    ```diff
    - import { getFormState } from '@payloadcms/ui'
    - const { state } = await getFormState({
    -   apiRoute: '',
    -   body: {
    -     // ...
    -   },
    -   serverURL: ''
    - })

    + const { getFormState } = useServerFunctions()
    +
    + const { state } = await getFormState({
    +   // ...
    + })
    ```

## Breaking Changes

```diff
- useFieldProps()
- useCellProps()
```

More details coming soon.

---------

Co-authored-by: Alessio Gravili <alessio@gravili.de>
Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com>
Co-authored-by: James <james@trbl.design>
2024-11-11 13:59:05 -05:00
Jessica Chowdhury
3e954f45c7 fix: empty publish dropdown when localization is false (#9106)
Closes https://github.com/payloadcms/payload/issues/9092
2024-11-11 12:19:29 -05:00
Paul
9a970d21a9 fix: custom id field not shown depending on field and db types (#9091)
Closes https://github.com/payloadcms/payload/issues/9080
2024-11-11 16:42:06 +00:00
Elliot DeNolf
8a20231d40 ci: debug publish-canary job 2024-11-11 11:21:16 -05:00
Nate
26691377d2 chore: update README.md asset URL (#9104)
Updated README asset URL

<!--

Thank you for the PR! Please go through the checklist below and make
sure you've completed all the steps.

Please review the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository if you haven't already.

The following items will ensure that your PR is handled as smoothly as
possible:

- PR Title must follow conventional commits format. For example, `feat:
my new feature`, `fix(plugin-seo): my fix`.
- Minimal description explained as if explained to someone not
immediately familiar with the code.
- Provide before/after screenshots or code diffs if applicable.
- Link any related issues/discussions from GitHub or Discord.
- Add review comments if necessary to explain to the reviewer the logic
behind a change

### What?
Update README asset URL for hero

### Why?
Reflect latest Payload branding

### How?
URL change for correct asset

-->
2024-11-11 10:41:48 -05:00
Paul
8201a6cacd chore(templates): remove old ecommerce template (#8916)
removes the previous v2 ecommerce template from the repo ahead of v3
launch until ecomm v3 is ready
2024-11-11 10:19:24 -05:00
Nate
d7fc944792 fix: update README with new asset, image URL (#9099) 2024-11-11 10:18:11 -05:00
Elliot DeNolf
0c19afcf91 chore(release): v3.0.0-beta.127 [skip ci] 2024-11-11 09:56:14 -05:00
Sasha
0a15388edb feat(db-postgres): add point field support (#9078)
### What?
Adds full support for the point field to Postgres and Vercel Postgres
adapters through the Postgis extension. Fully the same API as with
MongoDB, including support for `near`, `within` and `intersects`
operators.

Additionally, exposes to adapter args:
*
`tablesFilter`https://orm.drizzle.team/docs/drizzle-kit-push#including-tables-schemas-and-extensions.
* `extensions` list of extensions to create, for example `['vector',
'pg_search']`, `postgis` is created automatically if there's any point
field

### Why?
It's essential to support that field type, especially if the postgres
adapter should be out of beta on 3.0 stable.

### How?
* Bumps `drizzle-orm` to `0.36.1` and `drizzle-kit` to `0.28.0` as we
need this change https://github.com/drizzle-team/drizzle-orm/pull/3141
* Uses its functions to achieve querying functionality, for example the
`near` operator works through `ST_DWithin` or `intersects` through
`ST_Intersects`.
* Removes MongoDB condition from all point field tests, but keeps for
SQLite

Resolves these discussions:
https://github.com/payloadcms/payload/discussions/8996
https://github.com/payloadcms/payload/discussions/8644
2024-11-11 09:31:47 -05:00
Ruy Monteiro
a421503111 docs(storage-uploadthing): fix typo in README.md (#9090) 2024-11-10 07:59:46 +00:00
Nathan Clevenger
b86594f3f9 docs: fixed typo in onSuccess (#9096)
Changed `if the task fails` to `if the task succeeds`
2024-11-10 00:23:32 -07:00
Dan Ribbens
e3172f1e39 fix: migrateRefresh migrates without previously ran migrations (#9073)
fix: migrateRefresh migrates without previously ran migrations
chore: adds tests for database migrate:fresh and migrate:refresh

---------

Co-authored-by: Sasha <64744993+r1tsuu@users.noreply.github.com>
2024-11-08 15:35:46 -05:00
Dan Ribbens
ee117bb616 fix!: handle custom id logic in mongodb adapter (#9069)
### What?
Moved the logic for copying the data.id to data._id to the mongoose
adapter.

### Why?
If you have any hooks that need to set the `id`, the value does not get
sent to mongodb as you would expect since it was copied before the
beforeValidate hooks.

### How?
Now data._id is assigned only in the mongodb adapter's `create`
function.

BREAKING CHANGES:
When using custom ID fields, if you have any collection hooks for
beforeValidate, beforeChange then `data._id` will no longer be assigned
as this happens now in the database adapter. Use `data.id` instead.
2024-11-08 15:34:19 -05:00
Jessica Chowdhury
dc111041cb fix: incorrect form changed state after doc drawer edit (#9025)
Closes #9000

When you update a relationship document via the document drawer, the
initial document is registering `modified: true`. We should only set
modified to true on the initial document if the relationship id has
changed.
2024-11-08 14:04:37 -05:00
Sasha
f67761fe22 fix(db-mongodb): totalDocs with joins (#9056)
### What?
Fixes issue with incorrect `totalDocs` value when an aggregation is used
for `find`.
Previously, `limit: 5` for example always returned `totalDocs: 5`.

### Why?
`totalDocs` must be returned correctly.

### How?
Removes `$limit` from the pipeline, as `Model.aggregatePaginate` handles
it by itself.
2024-11-08 14:04:24 -05:00
Jessica Chowdhury
010ac2ac0c fix: login redirect missing route (#8990)
Closes #8920 - login form does not redirect after form submit.

In `handleAuthRedirect` the route parameter was unintentionally getting
overwritten.
2024-11-08 13:41:15 -05:00
Dan Ribbens
d20445b6f3 fix(db-mongodb): write migrations index file (#9071)
fix: remove 'undefined' written into mongodb migrations
fix: write migrations index file
2024-11-08 13:26:45 -05:00
Kendell Joseph
1f26237ba1 chore: correctly redirects after in-activity (#9070)
Issues: 
  - https://github.com/payloadcms/payload/issues/5009
  - https://github.com/payloadcms/payload/pull/8809
2024-11-08 12:20:44 -06:00
Sasha
721ae79716 fix: populate with find operation (#9087)
### What?
Because of my error, we didn't pass `populate` to `findOperation` from
the Local API.

### Why?
`populate` must work for every operation that has `depth`.

### How?
Passes `populate` in `operations/local/find.ts`, ensures it works with
the test, checked that other operations pass it.
2024-11-08 18:45:11 +02:00
Paul
1584c41790 fix(docs): auth page email broken link (#9089) 2024-11-08 16:14:26 +00:00
James Mikrut
963fee83e0 Update README.md 2024-11-08 09:18:19 -05:00
Paul
b32c4defd9 fix(templates): use direct blocks props instead of drilling them out of pages (#9074) 2024-11-08 04:36:40 +00:00
Steve
a6e7305696 fix(translations): improves Bulgarian translations (#9031)
### What?

Updated the Bulgarian translations for improved accuracy.
- Fixed translations that were not in Bulgarian. (Czech and Russian)
- Fixed translations that contained typos.
- Improved some translations to use more accurate wording.

Co-authored-by: Teodora Yaneva <theodorayaneva@gmail.com>
2024-11-07 17:56:57 -05:00
Alessio Gravili
0cd83f0591 perf: upgrade json-schema-to-typescript and various other dependencies (#9076)
This further reduces the amount of dependencies installed by payload.
Special thanks to @benmccann for this PR
(https://github.com/bcherny/json-schema-to-typescript/pull/639) !

json-schema-to-typescript before this PR (15.0.1):

![CleanShot 2024-11-07 at 15 35
53@2x](https://github.com/user-attachments/assets/83cb671c-82a8-4b59-b488-cf941d673c8e)

14mb, 37 dependencies

json-schema-to-typescript after this PR (15.0.3):

![CleanShot 2024-11-07 at 15 36
08@2x](https://github.com/user-attachments/assets/d9463275-37e5-452e-aca5-9c1bdeb2a435)

11 MB, 14 dependencies

This PR also upgrades:
- console-table-printer
- croner
- get-tsconfig
- jose
- pino-pretty
- ts-essentials
- tsx
2024-11-07 22:54:45 +00:00
James Mikrut
cd49783105 Update README.md 2024-11-07 17:53:55 -05:00
Elliot DeNolf
5b77653fe7 ci: run build when templates change 2024-11-07 15:45:57 -05:00
Paul
1d5d30391e fix(templates): fix website template's missing paragraph feature allowing you to change text from headings (#9072) 2024-11-07 20:36:01 +00:00
Elliot DeNolf
2c0caab761 ci: remove unused generated-templates, replaced with template build job 2024-11-07 15:30:11 -05:00
Mikkel Madsen
57e535e646 fix: corrected translation emailOrPasswordIncorrect for Danish (da) (#9063)
## Description
Corrected `emailOrPasswordIncorrect` translation for Danish (da)

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

Type of change
- [x] fix (non-breaking change)
2024-11-07 15:26:32 -05:00
Elliot DeNolf
8c10a23fa2 test: naming 2024-11-07 13:10:05 -05:00
Elliot DeNolf
80b69ac53d test: naming with matrix (#9064)
Better test naming when tests are in a matrix.
2024-11-07 13:08:32 -05:00
Dan Ribbens
e2607d4faa chore: database transactions docs formatting (#9068) 2024-11-07 12:48:17 -05:00
Tylan Davis
1267aedfd3 docs: closes <strong> tags properly on jobs queue documentation (#9067)
### What?

Fixes a formatting issue that prevents
payloadcms.com/docs/beta/jobs-queue/overview from displaying properly.
There were a couple `<strong>` tags in the `jobs-queue/overview` docs
that did not have proper closing `</strong>` tags.
2024-11-07 12:42:16 -05:00
Elliot DeNolf
e907724af7 chore(cpa): use git tags for template urls (#9065)
`create-payload-app` will now use git tags when cloning down the
templates instead of using latest from a branch.

The mechanism is cpa will read its own package.json version and use that
as a git tag prefixed w/ `v`
2024-11-07 11:47:26 -05:00
Elliot DeNolf
320916f542 ci: build templates with packed deps (#8970)
Build templates using packed deps from the repo.
2024-11-07 10:49:21 -05:00
Paul
015580aa32 feat(templates): add adminThumbnail to media in website template (#9059) 2024-11-07 03:25:27 +00:00
Patrik
f1ba9ca82a chore: updates flaky locked-documents e2e tests (#9055) 2024-11-06 16:49:06 -05:00
Elliot DeNolf
f878e35cc7 chore(release): v3.0.0-beta.126 [skip ci] 2024-11-06 16:23:57 -05:00
Dan Ribbens
f0f96e7558 fix: allow workflows to be empty or undefined (#9039)
### What?

- Makes `jobs.workflows` optional
- Dynamically include the `workflowSlugs` select field in the jobs
collection as needed

### Why?

When configuring jobs, it should be possible to define `job` with just
some simple tasks and not be forced to define workflows.

### How?

Workflows type was made optional and optional chaining is added where
needed. The workflowSlugs field is added to the jobs collection if
workflows are defined.

Fixes #

When using postgres, the workflowSlugs being an empty enum cause an
error when drizzle fails to detect the enum already exists. This results
in the error `"enum_payload_jobs_workflow_slug" already exists`. Drizzle
tries to make the enum as: `enum_payload_jobs_workflow_slug as enum();`
and the check for existing enums only works when it has values.
2024-11-06 15:50:17 -05:00
Javier
0165ab8930 fix: replace console.error with logger.errors (#9044)
## Problem
When `PayloadRequest` objects are logged using `console.log`, it creates
unstructured, multiline entries in logging services like DataDog and
Sentry. This circumvents the structured logging approach used throughout
the rest of the codebase.

## Solution
Replace `console.x` calls with the structured logging system when
logging `payload.logger.x` objects. This ensures consistent log
formatting and better integration with monitoring tools.

## Changes
- Replaced instances of `console.log` with structured logging methods
only in `@payloadcms/next`
- Maintains logging consistency across the codebase
- Improves log readability in DataDog, Sentry, and other monitoring
services

## First

<img width="914" alt="Screenshot 2024-11-06 at 09 53 44"
src="https://github.com/user-attachments/assets/019b6f4b-40ed-4e54-a92a-8d1b50baa303">

## Then

<img width="933" alt="Screenshot 2024-11-06 at 00 50 29"
src="https://github.com/user-attachments/assets/0a339db4-d706-4ff9-ba8c-80445bbef5d0">
2024-11-06 15:49:27 -05:00
Sasha
213b7c6fb6 feat: generate types for joins (#9054)
### What?
Generates types for `joins` property.
Example from our `joins` test, keys are type-safe:
<img width="708" alt="image"
src="https://github.com/user-attachments/assets/f1fbbb9d-7c39-49a2-8aa2-a4793ae4ad7e">

Output in `payload-types.ts`:
```ts
 collectionsJoins: {
    categories: {
      relatedPosts: 'posts';
      hasManyPosts: 'posts';
      hasManyPostsLocalized: 'posts';
      'group.relatedPosts': 'posts';
      'group.camelCasePosts': 'posts';
      filtered: 'posts';
      singulars: 'singular';
    };
  };
```
Additionally, we include type information about on which collection the
join is, it will help when we have types generation for `where` and
`sort`.

### Why?
It provides a better DX as you don't need to memoize your keys.

### How?
Modifies `configToJSONSchema` to generate the json schema for
`collectionsJoins`, uses that type within `JoinQuery`
2024-11-06 22:43:07 +02:00
Jessica Chowdhury
7dc52567f1 fix: publish locale with autosave enabled and close dropdown (#8719)
1. Fix publish specific locale option when no published versions exist
2. Close the publish locale dropdown on click
2024-11-06 14:36:28 -05:00
Sasha
a22c0e62fa feat: add populate property to Local / REST API (#8969)
### What?
Adds `populate` property to Local API and REST API operations that can
be used to specify `select` for a specific collection when it's
populated
```ts
const result = await payload.findByID({
  populate: {
   // type safe if you have generated types
    posts: {
      text: true,
    },
  },
  collection: 'pages',
  depth: 1,
  id: aboutPage.id,
})

result.relatedPost // only has text and id properties
``` 

```ts
fetch('https://localhost:3000/api/pages?populate[posts][text]=true') // highlight-line
  .then((res) => res.json())
  .then((data) => console.log(data))
```

It also overrides
[`defaultPopulate`](https://github.com/payloadcms/payload/pull/8934)

Ensures `defaultPopulate` doesn't affect GraphQL.

### How?
Implements the property for all operations that have the `depth`
argument.
2024-11-06 13:50:19 -05:00
Sasha
147d28e62c fix(db-postgres): handle special characters in createDatabase (#9022)
### What?
Handles database name with special characters. For example: `-` -
`my-awesome-app`.

### Why?
Previously, `my-awesome-app` led to this error:
```
Error: failed to create database my-awesome-app.
Details: syntax error at or near "-"
```
This can reproduced for example with `create-payload-app`, as the
generated db name is based on project's name.

### How?
Wraps the query variable to quotes, `create database "my-awesome-app"`
instead of `create database my-awesome-app`.
2024-11-06 13:29:57 -05:00
Sasha
f507305192 fix: handle bulk upload sequentially to prevent conflicts (#9052)
### What?
Uses sequential pattern for Bulk Upload instead of `Promise.all`.

### Why?
* Concurrent uploads led to filename conflicts for example when you have
`upload.png` and `upload(1).png` already and you try to upload
`upload.png`
* Potentially expensive for resources, especially with high amount of
files / sizes

### How?
Replaces `Promise.all` with `for` loop, adds indicator "Uploaded 2/20"
to the loading overlay.

---------

Co-authored-by: James <james@trbl.design>
2024-11-06 12:54:16 -05:00
Timothy Choi
d42529055a fix(richtext-slate, ui): use PointerEvents to show tooltips on enabled / disabled buttons (#9006)
Fixes #9005

Note: I did not replace all instances of `onMouseEnter`, just the ones
that can be disabled and have `tooltip` set.
2024-11-06 12:43:06 -05:00
Jacob Fletcher
4b4ecb386d fix(ui): dedupes custom id fields (#9050)
Setting a custom `id` field within unnamed fields causes duplicative ID
fields to be appear in the client config. When a top-level `id` field is
detected in your config, Payload uses that instead of injecting its
default field. But when nested within unnamed fields, such as an unnamed
tab, these custom `id` fields were not being found, causing the default
field to be duplicately rendered into tables columns, etc.
2024-11-06 12:19:19 -05:00
Andreas Bernhard
becf56d582 fix(ui): edit many modal draft action button order and style (#9047)
PR adjusts "draft" action button order and style on the edit-many-modal
to be consistent with default collection edit view action buttons.

This is the `v3` PR version of
https://github.com/payloadcms/payload/pull/9046 and fixes
https://github.com/payloadcms/payload/issues/9045
2024-11-06 11:19:44 -05:00
Elliot DeNolf
8a5f6f044d chore(release): v3.0.0-beta.125 [skip ci] 2024-11-06 10:24:31 -05:00
Dan Ribbens
93a55d1075 feat: add join field config where property (#8973)
### What?

Makes it possible to filter join documents using a `where` added
directly in the config.


### Why?

It makes the join field more powerful for adding contextual meaning to
the documents being returned. For example, maybe you have a
`requiresAction` field that you set and you can have a join that
automatically filters the documents to those that need attention.

### How?

In the database adapter, we merge the requested `where` to the `where`
defined on the field.
On the frontend the results are filtered using the `filterOptions`
property in the component.

Fixes
https://github.com/payloadcms/payload/discussions/8936
https://github.com/payloadcms/payload/discussions/8937

---------

Co-authored-by: Sasha <64744993+r1tsuu@users.noreply.github.com>
2024-11-06 10:06:25 -05:00
Elliot DeNolf
cdcefa88f2 fix(cpa): remove lock file on project creation 2024-11-06 10:02:29 -05:00
Elliot DeNolf
5c049f7c9c ci(triage): adjust tag condition 2024-11-05 23:03:27 -05:00
Elliot DeNolf
ae6fb4dd1b ci(triage): add granularity in actions to be performed, enable comments 2024-11-05 22:58:33 -05:00
Sasha
9ce2ba6a3f fix: custom endpoints with method: 'put' (#9037)
### What?
Fixes support for custom endpoints with `method: 'put'`.
Previously, this didn't work:
```ts
export default buildConfigWithDefaults({
  collections: [ ],
  endpoints: [
    {
      method: 'put',
      handler: () => new Response(),
      path: '/put',
    },
  ],
})
```

### Why?
We supported this in 2.0 and docs are saying that we can use `'put'` as
`method`
https://payloadcms.com/docs/beta/rest-api/overview#custom-endpoints

### How?
Implements the `REST_PUT` export for `@payloadcms/next/routes`, updates
all templates. Additionally, adds tests to ensure root/collection level
custom endpoints with all necessary methods execute properly.

Fixes https://github.com/payloadcms/payload/issues/8807

-->
2024-11-05 23:14:34 +02:00
Sasha
f52b7c45c0 fix: type augmentation of RequestContext (#9035)
### What?

Makes this to actually work
```ts
import type { RequestContext as OriginalRequestContext } from 'payload'

declare module 'payload' {
  // Create a new interface that merges your additional fields with the original one
  export interface RequestContext extends OriginalRequestContext {
    myObject?: string
    // ...
  }
}
```
<img width="502" alt="image"
src="https://github.com/user-attachments/assets/38570d3c-e8a8-48aa-a57d-6d11e79394f5">


### Why?
This is described in our docs
https://payloadcms.com/docs/beta/hooks/context#typescript therefore it
should work.

### How?
In order to get the declaration work, we need to reuse the type from the
root file `payload/src/index.js`. Additionally, removes `RequestContext`
type duplication in both `payload/src/types/index.js` and
`payload/src/index.js`.

Fixes https://github.com/payloadcms/payload/issues/8851
2024-11-05 23:14:04 +02:00
Elliot DeNolf
2eeed4a8ae chore(templates): add lock file to with-payload-cloud 2024-11-05 15:55:57 -05:00
Elliot DeNolf
c0335aa49e chore(templates): add lock files 2024-11-05 15:27:27 -05:00
Paul
3ca203e08c fix(ui): json fields can now take a maxHeight in admin props and there's a mininum height of 3 lines (#9018)
JSON fields are now 3 lines minimum in height like so:

![image](https://github.com/user-attachments/assets/0b2ad47e-6929-4836-ac9d-022ffcdc6f27)


This helps fix an issue where long content is wrapped:

![image](https://github.com/user-attachments/assets/40fc2426-11d7-4ca5-a716-3347bb0d5a4b)

Previously it would show like this:

![image](https://github.com/user-attachments/assets/7f321220-ffa2-40ff-bc4b-2b26d21d4911)
2024-11-05 13:43:51 -06:00
James Mikrut
50f3ca93ee docs: improves jobs queue (#9038)
improves docs for jobs queue
2024-11-05 19:25:14 +00:00
Friggo
4652e8d56e feat(plugin-seo): add czech translation (#8998)
Adds Czech translation to SEO plugin.
2024-11-05 18:28:12 +00:00
Patrik
2175e5cdfb fix(ui): ensure upload field updates reflect in edit popup changes (#9034)
### What?

Any changes inside edit popup for the field with type `upload` and the
`relationTo` collection does nothing in context of the field, it has
affect only to collection.

I.e. when you make an edit to an uploads field in the edit drawer -
after saving and existing the drawer, your new changes are not present
until a refresh of the page.

### Why?

Previously, we were not performing a reload of the document fetch upon
saving of the doc in the edit drawer.

### How?

Now, we perform a reload (fetch) for updated docs on save within the
edit drawer.

Fixes #8837
2024-11-05 13:03:12 -05:00
Paul
201d68663e feat(templates): website template now has configured image sizes, updated readme and simplified env vars for setting up (#9036) 2024-11-05 17:59:29 +00:00
Patrik
ebd3c025b7 fix(ui): updatedAt field in locked-docs collection able to be updated by non-owner (#9026)
### What?

If you have a custom field that sets the value of the field using the
`useField` hook on entry into a document - the `updatedAt` field would
be updated even when a non-owner tries to enter a locked document.

### Why?

When a field is updated in the edit view - we perform an update in
`form-state` to keep the doc in `payload-locked-documents` up to date
with the current editing status. The above scenario would hit this
update operation even on non-owner users because it was previously only
checking for `updateLastEdited` (which would get hit by the `setValue`
in the `useField` hook) so we also need to check to make sure the
current user entering a locked doc is also the owner of the document.

### How?

When performing an update to `payload-locked-documents` in
`buildFormState` - only perform the update if the current user is also
the owner of the locked document otherwise skip the `update` operation.

Fixes #8781
2024-11-05 12:39:48 -05:00
Paul
ddc9d9731a feat: adds x-powered-by Payload header in next config (#9027)
Adds the `x-powered-by` header to include Payload alongside Next.js

End result looks like this
```
x-powered-by:
Next.js, Payload
```

It also respects the nextConfig `poweredBy: false` to completely disable
it
2024-11-04 18:11:51 -06:00
Jesper We
3e31b7aec9 feat(plugin-seo): add Swedish translations (#9007)
### What?

Swedish text translations

### Why?

There was no Swedish before
2024-11-04 21:25:08 +00:00
3384 changed files with 164961 additions and 226768 deletions

View File

@@ -28,3 +28,6 @@ fb7d1be2f3325d076b7c967b1730afcef37922c2
# Prettier and lint remaining db packages
7fd736ea5b2e9fc4ef936e9dc9e5e3d722f6d8bf
# Bump all eslint deps, lint and format
03291472d6e427ff94e61fca0616cca7796a3a95

View File

@@ -1,6 +1,6 @@
name: Functionality Bug
description: '[REPRODUCTION REQUIRED] - Create a bug report'
labels: ['status: needs-triage', 'v3', 'validate-reproduction']
labels: ['status: needs-triage', 'validate-reproduction']
body:
- type: textarea
attributes:
@@ -14,7 +14,7 @@ body:
label: Link to the code that reproduces this issue
description: >-
_REQUIRED_: Please provide a link to your reproduction. Note, if the URL is invalid (404 or a private repository), we may close the issue.
Either use `npx create-payload-app@beta -t blank` then push to a repo or follow the [reproduction-guide](https://github.com/payloadcms/payload/blob/main/.github/reproduction-guide.md) for more information.
Either use `pnpx create-payload-app@latest -t blank` then push to a repo or follow the [reproduction-guide](https://github.com/payloadcms/payload/blob/main/.github/reproduction-guide.md) for more information.
validations:
required: true
@@ -39,6 +39,7 @@ body:
- 'db-postgres'
- 'db-sqlite'
- 'db-vercel-postgres'
- 'email-nodemailer'
- 'plugin: cloud'
- 'plugin: cloud-storage'
- 'plugin: form-builder'
@@ -56,7 +57,7 @@ body:
- type: textarea
attributes:
label: Environment Info
description: Paste output from `pnpm payload info` (>= beta.92) _or_ Payload, Node.js, and Next.js versions.
description: Paste output from `pnpm payload info` _or_ Payload, Node.js, and Next.js versions.
render: text
placeholder: |
Payload:

View File

@@ -20,7 +20,7 @@ body:
- type: textarea
attributes:
label: Environment Info
description: Paste output from `pnpm payload info` (>= beta.92) _or_ Payload, Node.js, and Next.js versions.
description: Paste output from `pnpm payload info` _or_ Payload, Node.js, and Next.js versions.
render: text
placeholder: |
Payload:

View File

@@ -18,7 +18,7 @@
},
"devDependencies": {
"@octokit/webhooks-types": "^7.5.1",
"@swc/jest": "^0.2.36",
"@swc/jest": "^0.2.37",
"@types/jest": "^27.5.2",
"@types/node": "^20.16.5",
"@typescript-eslint/eslint-plugin": "^4.33.0",

View File

@@ -1,6 +1,6 @@
{
"compilerOptions": {
"target": "es5",
"target": "ES2022",
"lib": ["es2020.string"],
"noEmit": true,
"strict": true,

View File

@@ -1,15 +1,33 @@
name: Setup node and pnpm
description: Configure the Node.js and pnpm versions
description: |
Configures Node, pnpm, cache, performs pnpm install
inputs:
node-version:
description: 'The Node.js version to use'
description: Node.js version
required: true
default: 22.6.2
default: 22.6.0
pnpm-version:
description: 'The pnpm version to use'
description: Pnpm version
required: true
default: 9.7.1
pnpm-run-install:
description: Whether to run pnpm install
required: false
default: true
pnpm-restore-cache:
description: Whether to restore cache
required: false
default: true
pnpm-install-cache-key:
description: The cache key for the pnpm install cache
default: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
outputs:
pnpm-store-path:
description: The resolved pnpm store path
pnpm-install-cache-key:
description: The cache key used for pnpm install cache
runs:
using: composite
@@ -30,19 +48,29 @@ runs:
version: ${{ inputs.pnpm-version }}
run_install: false
- name: Get pnpm store directory
- name: Get pnpm store path
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
STORE_PATH=$(pnpm store path --silent)
echo "STORE_PATH=$STORE_PATH" >> $GITHUB_ENV
echo "Pnpm store path resolved to: $STORE_PATH"
- name: Setup pnpm cache
- name: Restore pnpm install cache
if: ${{ inputs.pnpm-restore-cache == 'true' }}
uses: actions/cache@v4
with:
path: ${{ env.STORE_PATH }}
key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
key: ${{ inputs.pnpm-install-cache-key }}
restore-keys: |
pnpm-store-${{ inputs.pnpm-version }}-
pnpm-store-
pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- shell: bash
- name: Run pnpm install
if: ${{ inputs.pnpm-run-install == 'true' }}
shell: bash
run: pnpm install
# Set the cache key output
- run: |
echo "pnpm-install-cache-key=${{ inputs.pnpm-install-cache-key }}" >> $GITHUB_ENV
shell: bash

View File

@@ -17,9 +17,9 @@ inputs:
reproduction-link-section:
description: 'A regular expression string with "(.*)" matching a valid URL in the issue body. The result is trimmed. Example: "### Link to reproduction(.*)### To reproduce"'
default: '### Link to reproduction(.*)### To reproduce'
tag-only:
description: Log and tag only. Do not perform closing or commenting actions.
default: false
actions-to-perform:
description: 'Comma-separated list of actions to perform on the issue. Example: "tag,comment,close"'
default: 'tag,comment,close'
runs:
using: 'composite'
@@ -37,4 +37,4 @@ runs:
'INPUT_REPRODUCTION_INVALID_LABEL': ${{inputs.reproduction-invalid-label}}
'INPUT_REPRODUCTION_ISSUE_LABELS': ${{inputs.reproduction-issue-labels}}
'INPUT_REPRODUCTION_LINK_SECTION': ${{inputs.reproduction-link-section}}
'INPUT_TAG_ONLY': ${{inputs.tag-only}}
'INPUT_ACTIONS_TO_PERFORM': ${{inputs.actions-to-perform}}

View File

@@ -33843,6 +33843,7 @@ if (!process.env.GITHUB_TOKEN)
throw new TypeError('No GITHUB_TOKEN provided');
if (!process.env.GITHUB_WORKSPACE)
throw new TypeError('Not a GitHub workspace');
var validActionsToPerform = ['tag', 'comment', 'close'];
var config = {
invalidLink: {
comment: (0,core.getInput)('reproduction_comment') || '.github/invalid-reproduction.md',
@@ -33853,7 +33854,15 @@ var config = {
label: (0,core.getInput)('reproduction_invalid_label') || 'invalid-reproduction',
linkSection: (0,core.getInput)('reproduction_link_section') || '### Link to reproduction(.*)### To reproduce',
},
tagOnly: getBooleanOrUndefined('tag_only') || false,
actionsToPerform: ((0,core.getInput)('actions_to_perform') || validActionsToPerform.join(','))
.split(',')
.map(function (a) {
var action = a.trim().toLowerCase();
if (validActionsToPerform.includes(action)) {
return action;
}
throw new TypeError("Invalid action: ".concat(action));
}),
token: process.env.GITHUB_TOKEN,
workspace: process.env.GITHUB_WORKSPACE,
};
@@ -33870,7 +33879,7 @@ function tryParse(json) {
// Retrieves a boolean input or undefined based on environment variables
function getBooleanOrUndefined(value) {
var variable = process.env["INPUT_".concat(value.toUpperCase())];
return variable === undefined || variable === '' ? undefined : (0,core.getBooleanInput)(value);
return variable === undefined || variable === '' ? undefined : getBooleanInput(value);
}
// Returns the appropriate label match type
function getLabelMatch(value) {
@@ -33910,36 +33919,47 @@ function checkValidReproduction() {
case 3:
(0,core.info)("Invalid reproduction, issue will be closed/labeled/commented...");
// Adjust labels
return [4 /*yield*/, Promise.all(labelsToRemove.map(function (label) { return client.issues.removeLabel(__assign(__assign({}, common), { name: label })); }))];
return [4 /*yield*/, Promise.all(labelsToRemove.map(function (label) { return client.issues.removeLabel(__assign(__assign({}, common), { name: label })); }))
// Tag
];
case 4:
// Adjust labels
_f.sent();
(0,core.info)("Issue #".concat(issue.number, " - validate label removed"));
if (!!config.actionsToPerform.includes('tag')) return [3 /*break*/, 6];
(0,core.info)("Added label: ".concat(config.invalidLink.label));
return [4 /*yield*/, client.issues.addLabels(__assign(__assign({}, common), { labels: [config.invalidLink.label] }))];
case 5:
_f.sent();
(0,core.info)("Issue #".concat(issue.number, " - labeled"));
// If tagOnly, do not close or comment
if (config.tagOnly) {
(0,core.info)('Tag-only enabled, no closing/commenting actions taken');
return [2 /*return*/];
}
// Perform closing and commenting actions
return [4 /*yield*/, client.issues.update(__assign(__assign({}, common), { state: 'closed' }))];
return [3 /*break*/, 7];
case 6:
// Perform closing and commenting actions
_f.sent();
(0,core.info)("Issue #".concat(issue.number, " - closed"));
(0,core.info)('Tag - skipped, not provided in actions to perform');
_f.label = 7;
case 7:
if (!config.actionsToPerform.includes('comment')) return [3 /*break*/, 10];
comment = (0,external_node_path_namespaceObject.join)(config.workspace, config.invalidLink.comment);
_c = (_b = client.issues).createComment;
_d = [__assign({}, common)];
_e = {};
return [4 /*yield*/, getCommentBody(comment)];
case 7: return [4 /*yield*/, _c.apply(_b, [__assign.apply(void 0, _d.concat([(_e.body = _f.sent(), _e)]))])];
case 8:
case 8: return [4 /*yield*/, _c.apply(_b, [__assign.apply(void 0, _d.concat([(_e.body = _f.sent(), _e)]))])];
case 9:
_f.sent();
(0,core.info)("Issue #".concat(issue.number, " - commented"));
return [2 /*return*/];
(0,core.info)("Commented with invalid reproduction message");
return [3 /*break*/, 11];
case 10:
(0,core.info)('Comment - skipped, not provided in actions to perform');
_f.label = 11;
case 11:
if (!config.actionsToPerform.includes('close')) return [3 /*break*/, 13];
return [4 /*yield*/, client.issues.update(__assign(__assign({}, common), { state: 'closed' }))];
case 12:
_f.sent();
(0,core.info)("Closed issue #".concat(issue.number));
return [3 /*break*/, 14];
case 13:
(0,core.info)('Close - skipped, not provided in actions to perform');
_f.label = 14;
case 14: return [2 /*return*/];
}
});
});

View File

@@ -8,6 +8,9 @@ import { join } from 'node:path'
if (!process.env.GITHUB_TOKEN) throw new TypeError('No GITHUB_TOKEN provided')
if (!process.env.GITHUB_WORKSPACE) throw new TypeError('Not a GitHub workspace')
const validActionsToPerform = ['tag', 'comment', 'close'] as const
type ActionsToPerform = (typeof validActionsToPerform)[number]
// Define the configuration object
interface Config {
invalidLink: {
@@ -17,7 +20,7 @@ interface Config {
label: string
linkSection: string
}
tagOnly: boolean
actionsToPerform: ActionsToPerform[]
token: string
workspace: string
}
@@ -33,7 +36,16 @@ const config: Config = {
linkSection:
getInput('reproduction_link_section') || '### Link to reproduction(.*)### To reproduce',
},
tagOnly: getBooleanOrUndefined('tag_only') || false,
actionsToPerform: (getInput('actions_to_perform') || validActionsToPerform.join(','))
.split(',')
.map((a) => {
const action = a.trim().toLowerCase() as ActionsToPerform
if (validActionsToPerform.includes(action)) {
return action
}
throw new TypeError(`Invalid action: ${action}`)
}),
token: process.env.GITHUB_TOKEN,
workspace: process.env.GITHUB_WORKSPACE,
}
@@ -104,23 +116,31 @@ async function checkValidReproduction(): Promise<void> {
await Promise.all(
labelsToRemove.map((label) => client.issues.removeLabel({ ...common, name: label })),
)
info(`Issue #${issue.number} - validate label removed`)
await client.issues.addLabels({ ...common, labels: [config.invalidLink.label] })
info(`Issue #${issue.number} - labeled`)
// If tagOnly, do not close or comment
if (config.tagOnly) {
info('Tag-only enabled, no closing/commenting actions taken')
return
// Tag
if (config.actionsToPerform.includes('tag')) {
info(`Added label: ${config.invalidLink.label}`)
await client.issues.addLabels({ ...common, labels: [config.invalidLink.label] })
} else {
info('Tag - skipped, not provided in actions to perform')
}
// Perform closing and commenting actions
await client.issues.update({ ...common, state: 'closed' })
info(`Issue #${issue.number} - closed`)
// Comment
if (config.actionsToPerform.includes('comment')) {
const comment = join(config.workspace, config.invalidLink.comment)
await client.issues.createComment({ ...common, body: await getCommentBody(comment) })
info(`Commented with invalid reproduction message`)
} else {
info('Comment - skipped, not provided in actions to perform')
}
const comment = join(config.workspace, config.invalidLink.comment)
await client.issues.createComment({ ...common, body: await getCommentBody(comment) })
info(`Issue #${issue.number} - commented`)
// Close
if (config.actionsToPerform.includes('close')) {
await client.issues.update({ ...common, state: 'closed' })
info(`Closed issue #${issue.number}`)
} else {
info('Close - skipped, not provided in actions to perform')
}
}
/**

View File

@@ -1,6 +1,6 @@
{
"compilerOptions": {
"target": "es5",
"target": "ES2022",
"lib": ["es2020.string"],
"noEmit": true,
"strict": true,

View File

@@ -1,4 +1,6 @@
We cannot recreate the issue with the provided information. **Please add a reproduction in order for us to be able to investigate.**
**Please add a reproduction in order for us to be able to investigate.**
Depending on the quality of reproduction steps, this issue may be closed if no reproduction is provided.
### Why was this issue marked with the `invalid-reproduction` label?

View File

@@ -8,7 +8,7 @@ updates:
- /.github/workflows
- /.github/actions/* # Not working until resolved: https://github.com/dependabot/dependabot-core/issues/6345
- /.github/actions/setup
target-branch: beta
target-branch: main
schedule:
interval: monthly
timezone: America/Detroit
@@ -20,7 +20,8 @@ updates:
- package-ecosystem: npm
directory: /
target-branch: beta
target-branch: main
open-pull-requests-limit: 0 # Only allow security updates
schedule:
interval: weekly
day: sunday
@@ -38,8 +39,6 @@ updates:
- patch
patterns:
- '*'
exclude-patterns:
- 'drizzle*'
dev-deps:
dependency-type: development
update-types:
@@ -47,13 +46,11 @@ updates:
- patch
patterns:
- '*'
exclude-patterns:
- 'drizzle*'
# Only bump patch versions for 2.x
- package-ecosystem: npm
directory: /
target-branch: main
target-branch: 2.x
open-pull-requests-limit: 0 # Only allow security updates
schedule:
interval: weekly
day: sunday
@@ -70,5 +67,3 @@ updates:
- patch
patterns:
- '*'
exclude-patterns:
- 'drizzle*'

View File

@@ -40,7 +40,7 @@ There are a couple ways run integration tests:
- **Granularly** - you can run individual tests in vscode by installing the Jest Runner plugin and using that to run individual tests. Clicking the `debug` button will run the test in debug mode allowing you to set break points.
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/payload/src/admin/assets/images/github/int-debug.png" />
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/payload/src/assets/images/github/int-debug.png" />
- **Manually** - you can run all int tests in the `/test/_community/int.spec.ts` file by running the following command:
@@ -57,7 +57,7 @@ The easiest way to run E2E tests is to install
Once they are installed you can open the `testing` tab in vscode sidebar and drill down to the test you want to run, i.e. `/test/_community/e2e.spec.ts`
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/payload/src/admin/assets/images/github/e2e-debug.png" />
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/payload/src/assets/images/github/e2e-debug.png" />
#### Notes

View File

@@ -13,7 +13,7 @@ on:
jobs:
on-labeled-ensure-one-status:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
permissions:
issues: write
# Only run on issue labeled and if label starts with 'status:'
@@ -49,7 +49,7 @@ jobs:
}
on-issue-close:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
permissions:
issues: write
if: github.event.action == 'closed'
@@ -82,7 +82,7 @@ jobs:
}
on-issue-reopen:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
permissions:
issues: write
if: github.event.action == 'reopened'
@@ -93,7 +93,7 @@ jobs:
labels: 'status: needs-triage'
on-issue-assigned:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
permissions:
issues: write
if: >
@@ -106,11 +106,11 @@ jobs:
labels: 'status: needs-triage'
# on-pr-merge:
# runs-on: ubuntu-latest
# runs-on: ubuntu-24.04
# if: github.event.pull_request.merged == true
# steps:
# on-pr-close:
# runs-on: ubuntu-latest
# runs-on: ubuntu-24.04
# if: github.event_name == 'pull_request_target' && github.event.pull_request.merged == false
# steps:

View File

@@ -11,7 +11,7 @@ permissions:
jobs:
lock_issues:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- name: Lock issues
uses: dessant/lock-threads@v5

View File

@@ -9,7 +9,6 @@ on:
push:
branches:
- main
- beta
concurrency:
# <workflow_name>-<branch_name>-<true || commit_sha if branch is protected>
@@ -24,11 +23,12 @@ env:
jobs:
changes:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
permissions:
pull-requests: read
outputs:
needs_build: ${{ steps.filter.outputs.needs_build }}
needs_tests: ${{ steps.filter.outputs.needs_tests }}
templates: ${{ steps.filter.outputs.templates }}
steps:
# https://github.com/actions/virtual-environments/issues/1187
@@ -36,8 +36,6 @@ jobs:
run: sudo ethtool -K eth0 tx off rx off
- uses: actions/checkout@v4
with:
fetch-depth: 25
- uses: dorny/paths-filter@v3
id: filter
with:
@@ -48,53 +46,37 @@ jobs:
- 'test/**'
- 'pnpm-lock.yaml'
- 'package.json'
- 'templates/**'
needs_tests:
- '.github/workflows/**'
- 'packages/**'
- 'test/**'
- 'pnpm-lock.yaml'
- 'package.json'
templates:
- 'templates/**'
- name: Log all filter results
run: |
echo "needs_build: ${{ steps.filter.outputs.needs_build }}"
echo "needs_tests: ${{ steps.filter.outputs.needs_tests }}"
echo "templates: ${{ steps.filter.outputs.templates }}"
lint:
if: >
github.event_name == 'pull_request' && !contains(github.event.pull_request.title, 'no-lint') && !contains(github.event.pull_request.title, 'skip-lint')
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- name: Setup Node@${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
- name: Node setup
uses: ./.github/actions/setup
with:
node-version: ${{ env.NODE_VERSION }}
pnpm-version: ${{ env.PNPM_VERSION }}
pnpm-install-cache-key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
run_install: false
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: Setup pnpm cache
uses: actions/cache@v4
timeout-minutes: 720
with:
path: ${{ env.STORE_PATH }}
key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
pnpm-store-
pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- run: pnpm install
- name: Lint staged
run: |
git diff --name-only --diff-filter=d origin/${GITHUB_BASE_REF}...${GITHUB_SHA}
@@ -103,78 +85,46 @@ jobs:
build:
needs: changes
if: ${{ needs.changes.outputs.needs_build == 'true' }}
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 25
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- name: Setup Node@${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
- name: Node setup
uses: ./.github/actions/setup
with:
node-version: ${{ env.NODE_VERSION }}
pnpm-version: ${{ env.PNPM_VERSION }}
pnpm-install-cache-key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
run_install: false
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: Setup pnpm cache
uses: actions/cache@v4
timeout-minutes: 720
with:
path: ${{ env.STORE_PATH }}
key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
pnpm-store-
pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- run: pnpm install
- run: pnpm run build:all
env:
DO_NOT_TRACK: 1 # Disable Turbopack telemetry
- name: Cache build
uses: actions/cache@v4
timeout-minutes: 10
with:
path: ./*
key: ${{ github.sha }}-${{ github.run_number }}
tests-unit:
runs-on: ubuntu-latest
needs: build
runs-on: ubuntu-24.04
needs: [changes, build]
if: ${{ needs.changes.outputs.needs_tests == 'true' }}
steps:
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- uses: actions/checkout@v4
- name: Setup Node@${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
- name: Node setup
uses: ./.github/actions/setup
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
run_install: false
pnpm-version: ${{ env.PNPM_VERSION }}
pnpm-run-install: false
pnpm-restore-cache: false # Full build is restored below
pnpm-install-cache-key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Restore build
uses: actions/cache@v4
timeout-minutes: 10
with:
path: ./*
key: ${{ github.sha }}-${{ github.run_number }}
@@ -184,9 +134,38 @@ jobs:
env:
NODE_OPTIONS: --max-old-space-size=8096
tests-types:
runs-on: ubuntu-24.04
needs: [changes, build]
if: ${{ needs.changes.outputs.needs_tests == 'true' }}
steps:
- uses: actions/checkout@v4
- name: Node setup
uses: ./.github/actions/setup
with:
node-version: ${{ env.NODE_VERSION }}
pnpm-version: ${{ env.PNPM_VERSION }}
pnpm-run-install: false
pnpm-restore-cache: false # Full build is restored below
pnpm-install-cache-key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Restore build
uses: actions/cache@v4
with:
path: ./*
key: ${{ github.sha }}-${{ github.run_number }}
- name: Types Tests
run: pnpm test:types --target '>=5.7'
env:
NODE_OPTIONS: --max-old-space-size=8096
tests-int:
runs-on: ubuntu-latest
needs: build
runs-on: ubuntu-24.04
needs: [changes, build]
if: ${{ needs.changes.outputs.needs_tests == 'true' }}
name: int-${{ matrix.database }}
strategy:
fail-fast: false
matrix:
@@ -206,39 +185,40 @@ jobs:
AWS_SECRET_ACCESS_KEY: localstack
AWS_REGION: us-east-1
services:
postgres:
image: ${{ (startsWith(matrix.database, 'postgres') ) && 'postgis/postgis:16-3.4' || '' }}
env:
# must specify password for PG Docker container image, see: https://registry.hub.docker.com/_/postgres?tab=description&page=1&name=10
POSTGRES_USER: ${{ env.POSTGRES_USER }}
POSTGRES_PASSWORD: ${{ env.POSTGRES_PASSWORD }}
POSTGRES_DB: ${{ env.POSTGRES_DB }}
ports:
- 5432:5432
# needed because the postgres container does not provide a healthcheck
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 25
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- name: Setup Node@${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
- name: Node setup
uses: ./.github/actions/setup
with:
node-version: ${{ env.NODE_VERSION }}
pnpm-version: ${{ env.PNPM_VERSION }}
pnpm-run-install: false
pnpm-restore-cache: false # Full build is restored below
pnpm-install-cache-key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Restore build
uses: actions/cache@v4
with:
version: ${{ env.PNPM_VERSION }}
run_install: false
- run: pnpm install
path: ./*
key: ${{ github.sha }}-${{ github.run_number }}
- name: Start LocalStack
run: pnpm docker:start
- name: Start PostgreSQL
uses: CasperWA/postgresql-action@v1.2
with:
postgresql version: '14' # See https://hub.docker.com/_/postgres for available versions
postgresql db: ${{ env.POSTGRES_DB }}
postgresql user: ${{ env.POSTGRES_USER }}
postgresql password: ${{ env.POSTGRES_PASSWORD }}
if: startsWith(matrix.database, 'postgres')
- name: Install Supabase CLI
uses: supabase/setup-cli@v1
with:
@@ -251,10 +231,6 @@ jobs:
supabase start
if: matrix.database == 'supabase'
- name: Wait for PostgreSQL
run: sleep 30
if: startsWith(matrix.database, 'postgres')
- name: Configure PostgreSQL
run: |
psql "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" -c "CREATE ROLE runner SUPERUSER LOGIN;"
@@ -273,15 +249,23 @@ jobs:
if: matrix.database == 'supabase'
- name: Integration Tests
run: pnpm test:int
uses: nick-fields/retry@v3
env:
NODE_OPTIONS: --max-old-space-size=8096
PAYLOAD_DATABASE: ${{ matrix.database }}
POSTGRES_URL: ${{ env.POSTGRES_URL }}
with:
retry_on: any
max_attempts: 5
timeout_minutes: 15
command: pnpm test:int
on_retry_command: pnpm clean:build && pnpm install --no-frozen-lockfile
tests-e2e:
runs-on: ubuntu-latest
needs: build
runs-on: ubuntu-24.04
needs: [changes, build]
if: ${{ needs.changes.outputs.needs_tests == 'true' }}
name: e2e-${{ matrix.suite }}
strategy:
fail-fast: false
matrix:
@@ -291,8 +275,10 @@ jobs:
- access-control
- admin__e2e__1
- admin__e2e__2
- admin__e2e__3
- admin-root
- auth
- auth-basic
- field-error-states
- fields-relationship
- fields
@@ -321,24 +307,19 @@ jobs:
env:
SUITE_NAME: ${{ matrix.suite }}
steps:
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- uses: actions/checkout@v4
- name: Setup Node@${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
- name: Node setup
uses: ./.github/actions/setup
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
run_install: false
pnpm-version: ${{ env.PNPM_VERSION }}
pnpm-run-install: false
pnpm-restore-cache: false # Full build is restored below
pnpm-install-cache-key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Restore build
uses: actions/cache@v4
timeout-minutes: 10
with:
path: ./*
key: ${{ github.sha }}-${{ github.run_number }}
@@ -370,7 +351,13 @@ jobs:
run: pnpm exec playwright install-deps chromium
- name: E2E Tests
run: PLAYWRIGHT_JSON_OUTPUT_NAME=results_${{ matrix.suite }}.json pnpm test:e2e:prod:ci ${{ matrix.suite }}
uses: nick-fields/retry@v3
with:
retry_on: any
max_attempts: 5
timeout_minutes: 20
command: PLAYWRIGHT_JSON_OUTPUT_NAME=results_${{ matrix.suite }}.json pnpm test:e2e:prod:ci ${{ matrix.suite }}
on_retry_command: pnpm clean:build && pnpm install --no-frozen-lockfile && pnpm build:all
env:
PLAYWRIGHT_JSON_OUTPUT_NAME: results_${{ matrix.suite }}.json
NEXT_TELEMETRY_DISABLED: 1
@@ -390,74 +377,104 @@ jobs:
# report-tag: ${{ matrix.suite }}
# job-summary: true
app-build-with-packed:
if: false # Disable until package resolution in tgzs can be figured out
runs-on: ubuntu-latest
# Build listed templates with packed local packages
build-templates:
runs-on: ubuntu-24.04
needs: build
strategy:
matrix:
include:
- template: blank
database: mongodb
- template: website
database: mongodb
- template: with-payload-cloud
database: mongodb
- template: with-vercel-mongodb
database: mongodb
# Postgres
- template: with-postgres
database: postgres
- template: with-vercel-postgres
database: postgres
# Re-enable once PG conncection is figured out
# - template: with-vercel-website
# database: postgres
name: ${{ matrix.template }}-${{ matrix.database }}
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: payloadtests
steps:
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- uses: actions/checkout@v4
- name: Setup Node@${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
- name: Node setup
uses: ./.github/actions/setup
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
run_install: false
pnpm-version: ${{ env.PNPM_VERSION }}
pnpm-run-install: false
pnpm-restore-cache: false # Full build is restored below
pnpm-install-cache-key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Restore build
uses: actions/cache@v4
timeout-minutes: 10
with:
path: ./*
key: ${{ github.sha }}-${{ github.run_number }}
- name: Start PostgreSQL
uses: CasperWA/postgresql-action@v1.2
with:
postgresql version: '14' # See https://hub.docker.com/_/postgres for available versions
postgresql db: ${{ env.POSTGRES_DB }}
postgresql user: ${{ env.POSTGRES_USER }}
postgresql password: ${{ env.POSTGRES_PASSWORD }}
if: matrix.database == 'postgres'
- name: Wait for PostgreSQL
run: sleep 30
if: matrix.database == 'postgres'
- name: Configure PostgreSQL
run: |
psql "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" -c "CREATE ROLE runner SUPERUSER LOGIN;"
psql "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" -c "SELECT version();"
echo "POSTGRES_URL=postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" >> $GITHUB_ENV
if: matrix.database == 'postgres'
- name: Start MongoDB
uses: supercharge/mongodb-github-action@1.11.0
with:
mongodb-version: 6.0
- name: Pack and build app
- name: Build Template
run: |
set -ex
pnpm run script:pack --dest templates/blank
cd templates/blank
cp .env.example .env
ls -la
pnpm add ./*.tgz --ignore-workspace
pnpm install --ignore-workspace --no-frozen-lockfile
cat package.json
pnpm run build
pnpm run script:pack --dest templates/${{ matrix.template }}
pnpm runts scripts/build-template-with-local-pkgs.ts ${{ matrix.template }} $POSTGRES_URL
tests-type-generation:
runs-on: ubuntu-latest
needs: build
runs-on: ubuntu-24.04
needs: [changes, build]
if: ${{ needs.changes.outputs.needs_tests == 'true' }}
steps:
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- uses: actions/checkout@v4
- name: Setup Node@${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
- name: Node setup
uses: ./.github/actions/setup
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
run_install: false
pnpm-version: ${{ env.PNPM_VERSION }}
pnpm-run-install: false
pnpm-restore-cache: false # Full build is restored below
pnpm-install-cache-key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Restore build
uses: actions/cache@v4
timeout-minutes: 10
with:
path: ./*
key: ${{ github.sha }}-${{ github.run_number }}
@@ -468,82 +485,19 @@ jobs:
- name: Generate GraphQL schema file
run: pnpm dev:generate-graphql-schema graphql-schema-gen
templates:
needs: changes
if: false # Disable until templates are updated for 3.0
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
template: [blank, website, ecommerce]
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 25
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- name: Setup Node@${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Start MongoDB
uses: supercharge/mongodb-github-action@1.11.0
with:
mongodb-version: 6.0
- name: Build Template
run: |
cd templates/${{ matrix.template }}
cp .env.example .env
yarn install
yarn build
yarn generate:types
generated-templates:
needs: build
if: false # Needs to pull in tgz files from build
runs-on: ubuntu-latest
steps:
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- name: Setup Node@${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
run_install: false
- name: Restore build
uses: actions/cache@v4
timeout-minutes: 10
with:
path: ./*
key: ${{ github.sha }}-${{ github.run_number }}
- name: Build all generated templates
run: pnpm tsx ./scripts/build-generated-templates.ts
all-green:
name: All Green
if: always()
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
needs:
- lint
- build
- build-templates
- tests-unit
- tests-int
- tests-e2e
- tests-types
- tests-type-generation
steps:
- if: ${{ always() && (contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')) }}
@@ -551,7 +505,8 @@ jobs:
publish-canary:
name: Publish Canary
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
if: ${{ needs.all-green.result == 'success' && github.ref_name == 'main' }}
needs:
- all-green
@@ -559,5 +514,4 @@ jobs:
# debug github.ref output
- run: |
echo github.ref: ${{ github.ref }}
echo isBeta: ${{ github.ref == 'refs/heads/beta' }}
echo isMain: ${{ github.ref == 'refs/heads/main' }}
echo isV3: ${{ github.ref == 'refs/heads/main' }}

View File

@@ -0,0 +1,105 @@
name: post-release-templates
on:
release:
types:
- published
workflow_dispatch:
env:
NODE_VERSION: 22.6.0
PNPM_VERSION: 9.7.1
DO_NOT_TRACK: 1 # Disable Turbopack telemetry
NEXT_TELEMETRY_DISABLED: 1 # Disable Next telemetry
jobs:
update_templates:
runs-on: ubuntu-24.04
permissions:
contents: write
pull-requests: write
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: payloadtests
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # Needed for tags
- name: Setup
uses: ./.github/actions/setup
with:
node-version: ${{ env.NODE_VERSION }}
pnpm-version: ${{ env.PNPM_VERSION }}
- name: Start PostgreSQL
uses: CasperWA/postgresql-action@v1.2
with:
postgresql version: '14' # See https://hub.docker.com/_/postgres for available versions
postgresql db: ${{ env.POSTGRES_DB }}
postgresql user: ${{ env.POSTGRES_USER }}
postgresql password: ${{ env.POSTGRES_PASSWORD }}
- name: Wait for PostgreSQL
run: sleep 30
- name: Configure PostgreSQL
run: |
psql "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" -c "CREATE ROLE runner SUPERUSER LOGIN;"
psql "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" -c "SELECT version();"
echo "POSTGRES_URL=postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" >> $GITHUB_ENV
echo "DATABASE_URI=postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" >> $GITHUB_ENV
- name: Start MongoDB
uses: supercharge/mongodb-github-action@1.11.0
with:
mongodb-version: 6.0
- name: Update template lockfiles and migrations
run: pnpm script:gen-templates
- name: Determine Release Tag
id: determine_tag
run: |
if [ "${{ github.event_name }}" == "release" ]; then
echo "Using tag from release event: ${{ github.event.release.tag_name }}"
echo "release_tag=${{ github.event.release.tag_name }}" >> "$GITHUB_OUTPUT"
else
# pull latest tag from github, must match any version except v2. Should match v3, v4, v99, etc.
echo "Fetching latest tag from github..."
LATEST_TAG=$(git describe --tags --abbrev=0 --match 'v[0-9]*' --exclude 'v2*')
echo "Latest tag: $LATEST_TAG"
echo "release_tag=$LATEST_TAG" >> "$GITHUB_OUTPUT"
fi
- name: Commit and push changes
id: commit
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -ex
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git diff --name-only
export BRANCH_NAME=templates/bump-${{ steps.determine_tag.outputs.release_tag }}-$(date +%s)
echo "branch=$BRANCH_NAME" >> "$GITHUB_OUTPUT"
- name: Create pull request
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.GH_TOKEN_POST_RELEASE_TEMPLATES }}
labels: 'area: templates'
author: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
commit-message: 'templates: bump templates for ${{ steps.determine_tag.outputs.release_tag }}'
branch: ${{ steps.commit.outputs.branch }}
base: main
assignees: ${{ github.actor }}
title: 'templates: bump for ${{ steps.determine_tag.outputs.release_tag }}'
body: |
🤖 Automated bump of templates for ${{ steps.determine_tag.outputs.release_tag }}
Triggered by user: @${{ github.actor }}

View File

@@ -5,16 +5,24 @@ on:
types:
- published
workflow_dispatch:
inputs:
tag:
description: 'Release tag to process (optional)'
required: false
default: ''
env:
NODE_VERSION: 22.6.0
PNPM_VERSION: 9.7.1
DO_NOT_TRACK: 1 # Disable Turbopack telemetry
NEXT_TELEMETRY_DISABLED: 1 # Disable Next telemetry
jobs:
post_release:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
if: ${{ github.event_name != 'workflow_dispatch' }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
# Only needed if debugging on a branch other than default
# ref: ${{ github.event.release.target_commitish || github.ref }}
- uses: ./.github/actions/release-commenter
continue-on-error: true
env:
@@ -28,3 +36,17 @@ jobs:
comment-template: |
🚀 This is included in version {release_link}
github-releases-to-discord:
runs-on: ubuntu-24.04
if: ${{ github.event_name != 'workflow_dispatch' }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Github Releases To Discord
uses: SethCohen/github-releases-to-discord@v1.16.2
with:
webhook_url: ${{ secrets.DISCORD_RELEASES_WEBHOOK_URL }}
color: '16777215'
username: 'Payload Releases'
avatar_url: 'https://l4wlsi8vxy8hre4v.public.blob.vercel-storage.com/discord-bot-logo.png'

View File

@@ -1,7 +1,7 @@
name: pr-title
on:
pull_request:
pull_request_target:
types:
- opened
- edited
@@ -12,7 +12,7 @@ permissions:
jobs:
main:
name: lint-pr-title
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- uses: amannn/action-semantic-pull-request@v5
id: lint_pr_title
@@ -24,14 +24,15 @@ jobs:
chore
ci
docs
examples
feat
fix
perf
refactor
revert
style
templates
test
types
scopes: |
cpa
db-\*
@@ -106,16 +107,11 @@ jobs:
label-pr-on-open:
name: label-pr-on-open
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
if: github.event.action == 'opened'
steps:
- name: Tag with main branch with v2
if: github.event.pull_request.base.ref == 'main'
- name: Tag with 2.x branch with v2
if: github.event.pull_request.base.ref == '2.x'
uses: actions-ecosystem/action-add-labels@v1
with:
labels: v2
- name: Tag with beta branch with v3
if: github.event.pull_request.base.ref == 'beta'
uses: actions-ecosystem/action-add-labels@v1
with:
labels: v3

View File

@@ -2,8 +2,6 @@ name: release-canary
on:
workflow_dispatch:
branches:
- beta
env:
NODE_VERSION: 22.6.0
@@ -13,9 +11,10 @@ env:
jobs:
release:
name: release-canary-${{ github.ref_name }}-${{ github.sha }}
permissions:
id-token: write
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v4

View File

@@ -1,11 +1,21 @@
name: stale
on:
schedule:
# Run nightly at 1am EST
- cron: '0 5 * * *'
workflow_dispatch:
inputs:
dry-run:
description: Run the stale action in debug-only mode
default: false
required: false
type: boolean
jobs:
stale:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
permissions:
issues: write
pull-requests: write
@@ -13,30 +23,35 @@ jobs:
- uses: actions/stale@v9
id: stale
with:
debug-only: true
days-before-stale: 90
debug-only: ${{ inputs.dry-run || false }}
days-before-stale: 23
days-before-close: 7
days-before-pr-close: -1 # Never close PRs
ascending: true
operations-per-run: 300
# Ignore all assigned
exempt-all-assignees: false
# Issues
stale-issue-label: 'stale'
exempt-issue-labels: 'blocked,must,should,keep,created-by: Payload team,created-by: Contributor'
stale-issue-message: >
This issue has been marked as stale due to lack of activity. To keep the ticket open, please indicate that it is still relevant in a comment below.
close-issue-message: >
stale-issue-label: stale
exempt-issue-labels: 'prioritized,keep,created-by: Payload team,created-by: Contributor,status: verified'
stale-issue-message: |
This issue has been marked as stale due to lack of activity.
To keep this issue open, please indicate that it is still relevant in a comment below.
close-issue-message: |
This issue was automatically closed due to lack of activity.
# Pull Requests
stale-pr-label: 'stale'
exempt-pr-labels: 'blocked,must,should,keep,created-by: Payload team,created-by: Contributor'
stale-pr-message: >
This PR is stale due to lack of activity. To keep the PR open, please indicate that it is still relevant in a comment below.
close-pr-message: >
stale-pr-label: stale
exempt-pr-labels: 'prioritized,keep,created-by: Payload team,created-by: Contributor'
stale-pr-message: |
This PR is stale due to lack of activity.
To keep the PR open, please indicate that it is still relevant in a comment below.
close-pr-message: |
This pull request was automatically closed due to lack of activity.
# TODO: Add a step to notify team
- name: Print outputs
run: echo ${{ format('{0},{1}', toJSON(steps.stale.outputs.staled-issues-prs), toJSON(steps.stale.outputs.closed-issues-prs)) }}

View File

@@ -1,7 +1,7 @@
name: triage
on:
pull_request:
pull_request_target:
types:
- opened
issues:
@@ -18,7 +18,7 @@ permissions:
jobs:
debug-context:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- name: View context attributes
uses: actions/github-script@v7
@@ -27,11 +27,10 @@ jobs:
label-created-by:
name: label-on-open
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- name: Tag with 'created-by'
uses: actions/github-script@v7
if: github.event.action == 'opened'
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
@@ -89,14 +88,15 @@ jobs:
triage:
name: initial-triage
if: github.event_name == 'issues'
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.base.ref }}
token: ${{ secrets.GITHUB_TOKEN }}
- uses: ./.github/actions/triage
with:
reproduction-comment: '.github/comments/invalid-reproduction.md'
reproduction-link-section: '### Link to the code that reproduces this issue(.*)### Reproduction Steps'
reproduction-issue-labels: 'validate-reproduction'
tag-only: 'true'
actions-to-perform: 'tag,comment'

File diff suppressed because it is too large Load Diff

View File

@@ -45,7 +45,7 @@ There are a couple ways to do this:
- **Granularly** - you can run individual tests in vscode by installing the Jest Runner plugin and using that to run individual tests. Clicking the `debug` button will run the test in debug mode allowing you to set break points.
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/src/admin/assets/images/github/int-debug.png" />
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/payload/src/assets/images/github/int-debug.png" />
- **Manually** - you can run all int tests in the `/test/_community/int.spec.ts` file by running the following command:
@@ -62,7 +62,7 @@ The easiest way to run E2E tests is to install
Once they are installed you can open the `testing` tab in vscode sidebar and drill down to the test you want to run, i.e. `/test/_community/e2e.spec.ts`
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/src/admin/assets/images/github/e2e-debug.png" />
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/payload/src/assets/images/github/e2e-debug.png" />
#### Notes

View File

@@ -1,4 +1,4 @@
<a href="https://payloadcms.com"><img width="100%" src="https://github.com/payloadcms/payload/blob/main/packages/payload/src/admin/assets/images/github-banner-alt.jpg?raw=true" alt="Payload headless CMS Admin panel built with React" /></a>
<a href="https://payloadcms.com"><img width="100%" src="https://l4wlsi8vxy8hre4v.public.blob.vercel-storage.com/github-banner-new-logo.jpg" alt="Payload headless CMS Admin panel built with React" /></a>
<br />
<br />
@@ -7,57 +7,54 @@
&nbsp;
<a href="https://discord.gg/payload"><img alt="Discord" src="https://img.shields.io/discord/967097582721572934?label=Discord&color=7289da&style=flat-square" /></a>
&nbsp;
<a href="https://www.npmjs.com/package/payload"><img alt="npm" src="https://img.shields.io/npm/dw/payload?style=flat-square" /></a>
&nbsp;
<a href="https://github.com/payloadcms/payload/graphs/contributors"><img alt="npm" src="https://img.shields.io/github/contributors-anon/payloadcms/payload?color=yellow&style=flat-square" /></a>
&nbsp;
<a href="https://www.npmjs.com/package/payload"><img alt="npm" src="https://img.shields.io/npm/v/payload?style=flat-square" /></a>
&nbsp;
<a href="https://twitter.com/payloadcms"><img src="https://img.shields.io/badge/follow-payloadcms-1DA1F2?logo=twitter&style=flat-square" alt="Payload Twitter" /></a>
</p>
<hr/>
<h4>
<a target="_blank" href="https://payloadcms.com/docs/getting-started/what-is-payload" rel="dofollow"><strong>Explore the Docs</strong></a>&nbsp;·&nbsp;<a target="_blank" href="https://payloadcms.com/community-help" rel="dofollow"><strong>Community Help</strong></a>&nbsp;·&nbsp;<a target="_blank" href="https://demo.payloadcms.com/" rel="dofollow"><strong>Try Live Demo</strong></a>&nbsp;·&nbsp;<a target="_blank" href="https://github.com/payloadcms/payload/discussions/1539" rel="dofollow"><strong>Roadmap</strong></a>&nbsp;·&nbsp;<a target="_blank" href="https://www.g2.com/products/payload-cms/reviews#reviews" rel="dofollow"><strong>View G2 Reviews</strong></a>
<a target="_blank" href="https://payloadcms.com/docs/getting-started/what-is-payload" rel="dofollow"><strong>Explore the Docs</strong></a>&nbsp;·&nbsp;<a target="_blank" href="https://payloadcms.com/community-help" rel="dofollow"><strong>Community Help</strong></a>&nbsp;·&nbsp;<a target="_blank" href="https://github.com/payloadcms/payload/discussions/1539" rel="dofollow"><strong>Roadmap</strong></a>&nbsp;·&nbsp;<a target="_blank" href="https://www.g2.com/products/payload-cms/reviews#reviews" rel="dofollow"><strong>View G2 Reviews</strong></a>
</h4>
<hr/>
> [!IMPORTANT]
> 🎉 <strong>Payload 2.0 is now available!</strong> Read more in the <a target="_blank" href="https://payloadcms.com/blog/payload-2-0" rel="dofollow"><strong>announcement post</strong></a>.
> 🎉 <strong>We've released 3.0!</strong> Star this repo or keep an eye on it to follow along.
Payload is the first-ever Next.js native CMS that can install directly in your existing `/app` folder. It's the start of a new era for headless CMS.
<h3>Benefits over a regular CMS</h3>
<ul>
<li>Dont hit some third-party SaaS API, hit your own API</li>
<li>Use your own database and own your data</li>
<li>It's just Express - do what you want outside of Payload</li>
<li>No need to learn how Payload works - if you know JS, you know Payload</li>
<li>Deploy anywhere, including serverless on Vercel for free</li>
<li>Combine your front+backend in the same <code>/app</code> folder if you want</li>
<li>Don't sign up for yet another SaaS - Payload is open source</li>
<li>Query your database in React Server Components</li>
<li>Both admin and backend are 100% extensible</li>
<li>No vendor lock-in</li>
<li>Avoid microservices hell - get everything (even auth) in one place</li>
<li>Never touch ancient WP code again</li>
<li>Build faster, never hit a roadblock</li>
<li>Both admin and backend are 100% extensible</li>
</ul>
## ☁️ Deploy instantly with Payload Cloud.
Create a cloud account, connect your GitHub, and [deploy in minutes](https://payloadcms.com/new).
## 🚀 Get started by self-hosting completely free, forever.
## Quickstart
Before beginning to work with Payload, make sure you have all of the [required software](https://payloadcms.com/docs/getting-started/installation).
```text
npx create-payload-app@latest
pnpx create-payload-app@latest
```
Alternatively, it only takes about five minutes to [create an app from scratch](https://payloadcms.com/docs/getting-started/installation#from-scratch).
**If you're new to Payload, you should start with the website template** (`pnpx create-payload-app@latest -t website`). It shows how to do _everything_ - including custom Rich Text blocks, on-demand revalidation, live preview, and more. It comes with a frontend built with Tailwind all in one `/app` folder.
## 🖱️ One-click templates
## One-click templates
Jumpstart your next project by starting with a pre-made template. These are production-ready, end-to-end solutions designed to get you to market as fast as possible.
### [🛒 E-Commerce](https://github.com/payloadcms/payload/tree/main/templates/ecommerce)
Eliminate the need to combine Shopify and a CMS, and instead do it all with Payload + Stripe. Comes with a beautiful, fully functional front-end complete with shopping cart, checkout, orders, and much more.
### [🌐 Website](https://github.com/payloadcms/payload/tree/main/templates/website)
Build any kind of website, blog, or portfolio from small to enterprise. Comes with a beautiful, fully functional front-end complete with posts, projects, comments, and much more.
Build any kind of website, blog, or portfolio from small to enterprise. Comes with a fully functional front-end built with RSCs and Tailwind.
We're constantly adding more templates to our [Templates Directory](https://github.com/payloadcms/payload/tree/main/templates). If you maintain your own template, consider adding the `payload-template` topic to your GitHub repository for others to find.
@@ -67,20 +64,19 @@ We're constantly adding more templates to our [Templates Directory](https://gith
## ✨ Features
- Completely free and open-source
- [GraphQL](https://payloadcms.com/docs/graphql/overview), [REST](https://payloadcms.com/docs/rest-api/overview), and [Local](https://payloadcms.com/docs/local-api/overview) APIs
- [Easily customizable ReactJS Admin](https://payloadcms.com/docs/admin/overview)
- [Fully self-hosted](https://payloadcms.com/docs/production/deployment)
- [Extensible Authentication](https://payloadcms.com/docs/authentication/overview)
- [Local file storage & upload](https://payloadcms.com/docs/upload/overview)
- [Version History and Drafts](https://payloadcms.com/docs/versions/overview)
- [Field-based Localization](https://payloadcms.com/docs/configuration/localization)
- [Block-based Layout Builder](https://payloadcms.com/docs/fields/blocks)
- [Extensible SlateJS rich text editor](https://payloadcms.com/docs/fields/rich-text)
- [Array field type](https://payloadcms.com/docs/fields/array)
- [Field conditional logic](https://payloadcms.com/docs/fields/overview#conditional-logic)
- Next.js native, built to run inside _your_ `/app` folder
- Use server components to extend Payload UI
- Query your database directly in server components, no need for REST / GraphQL
- Fully TypeScript with automatic types for your data
- [Auth out of the box](https://payloadcms.com/docs/authentication/overview)
- [Versions and drafts](https://payloadcms.com/docs/versions/overview)
- [Localization](https://payloadcms.com/docs/configuration/localization)
- [Block-based layout builder](https://payloadcms.com/docs/fields/blocks)
- [Customizable React admin](https://payloadcms.com/docs/admin/overview)
- [Lexical rich text editor](https://payloadcms.com/docs/fields/rich-text)
- [Conditional field logic](https://payloadcms.com/docs/fields/overview#conditional-logic)
- Extremely granular [Access Control](https://payloadcms.com/docs/access-control/overview)
- [Document and field-level hooks](https://payloadcms.com/docs/hooks/overview) for every action Payload provides
- Built with Typescript & very Typescript-friendly
- Intensely fast API
- Highly secure thanks to HTTP-only cookies, CSRF protection, and more
@@ -90,7 +86,7 @@ We're constantly adding more templates to our [Templates Directory](https://gith
Check out the [Payload website](https://payloadcms.com/docs/getting-started/what-is-payload) to find in-depth documentation for everything that Payload offers.
Migrating from v1 to v2? Check out the [2.0 Release Notes](https://github.com/payloadcms/payload/releases/tag/v2.0.0) on how to do it.
Migrating from v2 to v3? Check out the [3.0 Migration Guide](https://github.com/payloadcms/payload/blob/main/docs/migration-guide/overview.mdx) on how to do it.
## 🙋 Contributing

View File

@@ -1,8 +1,10 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import configPromise from '@payload-config'
import { RootLayout } from '@payloadcms/next/layouts'
// import '@payloadcms/ui/styles.css' // Uncomment this line if `@payloadcms/ui` in `tsconfig.json` points to `/ui/dist` instead of `/ui/src`
import type { ServerFunctionClient } from 'payload'
import config from '@payload-config'
import { handleServerFunctions, RootLayout } from '@payloadcms/next/layouts'
import React from 'react'
import { importMap } from './admin/importMap.js'
@@ -12,8 +14,17 @@ type Args = {
children: React.ReactNode
}
const serverFunction: ServerFunctionClient = async function (args) {
'use server'
return handleServerFunctions({
...args,
config,
importMap,
})
}
const Layout = ({ children }: Args) => (
<RootLayout config={configPromise} importMap={importMap}>
<RootLayout config={config} importMap={importMap} serverFunction={serverFunction}>
{children}
</RootLayout>
)

View File

@@ -6,7 +6,7 @@ desc: With Collection-level Access Control you can define which users can create
keywords: collections, access control, permissions, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
Collection Access Control is [Access Control](../access-control) used to restrict access to Documents within a [Collection](../collections/overview), as well as what they can and cannot see within the [Admin Panel](../admin/overview) as it relates to that Collection.
Collection Access Control is [Access Control](../access-control/overview) used to restrict access to Documents within a [Collection](../getting-started/concepts#collections), as well as what they can and cannot see within the [Admin Panel](../admin/overview) as it relates to that Collection.
To add Access Control to a Collection, use the `access` property in your [Collection Config](../configuration/collections):
@@ -259,7 +259,7 @@ The following arguments are provided to the `delete` function:
### Admin
If the Collection is use to access the [Admin Panel](../admin/overview#the-admin-user-collection), the `Admin` Access Control function determines whether or not the currently logged in user can access the admin UI.
If the Collection is used to access the [Admin Panel](../admin/overview#the-admin-user-collection), the `Admin` Access Control function determines whether or not the currently logged in user can access the admin UI.
To add Admin Access Control to a Collection, use the `admin` property in the [Collection Config](../collections/overview):
@@ -315,7 +315,7 @@ The following arguments are provided to the `unlock` function:
If the Collection has [Versions](../versions/overview) enabled, the `readVersions` Access Control function determines whether or not the currently logged in user can access the version history of a Document.
To add Read Versions Access Control to a Collection, use the `readVersions` property in the [Collection Config](../collections/overview):
To add Read Versions Access Control to a Collection, use the `readVersions` property in the [Collection Config](../configuration/collections):
```ts
import type { CollectionConfig } from 'payload'

View File

@@ -6,7 +6,7 @@ desc: Field-level Access Control is specified within a field's config, and allow
keywords: fields, access control, permissions, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
Field Access Control is [Access Control](../access-control) used to restrict access to specific [Fields](../fields/overview) within a Document.
Field Access Control is [Access Control](../overview) used to restrict access to specific [Fields](../fields/overview) within a Document.
To add Access Control to a Field, use the `access` property in your [Field Config](../fields/overview):

View File

@@ -6,7 +6,7 @@ desc: Global-level Access Control is specified within each Global's `access` pro
keywords: globals, access control, permissions, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
Global Access Control is [Access Control](../access-control) used to restrict access to [Global](../globals/overview) Documents, as well as what they can and cannot see within the [Admin Panel](../admin/overview) as it relates to that Global.
Global Access Control is [Access Control](../overview) used to restrict access to [Global](../globals/overview) Documents, as well as what they can and cannot see within the [Admin Panel](../admin/overview) as it relates to that Global.
To add Access Control to a Global, use the `access` property in your [Global Config](../configuration/globals):

View File

@@ -25,25 +25,26 @@ export const MyCollection: CollectionConfig = {
The following options are available:
| Option | Description |
| -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`group`** | Text used as a label for grouping Collection and Global links together in the navigation. |
| **`hidden`** | Set to true or a function, called with the current user, returning true to exclude this Collection from navigation and admin routing. |
| **`hooks`** | Admin-specific hooks for this Collection. [More details](../hooks/collections). |
| **`useAsTitle`** | Specify a top-level field to use for a document title throughout the Admin Panel. If no field is defined, the ID of the document is used as the title. A field with `virtual: true` cannot be used as the title. |
| **`description`** | Text to display below the Collection label in the List View to give editors more information. Alternatively, you can use the `admin.components.Description` to render a React component. [More details](#components). |
| **`defaultColumns`** | Array of field names that correspond to which columns to show by default in this Collection's List View. |
| **`hideAPIURL`** | Hides the "API URL" meta field while editing documents within this Collection. |
| **`enableRichTextLink`** | The [Rich Text](../fields/rich-text) field features a `Link` element which allows for users to automatically reference related documents within their rich text. Set to `true` by default. |
| **`enableRichTextRelationship`** | The [Rich Text](../fields/rich-text) field features a `Relationship` element which allows for users to automatically reference related documents within their rich text. Set to `true` by default. |
| **`meta`** | Page metadata overrides to apply to this Collection within the Admin Panel. [More details](./metadata). |
| **`preview`** | Function to generate preview URLs within the Admin Panel that can point to your app. [More details](#preview). |
| **`livePreview`** | Enable real-time editing for instant visual feedback of your front-end application. [More details](../live-preview/overview). |
| **`components`** | Swap in your own React components to be used within this Collection. [More details](#components). |
| **`listSearchableFields`** | Specify which fields should be searched in the List search view. [More details](#list-searchable-fields). |
| **`pagination`** | Set pagination-specific options for this Collection. [More details](#pagination). |
| Option | Description |
| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`group`** | Text or localization object used to group Collection and Global links in the admin navigation. Set to `false` to hide the link from the navigation while keeping its routes accessible. |
| **`hidden`** | Set to true or a function, called with the current user, returning true to exclude this Collection from navigation and admin routing. |
| **`hooks`** | Admin-specific hooks for this Collection. [More details](../hooks/collections). |
| **`useAsTitle`** | Specify a top-level field to use for a document title throughout the Admin Panel. If no field is defined, the ID of the document is used as the title. A field with `virtual: true` cannot be used as the title. |
| **`description`** | Text to display below the Collection label in the List View to give editors more information. Alternatively, you can use the `admin.components.Description` to render a React component. [More details](#custom-components). |
| **`defaultColumns`** | Array of field names that correspond to which columns to show by default in this Collection's List View. |
| **`hideAPIURL`** | Hides the "API URL" meta field while editing documents within this Collection. |
| **`enableRichTextLink`** | The [Rich Text](../fields/rich-text) field features a `Link` element which allows for users to automatically reference related documents within their rich text. Set to `true` by default. |
| **`enableRichTextRelationship`** | The [Rich Text](../fields/rich-text) field features a `Relationship` element which allows for users to automatically reference related documents within their rich text. Set to `true` by default. |
| **`meta`** | Page metadata overrides to apply to this Collection within the Admin Panel. [More details](./metadata). |
| **`preview`** | Function to generate preview URLs within the Admin Panel that can point to your app. [More details](#preview). |
| **`livePreview`** | Enable real-time editing for instant visual feedback of your front-end application. [More details](../live-preview/overview). |
| **`components`** | Swap in your own React components to be used within this Collection. [More details](#custom-components). |
| **`listSearchableFields`** | Specify which fields should be searched in the List search view. [More details](#list-searchable-fields). |
| **`pagination`** | Set pagination-specific options for this Collection. [More details](#pagination). |
| **`baseListFilter`** | You can define a default base filter for this collection's List view, which will be merged into any filters that the user performs. |
### Components
### Custom Components
Collections can set their own [Custom Components](./components) which only apply to [Collection](../configuration/collections)-specific UI within the [Admin Panel](./overview). This includes elements such as the Save Button, or entire layouts such as the Edit View.
@@ -107,12 +108,20 @@ export const Posts: CollectionConfig = {
}
```
The `preview` property resolves to a string that points to your front-end application with additional URL parameters. This can be an absolute URL or a relative path.
The preview function receives two arguments:
| Argument | Description |
| --- | --- |
| **`doc`** | The Document being edited. |
| **`ctx`** | An object containing `locale` and `token` properties. The `token` is the currently logged-in user's JWT. |
| **`ctx`** | An object containing `locale`, `token`, and `req` properties. The `token` is the currently logged-in user's JWT. |
If your application requires a fully qualified URL, such as within deploying to Vercel Preview Deployments, you can use the `req` property to build this URL:
```ts
preview: (doc, { req }) => `${req.protocol}//${req.host}/${doc.slug}` // highlight-line
```
<Banner type="success">
<strong>Note:</strong>

View File

@@ -6,9 +6,9 @@ desc: Fully customize your Admin Panel by swapping in your own React components.
keywords: admin, components, custom, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
The Payload [Admin Panel](./overview) is designed to be as minimal and straightforward as possible to allow for both easy customization and full control over the UI. In order for Payload to support this level of customization, Payload provides a pattern for you to supply your own React components through your [Payload Config](../configuration/overview).
The Payload [Admin Panel](./overview) is designed to be as minimal and straightforward as possible to allow for easy customization and full control over the UI. In order for Payload to support this level of customization, Payload provides a pattern for you to supply your own React components through your [Payload Config](../configuration/overview).
All Custom Components in Payload are [React Server Components](https://react.dev/reference/rsc/server-components) by default, with the exception of [Custom Providers](#custom-providers). This enables the use of the [Local API](../local-api/overview) directly on the front-end. Custom Components are available for nearly every part of the Admin Panel for extreme granularity and control.
All Custom Components in Payload are [React Server Components](https://react.dev/reference/rsc/server-components) by default. This enables the use of the [Local API](../local-api/overview) directly on the front-end. Custom Components are available for nearly every part of the Admin Panel for extreme granularity and control.
<Banner type="success">
<strong>Note:</strong>
@@ -18,51 +18,45 @@ All Custom Components in Payload are [React Server Components](https://react.dev
There are four main types of Custom Components in Payload:
- [Root Components](#root-components)
- [Collection Components](./collections#components)
- [Global Components](./globals#components)
- [Field Components](./fields)
- [Collection Components](./collections#custom-components)
- [Global Components](./globals#custom-components)
- [Field Components](./fields#custom-components)
To swap in your own Custom Component, consult the list of available components. Determine the scope that corresponds to what you are trying to accomplish, then [author your React component(s)](#building-custom-components) accordingly.
To swap in your own Custom Component, first consult the list of available components, determine the scope that corresponds to what you are trying to accomplish, then [author your React component(s)](#building-custom-components) accordingly.
## Defining Custom Components
## Defining Custom Components in the Payload Config
As Payload compiles the Admin Panel, it checks your config for Custom Components. When detected, Payload either replaces its own default component with yours, or if none exists by default, renders yours outright. While are many places where Custom Components are supported in Payload, each is defined in the same way using [Component Paths](#component-paths).
In the Payload Config, you can define custom React Components to enhance the admin interface. However, these components should not be imported directly into the server-only Payload Config to avoid including client-side code. Instead, you specify the path to the component. Heres how you can do it:
To add a Custom Component, point to its file path in your Payload Config:
src/components/Logout.tsx
```tsx
'use client'
import React from 'react'
export const MyComponent = () => {
return (
<button>Click me!</button>
)
}
```
payload.config.ts:
```ts
import { buildConfig } from 'payload'
const config = buildConfig({
// ...
admin: { // highlight-line
admin: {
components: {
logout: {
Button: '/src/components/Logout#MyComponent'
Button: '/src/components/Logout#MyComponent' // highlight-line
}
}
},
})
```
In the path `/src/components/Logout#MyComponent`, `/src/components/Logout` is the file path, and `MyComponent` is the named export. If the component is the default export, the export name can be omitted. Path and export name are separated by a `#`.
<Banner type="success">
<strong>Note:</strong>
All Custom Components can be either Server Components or Client Components, depending on the presence of the `use client` directive at the top of the file.
</Banner>
### Configuring the Base Directory
### Component Paths
Component paths, by default, are relative to your working directory - this is usually where your Next.js config lies. To simplify component paths, you have the option to configure the *base directory* using the `admin.baseDir.baseDir` property:
In order to ensure the Payload Config is fully Node.js compatible and as lightweight as possible, components are not directly imported into your config. Instead, they are identified by their file path for the Admin Panel to resolve on its own.
Component Paths, by default, are relative to your project's base directory. This is either your current working directory, or the directory specified in `config.admin.baseDir`. To simplify Component Paths, you can also configure the base directory using the `admin.importMap.baseDir` property.
Components using named exports are identified either by appending `#` followed by the export name, or using the `exportName` property. If the component is the default export, this can be omitted.
```ts
import { buildConfig } from 'payload'
@@ -73,137 +67,72 @@ const dirname = path.dirname(filename)
const config = buildConfig({
// ...
admin: { // highlight-line
importMap: {
baseDir: path.resolve(dirname, 'src'),
admin: {
importMap: {
baseDir: path.resolve(dirname, 'src'), // highlight-line
},
components: {
logout: {
Button: '/components/Logout#MyComponent'
Button: '/components/Logout#MyComponent' // highlight-line
}
}
},
})
```
In this example, we set the base directory to the `src` directory - thus we can omit the `/src/` part of our component path string.
In this example, we set the base directory to the `src` directory, and omit the `/src/` part of our component path string.
### Passing Props
### Config Options
Each React Component in the Payload Config is typed as `PayloadComponent`. This usually is a string, but can also be an object containing the following properties:
While Custom Components are usually defined as a string, you can also pass in an object with additional options:
| Property | Description |
|---------------|-------------------------------------------------------------------------------------------------------------------------------|
| `clientProps` | Props to be passed to the React Component if it's a Client Component |
| `exportName` | Instead of declaring named exports using `#` in the component path, you can also omit them from `path` and pass them in here. |
| `path` | Path to the React Component. Named exports can be appended to the end of the path, separated by a `#` |
| `serverProps` | Props to be passed to the React Component if it's a Server Component |
To pass in props from the config, you can use the `clientProps` and/or `serverProps` properties. This alleviates the need to use an HOC (Higher-Order-Component) to declare a React Component with props passed in.
Here is an example:
src/components/Logout.tsx
```tsx
'use client'
import React from 'react'
export const MyComponent = ({ text }: { text: string }) => {
return (
<button>Click me! {text}</button>
)
}
```
payload.config.ts:
```ts
import { buildConfig } from 'payload'
const config = buildConfig({
// ...
admin: { // highlight-line
admin: {
components: {
logout: {
// highlight-start
Button: {
path: '/src/components/Logout',
clientProps: {
text: 'Some Text.'
},
exportName: 'MyComponent'
exportName: 'MyComponent',
}
// highlight-end
}
}
},
})
```
### Import Maps
The following options are available:
It's essential to understand how `PayloadComponent` paths function behind the scenes. Directly importing React Components into your Payload Config using import statements can introduce client-only modules like CSS into your server-only config. This could error when attempting to load the Payload Config in server-only environments and unnecessarily increase the size of the Payload Config, which should remain streamlined and efficient for server use.
| Property | Description |
|---------------|-------------------------------------------------------------------------------------------------------------------------------|
| **`clientProps`** | Props to be passed to the Custom Components if it's a Client Component. [More details](#custom-props). |
| **`exportName`** | Instead of declaring named exports using `#` in the component path, you can also omit them from `path` and pass them in here. |
| **`path`** | File path to the Custom Component. Named exports can be appended to the end of the path, separated by a `#`. |
| **`serverProps`** | Props to be passed to the Custom Component if it's a Server Component. [More details](#custom-props). |
Instead, we utilize component paths to reference React Components. This method enhances the Payload Config with actual React Component imports on the client side, without affecting server-side usage. A script is deployed to scan the Payload Config, collecting all component paths and creating an `importMap.js`. This file, located in app/(payload)/admin/importMap.js, must be statically imported by your Next.js root page and layout. The script imports all the React Components from the specified paths into a Map, associating them with their respective paths (the ones you defined).
For more details on how to build Custom Components, see [Building Custom Components](#building-custom-components).
When constructing the `ClientConfig`, Payload uses the component paths as keys to fetch the corresponding React Component imports from the Import Map. It then substitutes the `PayloadComponent` with a `MappedComponent`. A `MappedComponent` includes the React Component and additional metadata, such as whether it's a server or a client component and which props it should receive. These components are then rendered through the `<RenderComponent />` component within the Payload Admin Panel.
### Import Map
Import maps are regenerated whenever you modify any element related to component paths. This regeneration occurs at startup and whenever Hot Module Replacement (HMR) runs. If the import maps fail to regenerate during HMR, you can restart your application and execute the `payload generate:importmap` command to manually create a new import map. If you encounter any errors running this command, see the [Troubleshooting](../local-api/outside-nextjs#troubleshooting) section.
In order for Payload to make use of [Component Paths](#component-paths), an "Import Map" is automatically generated at `app/(payload)/admin/importMap.js`. This file contains every Custom Component in your config, keyed to their respective paths. When Payload needs to lookup a component, it uses this file to find the correct import.
### Component paths in external packages
The Import Map is automatically regenerated at startup and whenever Hot Module Replacement (HMR) runs, or you can run `payload generate:importmap` to manually regenerate it.
Component paths are resolved relative to your project's base directory, which is either your current working directory or the directory specified in `config.admin.baseDir`. When using custom components from external packages, you can't use relative paths. Instead, use an import path that's accessible as if you were writing an import statement in your project's base directory.
#### Custom Imports
For example, to export a field with a custom component from an external package named `my-external-package`:
If needed, custom items can be appended onto the Import Map. This is mostly only relevant for plugin authors who need to add a custom import that is not referenced in a known location.
To add a custom import to the Import Map, use the `admin.dependencies` property in your [Payload Config](../getting-started/overview):
```ts
import type { Field } from 'payload'
export const MyCustomField: Field = {
type: 'text',
name: 'MyField',
admin: {
components: {
Field: 'my-external-package/client#MyFieldComponent'
}
}
}
```
import { buildConfig } from 'payload'
Despite `MyFieldComponent` living in `src/components/MyFieldComponent.tsx` in `my-external-package`, this will not be accessible from the consuming project. Instead, we recommend exporting all custom components from one file in the external package. For example, you can define a `src/client.ts file in `my-external-package`:
```ts
'use client'
export { MyFieldComponent } from './components/MyFieldComponent'
```
Then, update the package.json of `my-external-package:
```json
{
...
"exports": {
"./client": {
"import": "./dist/client.js",
"types": "./dist/client.d.ts",
"default": "./dist/client.js"
}
}
}
```
This setup allows you to specify the component path as `my-external-package/client#MyFieldComponent` as seen above. The import map will generate:
```ts
import { MyFieldComponent } from 'my-external-package/client'
```
which is a valid way to access MyFieldComponent that can be resolved by the consuming project.
### Custom Components from unknown locations
By default, any component paths from known locations are added to the import map. However, if you need to add any components from unknown locations to the import map, you can do so by adding them to the `admin.dependencies` array in your Payload Config. This is mostly only relevant for plugin authors and not for regular Payload users.
Example:
```ts
export default {
export default buildConfig({
// ...
admin: {
// ...
@@ -220,116 +149,11 @@ export default {
}
```
This way, `TestComponent` is added to the import map, no matter if it's referenced in a known location or not. On the client, you can then use the component like this:
```tsx
'use client'
import { RenderComponent, useConfig } from '@payloadcms/ui'
import React from 'react'
export const CustomView = () => {
const { config } = useConfig()
return (
<div>
<RenderComponent mappedComponent={config.admin.dependencies?.myTestComponent} />
</div>
)
}
```
## Root Components
Root Components are those that effect the [Admin Panel](./overview) generally, such as the logo or the main nav.
To override Root Components, use the `admin.components` property in your [Payload Config](../getting-started/overview):
```ts
import { buildConfig } from 'payload'
export default buildConfig({
// ...
admin: {
// highlight-start
components: {
// ...
},
// highlight-end
},
})
```
_For details on how to build Custom Components, see [Building Custom Components](#building-custom-components)._
The following options are available:
| Path | Description |
|-----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`Nav`** | Contains the sidebar / mobile menu in its entirety. |
| **`beforeNavLinks`** | An array of Custom Components to inject into the built-in Nav, _before_ the links themselves. |
| **`afterNavLinks`** | An array of Custom Components to inject into the built-in Nav, _after_ the links. |
| **`beforeDashboard`** | An array of Custom Components to inject into the built-in Dashboard, _before_ the default dashboard contents. |
| **`afterDashboard`** | An array of Custom Components to inject into the built-in Dashboard, _after_ the default dashboard contents. |
| **`beforeLogin`** | An array of Custom Components to inject into the built-in Login, _before_ the default login form. |
| **`afterLogin`** | An array of Custom Components to inject into the built-in Login, _after_ the default login form. |
| **`logout.Button`** | The button displayed in the sidebar that logs the user out. |
| **`graphics.Icon`** | The simplified logo used in contexts like the the `Nav` component. |
| **`graphics.Logo`** | The full logo used in contexts like the `Login` view. |
| **`providers`** | Custom [React Context](https://react.dev/learn/scaling-up-with-reducer-and-context) providers that will wrap the entire Admin Panel. [More details](#custom-providers). |
| **`actions`** | An array of Custom Components to be rendered _within_ the header of the Admin Panel, providing additional interactivity and functionality. |
| **`header`** | An array of Custom Components to be injected above the Payload header. |
| **`views`** | Override or create new views within the Admin Panel. [More details](./views). |
<Banner type="success">
<strong>Note:</strong>
You can also use set [Collection Components](./collections#components) and [Global Components](./globals#components) in their respective configs.
</Banner>
### Custom Providers
As you add more and more Custom Components to your [Admin Panel](./overview), you may find it helpful to add additional [React Context](https://react.dev/learn/scaling-up-with-reducer-and-context)(s). Payload allows you to inject your own context providers in your app so you can export your own custom hooks, etc.
To add a Custom Provider, use the `admin.components.providers` property in your [Payload Config](../getting-started/overview):
```ts
import { buildConfig } from 'payload'
export default buildConfig({
// ...
admin: {
components: {
providers: ['/path/to/MyProvider'], // highlight-line
},
},
})
```
Then build your Custom Provider as follows:
```tsx
'use client'
import React, { createContext, useContext } from 'react'
const MyCustomContext = React.createContext(myCustomValue)
export const MyProvider: React.FC = ({ children }) => {
return (
<MyCustomContext.Provider value={myCustomValue}>
{children}
</MyCustomContext.Provider>
)
}
export const useMyCustomContext = () => useContext(MyCustomContext)
```
<Banner type="warning">
<strong>Reminder:</strong> Custom Providers are by definition Client Components. This means they must include the `use client` directive at the top of their files and cannot use server-only code.
</Banner>
## Building Custom Components
All Custom Components in Payload are [React Server Components](https://react.dev/reference/rsc/server-components) by default, with the exception of [Custom Providers](#custom-providers). This enables the use of the [Local API](../local-api/overview) directly on the front-end, among other things.
All Custom Components in Payload are [React Server Components](https://react.dev/reference/rsc/server-components) by default. This enables the use of the [Local API](../local-api/overview) directly on the front-end, among other things.
### Default Props
To make building Custom Components as easy as possible, Payload automatically provides common props, such as the [`payload`](../local-api/overview) class and the [`i18n`](../configuration/i18n) object. This means that when building Custom Components within the Admin Panel, you do not have to get these yourself.
@@ -359,12 +183,46 @@ Each Custom Component receives the following props by default:
| `payload` | The [Payload](../local-api/overview) class. |
| `i18n` | The [i18n](../configuration/i18n) object. |
Custom Components also receive various other props that are specific to the context in which the Custom Component is being rendered. For example, [Custom Views](./views) receive the `user` prop. For a full list of available props, consult the documentation related to the specific component you are working with.
<Banner type="success">
See [Root Components](#root-components), [Collection Components](#collection-components), [Global Components](#global-components), or [Field Components](#custom-field-components) for a complete list of all available components.
<Banner type="warning">
<strong>Reminder:</strong>
All Custom Components also receive various other props that are specific component being rendered. See [Root Components](#root-components), [Collection Components](./collections#custom-components), [Global Components](./globals#custom-components), or [Field Components](./fields#custom-components) for a complete list of all default props per component.
</Banner>
### Custom Props
To pass in custom props from the config, you can use either the `clientProps` or `serverProps` properties depending on whether your prop is [serializable](https://react.dev/reference/rsc/use-client#serializable-types), and whether your component is a Server or Client Component.
```ts
import { buildConfig } from 'payload'
const config = buildConfig({
// ...
admin: { // highlight-line
components: {
logout: {
Button: {
path: '/src/components/Logout#MyComponent',
clientProps: {
myCustomProp: 'Hello, World!' // highlight-line
},
}
}
}
},
})
```
```tsx
'use client'
import React from 'react'
export const MyComponent = ({ myCustomProp }: { myCustomProp: string }) => {
return (
<button>{myCustomProp}</button>
)
}
```
### Client Components
When [Building Custom Components](#building-custom-components), it's still possible to use client-side code such as `useState` or the `window` object. To do this, simply add the `use client` directive at the top of your file. Payload will automatically detect and remove all default, [non-serializable props](https://react.dev/reference/rsc/use-client#serializable-types) before rendering your component.
@@ -414,6 +272,7 @@ But, the Payload Config is [non-serializable](https://react.dev/reference/rsc/us
For this reason, Payload creates a Client Config and passes it into the Config Provider. This is a serializable version of the Payload Config that can be accessed from any Client Component via the [`useConfig`](./hooks#useconfig) hook:
```tsx
'use client'
import React from 'react'
import { useConfig } from '@payloadcms/ui'
@@ -432,14 +291,13 @@ export const MyClientComponent: React.FC = () => {
See [Using Hooks](#using-hooks) for more details.
</Banner>
All [Field Components](./fields) automatically receive their respective Field Config through a common [`field`](./fields#the-field-prop) prop:
All [Field Components](./fields) automatically receive their respective Field Config through props.
```tsx
'use client'
import React from 'react'
import type { TextFieldClientComponent } from 'payload'
import type { TextFieldServerComponent } from 'payload'
export const MyClientFieldComponent: TextFieldClientComponent = ({ field: { name } }) => {
export const MyClientFieldComponent: TextFieldServerComponent = ({ field: { name } }) => {
return (
<p>
{`This field's name is ${name}`}
@@ -448,28 +306,6 @@ export const MyClientFieldComponent: TextFieldClientComponent = ({ field: { name
}
```
### Using Hooks
To make it easier to [build your Custom Components](#building-custom-components), you can use [Payload's built-in React Hooks](./hooks) in any Client Component. For example, you might want to interact with one of Payload's many React Contexts:
```tsx
'use client'
import React from 'react'
import { useDocumentInfo } from '@payloadcms/ui'
export const MyClientComponent: React.FC = () => {
const { slug } = useDocumentInfo() // highlight-line
return (
<p>{`Entity slug: ${slug}`}</p>
)
}
```
<Banner type="success">
See the [Hooks](./hooks) documentation for a full list of available hooks.
</Banner>
### Getting the Current Language
All Custom Components can support multiple languages to be consistent with Payload's [Internationalization](../configuration/i18n). To do this, first add your translation resources to the [I18n Config](../configuration/i18n).
@@ -492,6 +328,7 @@ export default async function MyServerComponent({ i18n }) {
The best way to do this within a Client Component is to import the `useTranslation` hook from `@payloadcms/ui`:
```tsx
'use client'
import React from 'react'
import { useTranslation } from '@payloadcms/ui'
@@ -535,6 +372,7 @@ export default async function MyServerComponent({ payload, locale }) {
The best way to do this within a Client Component is to import the `useLocale` hook from `@payloadcms/ui`:
```tsx
'use client'
import React from 'react'
import { useLocale } from '@payloadcms/ui'
@@ -556,7 +394,29 @@ const Greeting: React.FC = () => {
See the [Hooks](./hooks) documentation for a full list of available hooks.
</Banner>
### Styling Custom Components
### Using Hooks
To make it easier to [build your Custom Components](#building-custom-components), you can use [Payload's built-in React Hooks](./hooks) in any Client Component. For example, you might want to interact with one of Payload's many React Contexts. To do this, you can one of the many hooks available depending on your needs.
```tsx
'use client'
import React from 'react'
import { useDocumentInfo } from '@payloadcms/ui'
export const MyClientComponent: React.FC = () => {
const { slug } = useDocumentInfo() // highlight-line
return (
<p>{`Entity slug: ${slug}`}</p>
)
}
```
<Banner type="success">
See the [Hooks](./hooks) documentation for a full list of available hooks.
</Banner>
### Adding Styles
Payload has a robust [CSS Library](./customizing-css) that you can use to style your Custom Components similarly to Payload's built-in styling. This will ensure that your Custom Components match the existing design system, and so that they automatically adapt to any theme changes that might occur.
@@ -585,17 +445,106 @@ Then to colorize your Custom Component's background, for example, you can use th
Payload also exports its [SCSS](https://sass-lang.com) library for reuse which includes mixins, etc. To use this, simply import it as follows into your `.scss` file:
```scss
@import '~payload/scss';
@import '~@payloadcms/ui/scss';
.my-component {
@include mid-break {
background-color: var(--theme-elevation-900);
}
}
```
<Banner type="success">
<strong>Note:</strong>
You can also drill into Payload's own component styles, or easily apply global, app-wide CSS. More on that [here](./customizing-css).
</Banner>
## Root Components
Root Components are those that effect the [Admin Panel](./overview) generally, such as the logo or the main nav.
To override Root Components, use the `admin.components` property in your [Payload Config](../getting-started/overview):
```ts
import { buildConfig } from 'payload'
export default buildConfig({
// ...
admin: {
// highlight-start
components: {
// ...
},
// highlight-end
},
})
```
_For details on how to build Custom Components, see [Building Custom Components](#building-custom-components)._
The following options are available:
| Path | Description |
|-----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`Nav`** | Contains the sidebar / mobile menu in its entirety. |
| **`beforeNavLinks`** | An array of Custom Components to inject into the built-in Nav, _before_ the links themselves. |
| **`afterNavLinks`** | An array of Custom Components to inject into the built-in Nav, _after_ the links. |
| **`beforeDashboard`** | An array of Custom Components to inject into the built-in Dashboard, _before_ the default dashboard contents. |
| **`afterDashboard`** | An array of Custom Components to inject into the built-in Dashboard, _after_ the default dashboard contents. |
| **`beforeLogin`** | An array of Custom Components to inject into the built-in Login, _before_ the default login form. |
| **`afterLogin`** | An array of Custom Components to inject into the built-in Login, _after_ the default login form. |
| **`logout.Button`** | The button displayed in the sidebar that logs the user out. |
| **`graphics.Icon`** | The simplified logo used in contexts like the the `Nav` component. |
| **`graphics.Logo`** | The full logo used in contexts like the `Login` view. |
| **`providers`** | Custom [React Context](https://react.dev/learn/scaling-up-with-reducer-and-context) providers that will wrap the entire Admin Panel. [More details](#custom-providers). |
| **`actions`** | An array of Custom Components to be rendered _within_ the header of the Admin Panel, providing additional interactivity and functionality. |
| **`header`** | An array of Custom Components to be injected above the Payload header. |
| **`views`** | Override or create new views within the Admin Panel. [More details](./views). |
<Banner type="success">
<strong>Note:</strong>
You can also use set [Collection Components](./collections#custom-components) and [Global Components](./globals#custom-components) in their respective configs.
</Banner>
### Custom Providers
As you add more and more Custom Components to your [Admin Panel](./overview), you may find it helpful to add additional [React Context](https://react.dev/learn/scaling-up-with-reducer-and-context)(s). Payload allows you to inject your own context providers in your app so you can export your own custom hooks, etc.
To add a Custom Provider, use the `admin.components.providers` property in your [Payload Config](../getting-started/overview):
```ts
import { buildConfig } from 'payload'
export default buildConfig({
// ...
admin: {
components: {
providers: ['/path/to/MyProvider'], // highlight-line
},
},
})
```
Then build your Custom Provider as follows:
```tsx
'use client'
import React, { createContext, useContext } from 'react'
const MyCustomContext = React.createContext(myCustomValue)
export const MyProvider: React.FC = ({ children }) => {
return (
<MyCustomContext.Provider value={myCustomValue}>
{children}
</MyCustomContext.Provider>
)
}
export const useMyCustomContext = () => useContext(MyCustomContext)
```
<Banner type="warning">
<strong>Reminder:</strong>React Context exists only within Client Components. This means they must include the `use client` directive at the top of their files and cannot contain server-only code. To use a Server Component here, simply _wrap_ your Client Component with it.
</Banner>

View File

@@ -8,7 +8,7 @@ keywords: admin, css, scss, documentation, Content Management System, cms, headl
Customizing the Payload [Admin Panel](./overview) through CSS alone is one of the easiest and most powerful ways to customize the look and feel of the dashboard. To allow for this level of customization, Payload:
1. Exposes a [root-level stylesheet](#global-css) for you to easily to inject custom selectors
1. Exposes a [root-level stylesheet](#global-css) for you to inject custom selectors
1. Provides a [CSS library](#css-library) that can be easily overridden or extended
1. Uses [BEM naming conventions](http://getbem.com) so that class names are globally accessible
@@ -30,7 +30,7 @@ Here is an example of how you might target the Dashboard View and change the bac
<Banner type="warning">
<strong>Note:</strong>
If you are building [Custom Components](./overview), it is best to import your own stylesheets directly into your components, rather than using the global stylesheet. You can continue to use the [CSS library](#css-library) as needed.
If you are building [Custom Components](./components), it is best to import your own stylesheets directly into your components, rather than using the global stylesheet. You can continue to use the [CSS library](#css-library) as needed.
</Banner>
### Specificity rules
@@ -62,13 +62,13 @@ You can also override Payload's built-in [CSS Variables](https://developer.mozil
The following variables are defined and can be overridden:
- [Breakpoints](https://github.com/payloadcms/payload/blob/beta/packages/ui/src/scss/queries.scss)
- [Colors](https://github.com/payloadcms/payload/blob/beta/packages/ui/src/scss/colors.scss)
- [Breakpoints](https://github.com/payloadcms/payload/blob/main/packages/ui/src/scss/queries.scss)
- [Colors](https://github.com/payloadcms/payload/blob/main/packages/ui/src/scss/colors.scss)
- Base color shades (white to black by default)
- Success / warning / error color shades
- Theme-specific colors (background, input background, text color, etc.)
- Elevation colors (used to determine how "bright" something should be when compared to the background)
- [Sizing](https://github.com/payloadcms/payload/blob/beta/packages/ui/src/scss/app.scss)
- [Sizing](https://github.com/payloadcms/payload/blob/main/packages/ui/src/scss/app.scss)
- Horizontal gutter
- Transition speeds
- Font sizes

View File

@@ -6,7 +6,7 @@ desc:
keywords:
---
[Fields](../fields/overview) within the [Admin Panel](./overview) can be endlessly customized in their appearance and behavior without affecting their underlying data structure. Fields are designed to withstand heavy modification or even complete replacement through the use of [Custom Field Components](#field-components), [Conditional Logic](#conditional-logic), [Custom Validations](../fields/overview#validation), and more.
[Fields](../fields/overview) within the [Admin Panel](./overview) can be endlessly customized in their appearance and behavior without affecting their underlying data structure. Fields are designed to withstand heavy modification or even complete replacement through the use of [Custom Field Components](#custom-components), [Conditional Logic](#conditional-logic), [Custom Validations](../fields/overview#validation), and more.
For example, your app might need to render a specific interface that Payload does not inherently support, such as a color picker. To do this, you could replace the default [Text Field](../fields/text) input with your own user-friendly component that formats the data into a valid color value.
@@ -50,341 +50,13 @@ The following options are available:
| **`style`** | [CSS Properties](https://developer.mozilla.org/en-US/docs/Web/CSS) to inject into the root element of the field. |
| **`className`** | Attach a [CSS class attribute](https://developer.mozilla.org/en-US/docs/Web/CSS/Class_selectors) to the root DOM element of a field. |
| **`readOnly`** | Setting a field to `readOnly` has no effect on the API whatsoever but disables the admin component's editability to prevent editors from modifying the field's value. |
| **`disabled`** | If a field is `disabled`, it is completely omitted from the [Admin Panel](../admin/overview). |
| **`disabled`** | If a field is `disabled`, it is completely omitted from the [Admin Panel](../admin/overview) entirely. |
| **`disableBulkEdit`** | Set `disableBulkEdit` to `true` to prevent fields from appearing in the select options when making edits for multiple documents. Defaults to `true` for UI fields. |
| **`disableListColumn`** | Set `disableListColumn` to `true` to prevent fields from appearing in the list view column selector. |
| **`disableListFilter`** | Set `disableListFilter` to `true` to prevent fields from appearing in the list view filter options. |
| **`hidden`** | Will transform the field into a `hidden` input type. Its value will still submit with requests in the Admin Panel, but the field itself will not be visible to editors. |
## Field Components
Within the [Admin Panel](./overview), fields are rendered in three distinct places:
- [Field](#the-field-component) - The actual form field rendered in the Edit View.
- [Cell](#the-cell-component) - The table cell component rendered in the List View.
- [Filter](#the-filter-component) - The filter component rendered in the List View.
To easily swap in Field Components with your own, use the `admin.components` property in your [Field Config](../fields/overview):
```ts
import type { CollectionConfig } from 'payload'
export const CollectionConfig: CollectionConfig = {
// ...
fields: [
// ...
{
// ...
admin: {
components: { // highlight-line
// ...
},
},
}
]
}
```
The following options are available:
| Component | Description |
| ---------- | --------------------------------------------------------------------------------------------------------------------------- |
| **`Field`** | The form field rendered of the Edit View. [More details](#the-field-component). |
| **`Cell`** | The table cell rendered of the List View. [More details](#the-cell-component). |
| **`Filter`** | The filter component rendered in the List View. [More details](#the-filter-component). || Component | Description |
| **`Label`** | Override the default Label of the Field Component. [More details](#the-label-component). |
| **`Error`** | Override the default Error of the Field Component. [More details](#the-error-component). |
| **`Description`** | Override the default Description of the Field Component. [More details](#the-description-component). |
| **`beforeInput`** | An array of elements that will be added before the input of the Field Component. [More details](#afterinput-and-beforeinput).|
| **`afterInput`** | An array of elements that will be added after the input of the Field Component. [More details](#afterinput-and-beforeinput). |
_\* **`beforeInput`** and **`afterInput`** are only supported in fields that do not contain other fields, such as [`Text`](../fields/text), and [`Textarea`](../fields/textarea)._
### The Field Component
The Field Component is the actual form field rendered in the Edit View. This is the input that user's will interact with when editing a document.
To easily swap in your own Field Component, use the `admin.components.Field` property in your [Field Config](../fields/overview):
```ts
import type { CollectionConfig } from 'payload'
export const CollectionConfig: CollectionConfig = {
// ...
fields: [
// ...
{
// ...
admin: {
components: {
Field: '/path/to/MyFieldComponent', // highlight-line
},
},
}
]
}
```
_For details on how to build Custom Components, see [Building Custom Components](./components#building-custom-components)._
<Banner type="warning">
Instead of replacing the entire Field Component, you can alternately replace or slot-in only specific parts by using the [`Label`](#the-label-component), [`Error`](#the-error-component), [`beforeInput`](#afterinput-and-beforinput), and [`afterInput`](#afterinput-and-beforinput) properties.
</Banner>
All Field Components receive the following props:
| Property | Description |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`docPreferences`** | An object that contains the [Preferences](./preferences) for the document.
| **`field`** | In Server Components, this is the original Field Config. In Client Components, this is the sanitized Client Field Config. [More details](#the-field-prop). |
| **`clientField`** | Server components receive the Client Field Config through this prop. [More details](#the-field-prop). |
| **`locale`** | The locale of the field. [More details](../configuration/localization). |
| **`readOnly`** | A boolean value that represents if the field is read-only or not. |
| **`user`** | The currently authenticated user. [More details](../authentication/overview). |
| **`validate`** | A function that can be used to validate the field. |
<Banner type="success">
<strong>Reminder:</strong>
All [Custom Server Components](./components) receive the `payload` and `i18n` properties by default. See [Building Custom Components](./components#building-custom-components) for more details.
</Banner>
#### Sending and receiving values from the form
When swapping out the `Field` component, you are responsible for sending and receiving the field's `value` from the form itself.
To do so, import the [`useField`](./hooks#usefield) hook from `@payloadcms/ui` and use it to manage the field's value:
```tsx
'use client'
import { useField } from '@payloadcms/ui'
export const CustomTextField: React.FC = () => {
const { value, setValue } = useField() // highlight-line
return (
<input
onChange={(e) => setValue(e.target.value)}
value={value}
/>
)
}
```
<Banner type="success">
For a complete list of all available React hooks, see the [Payload React Hooks](./hooks) documentation. For additional help, see [Building Custom Components](./components#building-custom-components).
</Banner>
#### TypeScript
When building Custom Field Components, you can import the component type to ensure type safety. There is an explicit type for the Field Component, one for every [Field Type](../fields/overview) and for every client/server environment. The convention is to prepend the field type onto the target type, i.e. `TextFieldClientComponent`:
```tsx
import type {
TextFieldClientComponent,
TextFieldServerComponent,
TextFieldClientProps,
TextFieldServerProps,
// ...and so on for each Field Type
} from 'payload'
```
### The `field` Prop
All Field Components are passed their own Field Config through a common `field` prop. Within Server Components, this is the original Field Config as written within your Payload Config. Within Client Components, however, this is a "Client Config", which is a sanitized, client-friendly version of the Field Config. This is because the original Field Config is [non-serializable](https://react.dev/reference/rsc/use-client#serializable-types), meaning it cannot be passed into Client Components without first being transformed.
The Client Field Config is an exact copy of the original Field Config, minus all non-serializable properties, plus all evaluated functions such as field labels, [Custom Components](../components), etc.
Server Component:
```tsx
import React from 'react'
import type { TextFieldServerComponent } from 'payload'
import { TextField } from '@payloadcms/ui'
export const MyServerField: TextFieldServerComponent = ({ clientField }) => {
return <TextField field={clientField} />
}
```
<Banner type="info">
<strong>Tip:</strong>
Server Components can still access the original Field Config through the `field` prop.
</Banner>
Client Component:
```tsx
'use client'
import React from 'react'
import type { TextFieldClientComponent } from 'payload'
import { TextField } from '@payloadcms/ui'
export const MyTextField: TextFieldClientComponent = ({ field }) => {
return <TextField field={field} />
}
```
The following additional properties are also provided to the `field` prop:
| Property | Description |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`_isPresentational`** | A boolean indicating that the field is purely visual and does not directly affect data or change data shape, i.e. the [UI Field](../fields/ui). |
| **`_path`** | A string representing the direct, dynamic path to the field at runtime, i.e. `myGroup.myArray[0].myField`. |
| **`_schemaPath`** | A string representing the direct, static path to the [Field Config](../fields/overview), i.e. `myGroup.myArray.myField` |
<Banner type="info">
<strong>Note:</strong>
These properties are underscored to denote that they are not part of the original Field Config, and instead are attached during client sanitization to make fields easier to work with on the front-end.
</Banner>
#### TypeScript
When building Custom Field Components, you can import the client field props to ensure type safety in your component. There is an explicit type for the Field Component, one for every [Field Type](../fields/overview) and server/client environment. The convention is to prepend the field type onto the target type, i.e. `TextFieldClientComponent`:
```tsx
import type {
TextFieldClientComponent,
TextFieldServerComponent,
TextFieldClientProps,
TextFieldServerProps,
// ...and so on for each Field Type
} from 'payload'
```
### The Cell Component
The Cell Component is rendered in the table of the List View. It represents the value of the field when displayed in a table cell.
To easily swap in your own Cell Component, use the `admin.components.Cell` property in your [Field Config](../fields/overview):
```ts
import type { Field } from 'payload'
export const myField: Field = {
name: 'myField',
type: 'text',
admin: {
components: {
Cell: '/path/to/MyCustomCellComponent', // highlight-line
},
},
}
```
_For details on how to build Custom Components, see [Building Custom Components](./components#building-custom-components)._
All Cell Components receive the following props:
| Property | Description |
| ---------------- | ----------------------------------------------------------------- |
| **`field`** | In Server Components, this is the original Field Config. In Client Components, this is the sanitized Client Field Config. [More details](#the-field-prop). |
| **`clientField`** | Server components receive the Client Field Config through this prop. [More details](#the-field-prop). |
| **`link`** | A boolean representing whether this cell should be wrapped in a link. |
| **`onClick`** | A function that is called when the cell is clicked. |
<Banner type="info">
<strong>Tip:</strong>
Use the [`useTableCell`](./hooks#usetablecell) hook to subscribe to the field's `cellData` and `rowData`.
</Banner>
<Banner type="success">
<strong>Reminder:</strong>
All [Custom Server Components](./components) receive the `payload` and `i18n` properties by default. See [Building Custom Components](./components#building-custom-components) for more details.
</Banner>
### The Label Component
The Label Component is rendered anywhere a field needs to be represented by a label. This is typically used in the Edit View, but can also be used in the List View and elsewhere.
To easily swap in your own Label Component, use the `admin.components.Label` property in your [Field Config](../fields/overview):
```ts
import type { Field } from 'payload'
export const myField: Field = {
name: 'myField',
type: 'text',
admin: {
components: {
Label: '/path/to/MyCustomLabelComponent', // highlight-line
},
},
}
```
_For details on how to build Custom Components, see [Building Custom Components](./components#building-custom-components)._
Custom Label Components receive all [Field Component](#the-field-component) props, plus the following props:
| Property | Description |
| -------------- | ---------------------------------------------------------------- |
| **`field`** | In Server Components, this is the original Field Config. In Client Components, this is the sanitized Client Field Config. [More details](#the-field-prop). |
| **`clientField`** | Server components receive the Client Field Config through this prop. [More details](#the-field-prop). |
<Banner type="success">
<strong>Reminder:</strong>
All [Custom Server Components](./components) receive the `payload` and `i18n` properties by default. See [Building Custom Components](./components#building-custom-components) for more details.
</Banner>
#### TypeScript
When building Custom Label Components, you can import the component props to ensure type safety in your component. There is an explicit type for the Label Component, one for every [Field Type](../fields/overview) and server/client environment. The convention is to append `LabelServerComponent` or `LabelClientComponent` to the type of field, i.e. `TextFieldLabelClientComponent`.
```tsx
import type {
TextFieldLabelServerComponent,
TextFieldLabelClientComponent,
// ...and so on for each Field Type
} from 'payload'
```
### The Error Component
The Error Component is rendered when a field fails validation. It is typically displayed beneath the field input in a visually-compelling style.
To easily swap in your own Error Component, use the `admin.components.Error` property in your [Field Config](../fields/overview):
```ts
import type { Field } from 'payload'
export const myField: Field = {
name: 'myField',
type: 'text',
admin: {
components: {
Error: '/path/to/MyCustomErrorComponent', // highlight-line
},
},
}
```
_For details on how to build Custom Components, see [Building Custom Components](./components#building-custom-components)._
Custom Error Components receive all [Field Component](#the-field-component) props, plus the following props:
| Property | Description |
| --------------- | ------------------------------------------------------------- |
| **`field`** | In Server Components, this is the original Field Config. In Client Components, this is the sanitized Client Field Config. [More details](#the-field-prop). |
| **`clientField`** | Server components receive the Client Field Config through this prop. [More details](#the-field-prop). |
<Banner type="success">
<strong>Reminder:</strong>
All [Custom Server Components](./components) receive the `payload` and `i18n` properties by default. See [Building Custom Components](./components#building-custom-components) for more details.
</Banner>
#### TypeScript
When building Custom Error Components, you can import the component props to ensure type safety in your component. There is an explicit type for the Error Component, one for every [Field Type](../fields/overview) and server/client environment. The convention is to append `ErrorServerComponent` or `ErrorClientComponent` to the type of field, i.e. `TextFieldErrorClientComponent`.
```tsx
import type {
TextFieldErrorServerComponent,
TextFieldErrorClientComponent,
// And so on for each Field Type
} from 'payload'
```
### The Description Property
## Field Descriptions
Field Descriptions are used to provide additional information to the editor about a field, such as special instructions. Their placement varies from field to field, but typically are displayed with subtle style differences beneath the field inputs.
@@ -392,9 +64,9 @@ A description can be configured in three ways:
- As a string.
- As a function which returns a string. [More details](#description-functions).
- As a React component. [More details](#the-description-component).
- As a React component. [More details](#description).
To easily add a Custom Description to a field, use the `admin.description` property in your [Field Config](../fields/overview):
To add a Custom Description to a field, use the `admin.description` property in your [Field Config](../fields/overview):
```ts
import type { SanitizedCollectionConfig } from 'payload'
@@ -416,14 +88,14 @@ export const MyCollectionConfig: SanitizedCollectionConfig = {
<Banner type="warning">
<strong>Reminder:</strong>
To replace the Field Description with a [Custom Component](./components), use the `admin.components.Description` property. [More details](#the-description-component).
To replace the Field Description with a [Custom Component](./components), use the `admin.components.Description` property. [More details](#description).
</Banner>
#### Description Functions
Custom Descriptions can also be defined as a function. Description Functions are executed on the server and can be used to format simple descriptions based on the user's current [Locale](../configuration/localization).
To easily add a Description Function to a field, set the `admin.description` property to a _function_ in your [Field Config](../fields/overview):
To add a Description Function to a field, set the `admin.description` property to a _function_ in your [Field Config](../fields/overview):
```ts
import type { SanitizedCollectionConfig } from 'payload'
@@ -449,89 +121,11 @@ All Description Functions receive the following arguments:
| -------------- | ---------------------------------------------------------------- |
| **`t`** | The `t` function used to internationalize the Admin Panel. [More details](../configuration/i18n) |
### The Description Component
Alternatively to the [Description Property](#the-description-property), you can also use a [Custom Component](./components) as the Field Description. This can be useful when you need to provide more complex feedback to the user, such as rendering dynamic field values or other interactive elements.
To easily add a Description Component to a field, use the `admin.components.Description` property in your [Field Config](../fields/overview):
```ts
import type { SanitizedCollectionConfig } from 'payload'
export const MyCollectionConfig: SanitizedCollectionConfig = {
// ...
fields: [
// ...
{
name: 'myField',
type: 'text',
admin: {
components: {
Description: '/path/to/MyCustomDescriptionComponent', // highlight-line
}
}
}
]
}
```
_For details on how to build a Custom Description, see [Building Custom Components](./components#building-custom-components)._
Custom Description Components receive all [Field Component](#the-field-component) props, plus the following props:
| Property | Description |
| -------------- | ---------------------------------------------------------------- |
| **`field`** | In Server Components, this is the original Field Config. In Client Components, this is the sanitized Client Field Config. [More details](#the-field-prop). |
| **`clientField`** | Server components receive the Client Field Config through this prop. [More details](#the-field-prop). |
<Banner type="success">
<strong>Reminder:</strong>
All [Custom Server Components](./components) receive the `payload` and `i18n` properties by default. See [Building Custom Components](./components#building-custom-components) for more details.
<Banner type="info">
<strong>Note:</strong>
If you need to subscribe to live updates within your form, use a Description Component instead. [More details](#description).
</Banner>
#### TypeScript
When building Custom Description Components, you can import the component props to ensure type safety in your component. There is an explicit type for the Description Component, one for every [Field Type](../fields/overview) and server/client environment. The convention is to append `DescriptionServerComponent` or `DescriptionClientComponent` to the type of field, i.e. `TextFieldDescriptionClientComponent`.
```tsx
import type {
TextFieldDescriptionServerComponent,
TextFieldDescriptionClientComponent,
// And so on for each Field Type
} from 'payload'
```
### afterInput and beforeInput
With these properties you can add multiple components _before_ and _after_ the input element, as their name suggests. This is useful when you need to render additional elements alongside the field without replacing the entire field component.
To add components before and after the input element, use the `admin.components.beforeInput` and `admin.components.afterInput` properties in your [Field Config](../fields/overview):
```ts
import type { SanitizedCollectionConfig } from 'payload'
export const MyCollectionConfig: SanitizedCollectionConfig = {
// ...
fields: [
// ...
{
name: 'myField',
type: 'text',
admin: {
components: {
// highlight-start
beforeInput: ['/path/to/MyCustomComponent'],
afterInput: ['/path/to/MyOtherCustomComponent'],
// highlight-end
}
}
}
]
}
```
_For details on how to build Custom Components, see [Building Custom Components](./components#building-custom-components)._
## Conditional Logic
You can show and hide fields based on what other fields are doing by utilizing conditional logic on a field by field basis. The `condition` property on a field's admin config accepts a function which takes three arguments:
@@ -570,3 +164,346 @@ The `condition` function should return a boolean that will control if the field
]
}
```
## Custom Components
Within the [Admin Panel](./overview), fields are represented in three distinct places:
- [Field](#field) - The actual form field rendered in the Edit View.
- [Cell](#cell) - The table cell component rendered in the List View.
- [Filter](#filter) - The filter component rendered in the List View.
To swap in Field Components with your own, use the `admin.components` property in your [Field Config](../fields/overview):
```ts
import type { CollectionConfig } from 'payload'
export const CollectionConfig: CollectionConfig = {
// ...
fields: [
// ...
{
// ...
admin: {
components: { // highlight-line
// ...
},
},
}
]
}
```
The following options are available:
| Component | Description |
| ---------- | --------------------------------------------------------------------------------------------------------------------------- |
| **`Field`** | The form field rendered of the Edit View. [More details](#field). |
| **`Cell`** | The table cell rendered of the List View. [More details](#cell). |
| **`Filter`** | The filter component rendered in the List View. [More details](#filter). |
| **`Label`** | Override the default Label of the Field Component. [More details](#label). |
| **`Error`** | Override the default Error of the Field Component. [More details](#error). |
| **`Description`** | Override the default Description of the Field Component. [More details](#description). |
| **`beforeInput`** | An array of elements that will be added before the input of the Field Component. [More details](#afterinput-and-beforeinput).|
| **`afterInput`** | An array of elements that will be added after the input of the Field Component. [More details](#afterinput-and-beforeinput). |
### Field
The Field Component is the actual form field rendered in the Edit View. This is the input that user's will interact with when editing a document.
To swap in your own Field Component, use the `admin.components.Field` property in your [Field Config](../fields/overview):
```ts
import type { CollectionConfig } from 'payload'
export const CollectionConfig: CollectionConfig = {
// ...
fields: [
// ...
{
// ...
admin: {
components: {
Field: '/path/to/MyFieldComponent', // highlight-line
},
},
}
]
}
```
_For details on how to build Custom Components, see [Building Custom Components](./components#building-custom-components)._
<Banner type="warning">
Instead of replacing the entire Field Component, you can alternately replace or slot-in only specific parts by using the [`Label`](#label), [`Error`](#error), [`beforeInput`](#afterinput-and-beforinput), and [`afterInput`](#afterinput-and-beforinput) properties.
</Banner>
#### Default Props
All Field Components receive the following props by default:
| Property | Description |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`docPreferences`** | An object that contains the [Preferences](./preferences) for the document.
| **`field`** | In Client Components, this is the sanitized Client Field Config. In Server Components, this is the original Field Config. Server Components will also receive the sanitized field config through the`clientField` prop (see below). |
| **`locale`** | The locale of the field. [More details](../configuration/localization). |
| **`readOnly`** | A boolean value that represents if the field is read-only or not. |
| **`user`** | The currently authenticated user. [More details](../authentication/overview). |
| **`validate`** | A function that can be used to validate the field. |
| **`path`** | A string representing the direct, dynamic path to the field at runtime, i.e. `myGroup.myArray.0.myField`. |
| **`schemaPath`** | A string representing the direct, static path to the [Field Config](../fields/overview), i.e. `posts.myGroup.myArray.myField`. |
| **`indexPath`** | A hyphen-notated string representing the path to the field _within the nearest named ancestor field_, i.e. `0-0` |
In addition to the above props, all Server Components will also receive the following props:
| Property | Description |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`clientField`** | The serializable Client Field Config. |
| **`field`** | The Field Config. [More details](../fields/overview). |
| **`data`** | The current document being edited. |
| **`i18n`** | The [i18n](../configuration/i18n) object.
| **`payload`** | The [Payload](../local-api/overview) class. |
| **`permissions`** | The field permissions based on the currently authenticated user. |
| **`siblingData`** | The data of the field's siblings. |
| **`user`** | The currently authenticated user. [More details](../authentication/overview). |
| **`value`** | The value of the field at render-time. |
#### Sending and receiving values from the form
When swapping out the `Field` component, you are responsible for sending and receiving the field's `value` from the form itself.
To do so, import the [`useField`](./hooks#usefield) hook from `@payloadcms/ui` and use it to manage the field's value:
```tsx
'use client'
import { useField } from '@payloadcms/ui'
export const CustomTextField: React.FC = () => {
const { value, setValue } = useField() // highlight-line
return (
<input
onChange={(e) => setValue(e.target.value)}
value={value}
/>
)
}
```
<Banner type="success">
For a complete list of all available React hooks, see the [Payload React Hooks](./hooks) documentation. For additional help, see [Building Custom Components](./components#building-custom-components).
</Banner>
#### TypeScript
When building Custom Field Components, you can import the client field props to ensure type safety in your component. There is an explicit type for the Field Component, one for every [Field Type](../fields/overview) and server/client environment. The convention is to prepend the field type onto the target type, i.e. `TextFieldClientComponent`:
```tsx
import type {
TextFieldClientComponent,
TextFieldServerComponent,
TextFieldClientProps,
TextFieldServerProps,
// ...and so on for each Field Type
} from 'payload'
```
### Cell
The Cell Component is rendered in the table of the List View. It represents the value of the field when displayed in a table cell.
To swap in your own Cell Component, use the `admin.components.Cell` property in your [Field Config](../fields/overview):
```ts
import type { Field } from 'payload'
export const myField: Field = {
name: 'myField',
type: 'text',
admin: {
components: {
Cell: '/path/to/MyCustomCellComponent', // highlight-line
},
},
}
```
All Cell Components receive the same [Default Field Component Props](#field), plus the following:
| Property | Description |
| ---------------- | ----------------------------------------------------------------- |
| **`link`** | A boolean representing whether this cell should be wrapped in a link. |
| **`onClick`** | A function that is called when the cell is clicked. |
For details on how to build Custom Components themselves, see [Building Custom Components](./components#building-custom-components).
### Filter
The Filter Component is the actual input element rendered within the "Filter By" dropdown of the List View used to represent this field when building filters.
To swap in your own Filter Component, use the `admin.components.Filter` property in your [Field Config](../fields/overview):
```ts
import type { Field } from 'payload'
export const myField: Field = {
name: 'myField',
type: 'text',
admin: {
components: {
Filter: '/path/to/MyCustomFilterComponent', // highlight-line
},
},
}
```
All Custom Filter Components receive the same [Default Field Component Props](#field).
For details on how to build Custom Components themselves, see [Building Custom Components](./components#building-custom-components).
### Label
The Label Component is rendered anywhere a field needs to be represented by a label. This is typically used in the Edit View, but can also be used in the List View and elsewhere.
To swap in your own Label Component, use the `admin.components.Label` property in your [Field Config](../fields/overview):
```ts
import type { Field } from 'payload'
export const myField: Field = {
name: 'myField',
type: 'text',
admin: {
components: {
Label: '/path/to/MyCustomLabelComponent', // highlight-line
},
},
}
```
All Custom Label Components receive the same [Default Field Component Props](#field).
For details on how to build Custom Components themselves, see [Building Custom Components](./components#building-custom-components).
#### TypeScript
When building Custom Label Components, you can import the component types to ensure type safety in your component. There is an explicit type for the Label Component, one for every [Field Type](../fields/overview) and server/client environment. The convention is to append `LabelServerComponent` or `LabelClientComponent` to the type of field, i.e. `TextFieldLabelClientComponent`.
```tsx
import type {
TextFieldLabelServerComponent,
TextFieldLabelClientComponent,
// ...and so on for each Field Type
} from 'payload'
```
### Description
Alternatively to the [Description Property](#the-description-property), you can also use a [Custom Component](./components) as the Field Description. This can be useful when you need to provide more complex feedback to the user, such as rendering dynamic field values or other interactive elements.
To add a Description Component to a field, use the `admin.components.Description` property in your [Field Config](../fields/overview):
```ts
import type { SanitizedCollectionConfig } from 'payload'
export const MyCollectionConfig: SanitizedCollectionConfig = {
// ...
fields: [
// ...
{
name: 'myField',
type: 'text',
admin: {
components: {
Description: '/path/to/MyCustomDescriptionComponent', // highlight-line
}
}
}
]
}
```
All Custom Description Components receive the same [Default Field Component Props](#field).
For details on how to build a Custom Components themselves, see [Building Custom Components](./components#building-custom-components).
#### TypeScript
When building Custom Description Components, you can import the component props to ensure type safety in your component. There is an explicit type for the Description Component, one for every [Field Type](../fields/overview) and server/client environment. The convention is to append `DescriptionServerComponent` or `DescriptionClientComponent` to the type of field, i.e. `TextFieldDescriptionClientComponent`.
```tsx
import type {
TextFieldDescriptionServerComponent,
TextFieldDescriptionClientComponent,
// And so on for each Field Type
} from 'payload'
```
### Error
The Error Component is rendered when a field fails validation. It is typically displayed beneath the field input in a visually-compelling style.
To swap in your own Error Component, use the `admin.components.Error` property in your [Field Config](../fields/overview):
```ts
import type { Field } from 'payload'
export const myField: Field = {
name: 'myField',
type: 'text',
admin: {
components: {
Error: '/path/to/MyCustomErrorComponent', // highlight-line
},
},
}
```
All Error Components receive the [Default Field Component Props](#field).
For details on how to build Custom Components themselves, see [Building Custom Components](./components#building-custom-components).
#### TypeScript
When building Custom Error Components, you can import the component types to ensure type safety in your component. There is an explicit type for the Error Component, one for every [Field Type](../fields/overview) and server/client environment. The convention is to append `ErrorServerComponent` or `ErrorClientComponent` to the type of field, i.e. `TextFieldErrorClientComponent`.
```tsx
import type {
TextFieldErrorServerComponent,
TextFieldErrorClientComponent,
// And so on for each Field Type
} from 'payload'
```
### afterInput and beforeInput
With these properties you can add multiple components _before_ and _after_ the input element, as their name suggests. This is useful when you need to render additional elements alongside the field without replacing the entire field component.
To add components before and after the input element, use the `admin.components.beforeInput` and `admin.components.afterInput` properties in your [Field Config](../fields/overview):
```ts
import type { SanitizedCollectionConfig } from 'payload'
export const MyCollectionConfig: SanitizedCollectionConfig = {
// ...
fields: [
// ...
{
name: 'myField',
type: 'text',
admin: {
components: {
// highlight-start
beforeInput: ['/path/to/MyCustomComponent'],
afterInput: ['/path/to/MyOtherCustomComponent'],
// highlight-end
}
}
}
]
}
```
All `afterInput` and `beforeInput` Components receive the same [Default Field Component Props](#field).
For details on how to build Custom Components, see [Building Custom Components](./components#building-custom-components).

View File

@@ -25,17 +25,17 @@ export const MyGlobal: GlobalConfig = {
The following options are available:
| Option | Description |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| **`group`** | Text used as a label for grouping Collection and Global links together in the navigation. |
| Option | Description |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| **`group`** | Text or localization object used to group Collection and Global links in the admin navigation. Set to `false` to hide the link from the navigation while keeping its routes accessible. |
| **`hidden`** | Set to true or a function, called with the current user, returning true to exclude this Global from navigation and admin routing. |
| **`components`** | Swap in your own React components to be used within this Global. [More details](#components). |
| **`components`** | Swap in your own React components to be used within this Global. [More details](#custom-components). |
| **`preview`** | Function to generate a preview URL within the Admin Panel for this Global that can point to your app. [More details](#preview). |
| **`livePreview`** | Enable real-time editing for instant visual feedback of your front-end application. [More details](../live-preview/overview). |
| **`hideAPIURL`** | Hides the "API URL" meta field while editing documents within this collection. |
| **`meta`** | Page metadata overrides to apply to this Global within the Admin Panel. [More details](./metadata). |
### Components
### Custom Components
Globals can set their own [Custom Components](./components) which only apply to [Global](../configuration/globals)-specific UI within the [Admin Panel](./overview). This includes elements such as the Save Button, or entire layouts such as the Edit View.

View File

@@ -21,10 +21,11 @@ To do so, import the `useField` hook as follows:
```tsx
'use client'
import type { TextFieldClientComponent } from 'payload'
import { useField } from '@payloadcms/ui'
const CustomTextField: React.FC = () => {
const { value, setValue, path } = useField() // highlight-line
export const CustomTextField: TextFieldClientComponent = ({ path }) => {
const { value, setValue } = useField({ path }) // highlight-line
return (
<div>
@@ -44,7 +45,7 @@ The `useField` hook accepts the following arguments:
| Property | Description |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `path` | If you do not provide a `path` or a `name`, this hook will look for one using the [`useFieldProps`](#usefieldprops) hook. |
| `path` | If you do not provide a `path`, `name` will be used instead. This is the path to the field in the form data. |
| `validate` | A validation function executed client-side _before_ submitting the form to the server. Different than [Field-level Validation](../fields/overview#validation) which runs strictly on the server. |
| `disableFormData` | If `true`, the field will not be included in the form data when the form is submitted. |
| `hasRows` | If `true`, the field will be treated as a field with rows. This is useful for fields like `array` and `blocks`. |
@@ -72,32 +73,6 @@ type FieldType<T> = {
}
```
## useFieldProps
[Custom Field Components](./fields#the-field-component) can be rendered on the server. When using a server component as a custom field component, you can access dynamic props from within any client component rendered by your custom server component. This is done using the `useFieldProps` hook. This is important because some fields can be dynamic, such as when nested in an [`array`](../fields/array) or [`blocks`](../fields/block) field. For example, items can be added, re-ordered, or deleted on-the-fly.
You can use the `useFieldProps` hooks to access dynamic props like `path`:
```tsx
'use client'
import { useFieldProps } from '@payloadcms/ui'
const CustomTextField: React.FC = () => {
const { path } = useFieldProps() // highlight-line
return (
<div>
{path}
</div>
)
}
```
<Banner type="success">
<strong>Tip:</strong>
The [`useField`](#usefield) hook calls the `useFieldProps` hook internally, so you don't need to use both in the same component unless explicitly needed.
</Banner>
## useFormFields
There are times when a custom field component needs to have access to data from other fields, and you have a few options to do so. The `useFormFields` hook is a powerful and highly performant way to retrieve a form's field state, as well as to retrieve the `dispatchFields` method, which can be helpful for setting other fields' form states from anywhere within a form.
@@ -810,7 +785,7 @@ const Greeting: React.FC = () => {
## useConfig
Used to easily retrieve the Payload [Client Config](./components#accessing-the-payload-config).
Used to retrieve the Payload [Client Config](./components#accessing-the-payload-config).
```tsx
'use client'
@@ -900,27 +875,6 @@ const MyComponent: React.FC = () => {
}
```
## useTableCell
Similar to [`useFieldProps`](#usefieldprops), all [Custom Cell Components](./fields#the-cell-component) are rendered on the server, and as such, only have access to static props at render time. But, some props need to be dynamic, such as the field value itself.
For this reason, dynamic props like `cellData` are managed in their own React context, which can be accessed using the `useTableCell` hook.
```tsx
'use client'
import { useTableCell } from '@payloadcms/ui'
const MyComponent: React.FC = () => {
const { cellData } = useTableCell() // highlight-line
return (
<div>
{cellData}
</div>
)
}
```
## useDocumentEvents
The `useDocumentEvents` hook provides a way of subscribing to cross-document events, such as updates made to nested documents within a drawer. This hook will report document events that are outside the scope of the document currently being edited. This hook provides the following:

View File

@@ -184,7 +184,7 @@ export const MyGlobal: GlobalConfig = {
meta: {
// highlight-end
title: 'My Global',
description: 'The best
description: 'The best admin panel in the world',
},
},
}

View File

@@ -86,20 +86,21 @@ const config = buildConfig({
The following options are available:
| Option | Description |
|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **`avatar`** | Set account profile picture. Options: `gravatar`, `default` or a custom React component. |
| **`autoLogin`** | Used to automate log-in for dev and demonstration convenience. [More details](../authentication/overview). |
| **`buildPath`** | Specify an absolute path for where to store the built Admin bundle used in production. Defaults to `path.resolve(process.cwd(), 'build')`. |
| **`components`** | Component overrides that affect the entirety of the Admin Panel. [More details](./components). |
| **`custom`** | Any custom properties you wish to pass to the Admin Panel. |
| **`dateFormat`** | The date format that will be used for all dates within the Admin Panel. Any valid [date-fns](https://date-fns.org/) format pattern can be used. |
| **`disable`** | If set to `true`, the entire Admin Panel will be disabled. |
| **`livePreview`** | Enable real-time editing for instant visual feedback of your front-end application. [More details](../live-preview/overview). |
| **`meta`** | Base metadata to use for the Admin Panel. [More details](./metadata). |
| **`routes`** | Replace built-in Admin Panel routes with your own custom routes. [More details](#customizing-routes). |
| **`theme`** | Restrict the Admin Panel theme to use only one of your choice. Default is `all`.
| **`user`** | The `slug` of the Collection that you want to allow to login to the Admin Panel. [More details](#the-admin-user-collection). |
| Option | Description |
|--------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **`avatar`** | Set account profile picture. Options: `gravatar`, `default` or a custom React component. |
| **`autoLogin`** | Used to automate log-in for dev and demonstration convenience. [More details](../authentication/overview). |
| **`buildPath`** | Specify an absolute path for where to store the built Admin bundle used in production. Defaults to `path.resolve(process.cwd(), 'build')`. |
| **`components`** | Component overrides that affect the entirety of the Admin Panel. [More details](./components). |
| **`custom`** | Any custom properties you wish to pass to the Admin Panel. |
| **`dateFormat`** | The date format that will be used for all dates within the Admin Panel. Any valid [date-fns](https://date-fns.org/) format pattern can be used. |
| **`disable`** | If set to `true`, the entire Admin Panel will be disabled. |
| **`livePreview`** | Enable real-time editing for instant visual feedback of your front-end application. [More details](../live-preview/overview). |
| **`meta`** | Base metadata to use for the Admin Panel. [More details](./metadata). |
| **`routes`** | Replace built-in Admin Panel routes with your own custom routes. [More details](#customizing-routes). |
| **`suppressHydrationWarning`** | If set to `true`, suppresses React hydration mismatch warnings during the hydration of the root <html> tag. Defaults to `false`. |
| **`theme`** | Restrict the Admin Panel theme to use only one of your choice. Default is `all`. |
| **`user`** | The `slug` of the Collection that you want to allow to login to the Admin Panel. [More details](#the-admin-user-collection). |
<Banner type="success">
<strong>Reminder:</strong>
@@ -143,7 +144,7 @@ It is also possible to allow multiple user types into the Admin Panel with limit
- `super-admin` - full access to the Admin Panel to perform any action
- `editor` - limited access to the Admin Panel to only manage content
To do this, add a `roles` or similar field to your auth-enabled Collection, then use the `access.admin` property to grant or deny access based on the value of that field. See [Access Control](/docs/access-control/overview) for full details. For a complete, working example of role-based access control, check out the official [Auth Example](https://github.com/payloadcms/payload/tree/main/examples/auth/payload).
To do this, add a `roles` or similar field to your auth-enabled Collection, then use the `access.admin` property to grant or deny access based on the value of that field. See [Access Control](/docs/access-control/overview) for full details. For a complete, working example of role-based access control, check out the official [Auth Example](https://github.com/payloadcms/payload/tree/main/examples/auth).
## Customizing Routes
@@ -177,7 +178,7 @@ The following options are available:
<Banner type="success">
<strong>Tip:</strong>
You can easily add _new_ routes to the Admin Panel through [Custom Endpoints](../rest-api/overview#custom-endpoints) and [Custom Views](./views).
You can easily add _new_ routes to the Admin Panel through [Custom Endpoints](../rest-api/overview#custom-endpoints) and [Custom Views](./views).
</Banner>
#### Customizing Root-level Routes
@@ -195,7 +196,7 @@ app/
<Banner type="warning">
<strong>Note:</strong>
If you set Root-level Routes _before_ auto-generating the Admin Panel, your [Project Structure](#project-structure) will already be set up correctly.
If you set Root-level Routes _before_ auto-generating the Admin Panel via `create-payload-app`, your [Project Structure](#project-structure) will already be set up correctly.
</Banner>
### Admin-level Routes
@@ -237,7 +238,7 @@ The following options are available:
## I18n
The Payload Admin Panel is translated in over [30 languages and counting](https://github.com/payloadcms/payload/tree/beta/packages/translations). Languages are automatically detected based on the user's browser and used by the Admin Panel to display all text in that language. If no language was detected, or if the user's language is not yet supported, English will be chosen. Users can easily specify their language by selecting one from their account page. See [I18n](../configuration/i18n) for more information.
The Payload Admin Panel is translated in over [30 languages and counting](https://github.com/payloadcms/payload/tree/main/packages/translations). Languages are automatically detected based on the user's browser and used by the Admin Panel to display all text in that language. If no language was detected, or if the user's language is not yet supported, English will be chosen. Users can easily specify their language by selecting one from their account page. See [I18n](../configuration/i18n) for more information.
## Light and Dark Modes

View File

@@ -47,7 +47,7 @@ Payload automatically creates an internally used `payload-preferences` Collectio
## APIs
Preferences are available to both [GraphQL](/docs/graphql/overview#preferences) and [REST](/docs/rest-api/overview#) APIs.
Preferences are available to both [GraphQL](/docs/graphql/overview#preferences) and [REST](/docs/rest-api/overview#preferences) APIs.
## Adding or reading Preferences in your own components

View File

@@ -15,13 +15,13 @@ There are four types of views within the Admin Panel:
- [Global Views](#global-views)
- [Document Views](#document-views)
To swap in your own Custom Views, consult the list of available components. Determine the scope that corresponds to what you are trying to accomplish, then [author your React component(s)](#building-custom-views) accordingly.
To swap in your own Custom View, first consult the list of available components, determine the scope that corresponds to what you are trying to accomplish, then [author your React component(s)](#building-custom-views) accordingly.
## Root Views
Root Views are the main views of the [Admin Panel](./overview). These are views that are scoped directly under the `/admin` route, such as the Dashboard or Account views.
To easily swap Root Views with your own, or to [create entirely new ones](#adding-new-root-views), use the `admin.components.views` property of your root [Payload Config](../configuration/overview):
To swap Root Views with your own, or to [create entirely new ones](#adding-new-root-views), use the `admin.components.views` property of your root [Payload Config](../configuration/overview):
```ts
import { buildConfig } from 'payload'
@@ -143,7 +143,7 @@ The above example shows how to add a new [Root View](#root-views), but the patte
Collection Views are views that are scoped under the `/collections` route, such as the Collection List and Document Edit views.
To easily swap out Collection Views with your own, or to [create entirely new ones](#adding-new-views), use the `admin.components.views` property of your [Collection Config](../collections/overview):
To swap out Collection Views with your own, or to [create entirely new ones](#adding-new-views), use the `admin.components.views` property of your [Collection Config](../collections/overview):
```ts
import type { SanitizedCollectionConfig } from 'payload'
@@ -198,7 +198,7 @@ The following options are available:
Global Views are views that are scoped under the `/globals` route, such as the Document Edit View.
To easily swap out Global Views with your own or [create entirely new ones](#adding-new-views), use the `admin.components.views` property in your [Global Config](../globals/overview):
To swap out Global Views with your own or [create entirely new ones](#adding-new-views), use the `admin.components.views` property in your [Global Config](../globals/overview):
```ts
import type { SanitizedGlobalConfig } from 'payload'
@@ -248,7 +248,7 @@ The following options are available:
Document Views are views that are scoped under the `/collections/:collectionSlug/:id` or the `/globals/:globalSlug` route, such as the Edit View or the API View. All Document Views keep their overall structure across navigation changes, such as their title and tabs, and replace only the content below.
To easily swap out Document Views with your own, or to [create entirely new ones](#adding-new-document-views), use the `admin.components.views.Edit[key]` property in your [Collection Config](../collections/overview) or [Global Config](../globals/overview):
To swap out Document Views with your own, or to [create entirely new ones](#adding-new-document-views), use the `admin.components.views.Edit[key]` property in your [Collection Config](../collections/overview) or [Global Config](../globals/overview):
```ts
import type { SanitizedCollectionConfig } from 'payload'
@@ -350,6 +350,8 @@ export const MyCollectionConfig: SanitizedCollectionConfig = {
}
```
### Default Props
Your Custom Views will be provided with the following props:
| Prop | Description |
@@ -359,6 +361,7 @@ Your Custom Views will be provided with the following props:
| **`importMap`** | The import map object. |
| **`params`** | An object containing the [Dynamic Route Parameters](https://nextjs.org/docs/app/building-your-application/routing/dynamic-routes). |
| **`searchParams`** | An object containing the [Search Parameters](https://developer.mozilla.org/docs/Learn/Common_questions/What_is_a_URL#parameters). |
| **`doc`** | The document being edited. Only available in Document Views. [More details](#document-views). |
<Banner type="success">
<strong>Reminder:</strong>

View File

@@ -6,7 +6,7 @@ desc: Email Verification allows users to verify their email address before they'
keywords: authentication, email, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
[Authentication](./overview) ties directly into the [Email](../email) functionality that Payload provides. This allows you to send emails to users for verification, password resets, and more. While Payload provides default email templates for these actions, you can customize them to fit your brand.
[Authentication](./overview) ties directly into the [Email](../email/overview) functionality that Payload provides. This allows you to send emails to users for verification, password resets, and more. While Payload provides default email templates for these actions, you can customize them to fit your brand.
## Email Verification
@@ -34,8 +34,8 @@ The following options are available:
| Option | Description |
|----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **`generateEmailHTML`** | Allows for overriding the HTML within emails that are sent to users indicating how to validate their account. [More details](#generateEmailHTML). |
| **`generateEmailSubject`** | Allows for overriding the subject of the email that is sent to users indicating how to validate their account. [More details](#generateEmailSubject). |
| **`generateEmailHTML`** | Allows for overriding the HTML within emails that are sent to users indicating how to validate their account. [More details](#generateemailhtml). |
| **`generateEmailSubject`** | Allows for overriding the subject of the email that is sent to users indicating how to validate their account. [More details](#generateemailsubject). |
#### generateEmailHTML
@@ -111,6 +111,7 @@ The following options are available:
| Option | Description |
|----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **`expiration`** | Configure how long password reset tokens remain valid, specified in milliseconds. |
| **`generateEmailHTML`** | Allows for overriding the HTML within emails that are sent to users attempting to reset their password. [More details](#generateEmailHTML). |
| **`generateEmailSubject`** | Allows for overriding the subject of the email that is sent to users attempting to reset their password. [More details](#generateEmailSubject). |

View File

@@ -273,7 +273,7 @@ const result = await payload.verifyEmail({
If a user locks themselves out and you wish to deliberately unlock them, you can utilize the Unlock operation. The [Admin Panel](../admin/overview) features an Unlock control automatically for all collections that feature max login attempts, but you can programmatically unlock users as well by using the Unlock operation.
To restrict who is allowed to unlock users, you can utilize the [`unlock`](../access-control/overview#unlock) access control function.
To restrict who is allowed to unlock users, you can utilize the [`unlock`](../access-control/collections#unlock) access control function.
**Example REST API unlock**:

View File

@@ -22,7 +22,7 @@ Here are some common use cases of Authentication in your own applications:
When Authentication is enabled on a [Collection](../configuration/collections), Payload injects all necessary functionality to support the entire user flow. This includes all [auth-related operations](./operations) like account creation, logging in and out, and resetting passwords, all [auth-related emails](./email) like email verification and password reset, as well as any necessary UI to manage users from the Admin Panel.
To enable Authentication on a Collection, use the `auth` property in the [Collection Config](../configuration/collection#auth):
To enable Authentication on a Collection, use the `auth` property in the [Collection Config](../configuration/collections#config-options):
```ts
import type { CollectionConfig } from 'payload'

View File

@@ -46,6 +46,6 @@ _Creating a new project from an existing repository._
<Banner type="warning">
<strong>Note:</strong> In order to make use of the features of Payload Cloud in your own codebase,
you will need to add the [Cloud Plugin](https://github.com/payloadcms/plugin-cloud) to your
you will need to add the [Cloud Plugin](https://github.com/payloadcms/payload/tree/main/packages/payload-cloud) to your
Payload app.
</Banner>

View File

@@ -28,7 +28,7 @@ Your Payload Cloud project comes with a MongoDB serverless Atlas DB instance or
Payload Cloud gives you S3 file storage backed by Cloudflare as a CDN, and this plugin extends Payload so that all of your media will be stored in S3 rather than locally.
AWS Cognito is used for authentication to your S3 bucket. The [Payload Cloud Plugin](https://github.com/payloadcms/plugin-cloud) will automatically pick up these values. These values are only if you'd like to access your files directly, outside of Payload Cloud.
AWS Cognito is used for authentication to your S3 bucket. The [Payload Cloud Plugin](https://github.com/payloadcms/payload/tree/main/packages/payload-cloud) will automatically pick up these values. These values are only if you'd like to access your files directly, outside of Payload Cloud.
### Accessing Files Outside of Payload Cloud
@@ -98,7 +98,7 @@ From there, you are ready to make updates to your project. When you are ready to
Projects generated from a template will come pre-configured with the official Cloud Plugin, but if you are using your own repository you will need to add this into your project. To do so, add the plugin to your Payload Config:
`yarn add @payloadcms/payload-cloud`
`pnpm add @payloadcms/payload-cloud`
```js
import { payloadCloudPlugin } from '@payloadcms/payload-cloud'

View File

@@ -52,7 +52,7 @@ export const Posts: CollectionConfig = {
<Banner type="success">
<strong>Reminder:</strong>
For a more complex example, see the [Public Demo](https://github.com/payloadcms/public-demo) source code on GitHub, or the [Templates](https://github.com/payloadcms/payload/tree/main/templates) and [Examples](https://github.com/payloadcms/payload/tree/main/examples) directories in the Payload repository.
For more complex examples, see the [Templates](https://github.com/payloadcms/payload/tree/main/templates) and [Examples](https://github.com/payloadcms/payload/tree/main/examples) directories in the Payload repository.
</Banner>
The following options are available:

View File

@@ -6,7 +6,7 @@ desc: Set up your Global config for your needs by defining fields, adding slugs
keywords: globals, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
Globals are in many ways similar to [Collections](../configuration/collections), except they correspond to only a single Document. You can define as many Globals as your application needs. Each Global Document is stored in the [Database](../database/overview) based on the [Fields](../fields/overview) that you define, and automatically generates a [Local API](../local-api/overview), [REST API](../rest-api/overview), and [GraphQL API](../graphql/overview) used to manage your Documents.
Globals are in many ways similar to [Collections](../configuration/collections), except that they correspond to only a single Document. You can define as many Globals as your application needs. Each Global Document is stored in the [Database](../database/overview) based on the [Fields](../fields/overview) that you define, and automatically generates a [Local API](../local-api/overview), [REST API](../rest-api/overview), and [GraphQL API](../graphql/overview) used to manage your Documents.
Globals are the primary way to structure singletons in Payload, such as a header navigation, site-wide banner alerts, or app-wide localized strings. Each Global can have its own unique [Access Control](../access-control/overview), [Hooks](../hooks/overview), [Admin Options](#admin-options), and more.
@@ -60,7 +60,7 @@ export const Nav: GlobalConfig = {
<Banner type="success">
<strong>Reminder:</strong>
For a more complex example, see the [Public Demo](https://github.com/payloadcms/public-demo) source code on GitHub, or the [Templates](https://github.com/payloadcms/payload/tree/main/templates) and [Examples](https://github.com/payloadcms/payload/tree/main/examples) directories in the Payload repository.
For more complex examples, see the [Templates](https://github.com/payloadcms/payload/tree/main/templates) and [Examples](https://github.com/payloadcms/payload/tree/main/examples) directories in the Payload repository.
</Banner>
The following options are available:

View File

@@ -6,9 +6,9 @@ desc: Manage and customize internationalization support in your CMS editor exper
keywords: internationalization, i18n, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
The [Admin Panel](../admin/overview) is translated in over [30 languages and counting](https://github.com/payloadcms/payload/tree/beta/packages/translations). With I18n, editors can navigate the interface and read API error messages in their preferred language. This is similar to [Localization](./localization), but instead of managing translations for the data itself, you are managing translations for your application's interface.
The [Admin Panel](../admin/overview) is translated in over [30 languages and counting](https://github.com/payloadcms/payload/tree/main/packages/translations). With I18n, editors can navigate the interface and read API error messages in their preferred language. This is similar to [Localization](./localization), but instead of managing translations for the data itself, you are managing translations for your application's interface.
By default, Payload comes with preinstalled with English, but you can easily load other languages into your own application. Languages are automatically detected based on the request. If no language was detected, or if the user's language is not yet supported by your application, English will be chosen.
By default, Payload comes preinstalled with English, but you can easily load other languages into your own application. Languages are automatically detected based on the request. If no language is detected, or if the user's language is not yet supported by your application, English will be chosen.
To configure I18n, use the `i18n` key in your [Payload Config](./overview):

View File

@@ -35,7 +35,7 @@ import { buildConfig } from 'payload'
export default buildConfig({
// ...
localization: {
locales: ['en', 'es', 'de'] // required
locales: ['en', 'es', 'de'], // required
defaultLocale: 'en', // required
},
})
@@ -65,7 +65,7 @@ export default buildConfig({
},
],
defaultLocale: 'en', // required
fallback: true,
fallback: true, // defaults to true
},
})
```
@@ -81,7 +81,7 @@ The following options are available:
| -------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| **`locales`** | Array of all the languages that you would like to support. [More details](#locales) |
| **`defaultLocale`** | Required string that matches one of the locale codes from the array provided. By default, if no locale is specified, documents will be returned in this locale. |
| **`fallback`** | Boolean enabling "fallback" locale functionality. If a document is requested in a locale, but a field does not have a localized value corresponding to the requested locale, then if this property is enabled, the document will automatically fall back to the fallback locale value. If this property is not enabled, the value will not be populated. |
| **`fallback`** | Boolean enabling "fallback" locale functionality. If a document is requested in a locale, but a field does not have a localized value corresponding to the requested locale, then if this property is enabled, the document will automatically fall back to the fallback locale value. If this property is not enabled, the value will not be populated unless a fallback is explicitly provided in the request. True by default. |
### Locales

View File

@@ -8,7 +8,7 @@ keywords: overview, config, configuration, documentation, Content Management Sys
Payload is a _config-based_, code-first CMS and application framework. The Payload Config is central to everything that Payload does, allowing for deep configuration of your application through a simple and intuitive API. The Payload Config is a fully-typed JavaScript object that can be infinitely extended upon.
Everything from your [Database](../database/overview) choice, to the appearance of the [Admin Panel](../admin/overview), is fully controlled through the Payload Config. From here you can define [Fields](../fields/overview), add [Localization](./localization), enable [Authentication](../authentication/overview), configure [Access Control](../access-control/overview), and so much more.
Everything from your [Database](../database/overview) choice to the appearance of the [Admin Panel](../admin/overview) is fully controlled through the Payload Config. From here you can define [Fields](../fields/overview), add [Localization](./localization), enable [Authentication](../authentication/overview), configure [Access Control](../access-control/overview), and so much more.
The Payload Config is a `payload.config.ts` file typically located in the root of your project:
@@ -29,7 +29,7 @@ The Payload Config is strongly typed and ties directly into Payload's TypeScript
## Config Options
To author your Payload Config, first determine which [Database](../database/overview) you'd like to use, then use [Collections](./collections) or [Globals](./globals) to define the schema of your data.
To author your Payload Config, first determine which [Database](../database/overview) you'd like to use, then use [Collections](./collections) or [Globals](./globals) to define the schema of your data through [Fields](../fields/overview).
Here is one of the simplest possible Payload configs:
@@ -58,7 +58,7 @@ export default buildConfig({
<Banner type="success">
<strong>Note:</strong>
For a more complex example, see the [Public Demo](https://github.com/payloadcms/public-demo) source code on GitHub, or the [Templates](https://github.com/payloadcms/payload/tree/main/templates) and [Examples](https://github.com/payloadcms/payload/tree/main/examples) directories in the Payload repository.
For more complex examples, see the [Templates](https://github.com/payloadcms/payload/tree/main/templates) and [Examples](https://github.com/payloadcms/payload/tree/main/examples) directories in the Payload repository.
</Banner>
The following options are available:
@@ -76,6 +76,7 @@ The following options are available:
| **`cors`** | Cross-origin resource sharing (CORS) is a mechanism that accept incoming requests from given domains. You can also customize the `Access-Control-Allow-Headers` header. [More details](#cors). |
| **`localization`** | Opt-in to translate your content into multiple locales. [More details](./localization). |
| **`logger`** | Logger options, logger options with a destination stream, or an instantiated logger instance. [More details](https://getpino.io/#/docs/api?id=options). |
| **`loggingLevels`** | An object to override the level to use in the logger for Payload's errors. |
| **`graphQL`** | Manage GraphQL-specific functionality, including custom queries and mutations, query complexity limits, etc. [More details](../graphql/overview#graphql-options). |
| **`cookiePrefix`** | A string that will be prefixed to all cookies that Payload sets. |
| **`csrf`** | A whitelist array of URLs to allow Payload to accept cookies from. [More details](../authentication/overview#csrf-protection). |

View File

@@ -51,12 +51,44 @@ export async function down({ payload, req }: MigrateDownArgs): Promise<void> {
## Using Transactions
When migrations are run, each migration is performed in a new [transactions](/docs/database/transactions) for you. All
When migrations are run, each migration is performed in a new [transaction](/docs/database/transactions) for you. All
you need to do is pass the `req` object to any [local API](/docs/local-api/overview) or direct database calls, such as
`payload.db.updateMany()`, to make database changes inside the transaction. Assuming no errors were thrown, the transaction is committed
after your `up` or `down` function runs. If the migration errors at any point or fails to commit, it is caught and the
transaction gets aborted. This way no change is made to the database if the migration fails.
### Using database directly with the transaction
Additionally, you can bypass Payload's layer entirely and perform operations directly on your underlying database within the active transaction:
### MongoDB:
```ts
import { type MigrateUpArgs } from '@payloadcms/db-mongodb'
export async function up({ session, payload, req }: MigrateUpArgs): Promise<void> {
const posts = await payload.db.collections.posts.collection.find({ session }).toArray()
}
```
### Postgres:
```ts
import { type MigrateUpArgs, sql } from '@payloadcms/db-postgres'
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
const { rows: posts } = await db.execute(sql`SELECT * from posts`)
}
```
### SQLite:
In SQLite, transactions are disabled by default. [More](./transactions).
```ts
import { type MigrateUpArgs, sql } from '@payloadcms/db-sqlite'
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
const { rows: posts } = await db.run(sql`SELECT * from posts`)
}
```
## Migrations Directory
Each DB adapter has an optional property `migrationDir` where you can override where you want your migrations to be

View File

@@ -67,6 +67,6 @@ You should prefer a relational DB like Postgres or SQLite if:
## Payload Differences
It's important to note that nearly every Payload feature is available in all of our officially supported Database Adapters, including [Localization](../configuration/localization), [Arrays](../fields/array), [Blocks](../fields/blocks), etc. The only thing that is not supported in Postgres yet is the [Point Field](/docs/fields/point), but that should be added soon.
It's important to note that nearly every Payload feature is available in all of our officially supported Database Adapters, including [Localization](../configuration/localization), [Arrays](../fields/array), [Blocks](../fields/blocks), etc. The only thing that is not supported in SQLite yet is the [Point Field](/docs/fields/point), but that should be added soon.
It's up to you to choose which database you would like to use based on the requirements of your project. Payload has no opinion on which database you should ultimately choose.

View File

@@ -60,7 +60,7 @@ export default buildConfig({
| `schemaName` (experimental) | A string for the postgres schema to use, defaults to 'public'. |
| `idType` | A string of 'serial', or 'uuid' that is used for the data type given to id columns. |
| `transactionOptions` | A PgTransactionConfig object for transactions, or set to `false` to disable using transactions. [More details](https://orm.drizzle.team/docs/transactions) |
| `disableCreateDatabase` | Pass `true` to disale auto database creation if it doesn't exist. Defaults to `false`. |
| `disableCreateDatabase` | Pass `true` to disable auto database creation if it doesn't exist. Defaults to `false`. |
| `localesSuffix` | A string appended to the end of table names for storing localized fields. Default is '_locales'. |
| `relationshipsSuffix` | A string appended to the end of table names for storing relationships. Default is '_rels'. |
| `versionsSuffix` | A string appended to the end of table names for storing versions. Defaults to '_v'. |

View File

@@ -16,7 +16,13 @@ By default, Payload will use transactions for all data changing operations, as l
MongoDB requires a connection to a replicaset in order to make use of transactions.
</Banner>
The initial request made to Payload will begin a new transaction and attach it to the `req.transactionID`. If you have a `hook` that interacts with the database, you can opt-in to using the same transaction by passing the `req` in the arguments. For example:
<Banner type="info">
<strong>Note:</strong>
<br />
Transactions in SQLite are disabled by default. You need to pass `transactionOptions: {}` to enable them.
</Banner>
The initial request made to Payload will begin a new transaction and attach it to the `req.transactionID`. If you have a `hook` that interacts with the database, you can opt in to using the same transaction by passing the `req` in the arguments. For example:
```ts
const afterChange: CollectionAfterChangeHook = async ({ req }) => {
@@ -65,9 +71,9 @@ When writing your own scripts or custom endpoints, you may wish to have direct c
The following functions can be used for managing transactions:
`payload.db.beginTransaction` - Starts a new session and returns a transaction ID for use in other Payload Local API calls.
`payload.db.commitTransaction` - Takes the identifier for the transaction, finalizes any changes.
`payload.db.rollbackTransaction` - Takes the identifier for the transaction, discards any changes.
- `payload.db.beginTransaction` - Starts a new session and returns a transaction ID for use in other Payload Local API calls.
- `payload.db.commitTransaction` - Takes the identifier for the transaction, finalizes any changes.
- `payload.db.rollbackTransaction` - Takes the identifier for the transaction, discards any changes.
Payload uses the `req` object to pass the transaction ID through to the database adapter. If you are not using the `req` object, you can make a new object to pass the transaction ID directly to database adapter methods and local API calls.
Example:

View File

@@ -6,36 +6,17 @@ desc:
keywords: example, examples, starter, boilerplate, template, templates
---
Payload provides a vast array of examples to help you get started with your project no matter what you are working on. These examples are designed to be easy to get up and running, and to be easy to understand. They showcase nothing more than the specific features being demonstrated so you can easily decipher what is going on.
Examples are changing every day, so be sure to check back often to see what new examples have been added. If you have a specific example you would like to see, please feel free to start a new [Discussion](https://github.com/payloadcms/payload/discussions) or open a new [PR](https://github.com/payloadcms/payload/pulls) to add it yourself.
Payload provides a vast array of examples to help you get started with your project no matter what you are working on. These examples are designed to be easy to get up and running, and to be easy to understand. They showcase nothing more than the specific features being demonstrated so you can easily decipher precisely what is going on.
- [Auth](https://github.com/payloadcms/payload/tree/main/examples/auth)
- [Custom Components](https://github.com/payloadcms/payload/tree/main/examples/custom-components)
- [Custom Server](https://github.com/payloadcms/payload/tree/main/examples/custom-server)
- [Draft Preview](https://github.com/payloadcms/payload/tree/main/examples/draft-preview)
- [Email](https://github.com/payloadcms/payload/tree/main/examples/email)
- [Form Builder](https://github.com/payloadcms/payload/tree/main/examples/form-builder)
- [Hierarchy](https://github.com/payloadcms/payload/tree/main/examples/hierarchy)
- [Live Preview](https://github.com/payloadcms/payload/tree/main/examples/live-preview)
- [Multi-tenant](https://github.com/payloadcms/payload/tree/main/examples/multi-tenant)
- [Nested Docs](https://github.com/payloadcms/payload/tree/main/examples/nested-docs)
- [Redirects](https://github.com/payloadcms/payload/tree/main/examples/redirects)
- [Tailwind / Shadcn-ui](https://github.com/payloadcms/payload/tree/main/examples/tailwind-shadcn-ui)
- [Tests](https://github.com/payloadcms/payload/tree/main/examples/testing)
- [Virtual Fields](https://github.com/payloadcms/payload/tree/main/examples/virtual-fields)
- [White-label Admin UI](https://github.com/payloadcms/payload/tree/main/examples/whitelabel)
When necessary, some examples include a front-end. Examples that require a front-end share this folder structure:
```plaintext
example/
├── payload/
├── next-app/
├── next-pages/
├── react-router/
├── vue/
├── svelte/
```
Where `payload` is your Payload project, and the other directories are dedicated to their respective front-end framework. We are adding new examples every day, so if your framework of choice is not yet supported in any particular example, please feel free to start a new [Discussion](https://github.com/payloadcms/payload/discussions) or open a new [PR](https://github.com/payloadcms/payload/pulls) to add it yourself.
We are adding new examples every day, so if your particular use case is not demonstrated in any existing example, please feel free to start a new [Discussion](https://github.com/payloadcms/payload/discussions) or open a new [PR](https://github.com/payloadcms/payload/pulls) to add it yourself.

View File

@@ -6,7 +6,7 @@ desc: Array Fields are intended for sets of repeating fields, that you define. L
keywords: array, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
The Array Field is used when you need to have a set of "repeating" [Fields](./overview). It stores an array of objects containing fields that you define. These fields can be of any type, including other arrays to achieve infinitely nested structures.
The Array Field is used when you need to have a set of "repeating" [Fields](./overview). It stores an array of objects containing fields that you define. These fields can be of any type, including other arrays, to achieve infinitely nested data structures.
Arrays are useful for many different types of content from simple to complex, such as:

View File

@@ -84,6 +84,51 @@ The Blocks Field inherits all of the default options from the base [Field Admin
| **`initCollapsed`** | Set the initial collapsed state |
| **`isSortable`** | Disable order sorting by setting this value to `false` |
#### Customizing the way your block is rendered in Lexical
If you're using this block within the [Lexical editor](/docs/lexical/overview), you can also customize how the block is rendered in the Lexical editor itself by specifying custom components.
- `admin.components.Label` - pass a custom React component here to customize the way that the label is rendered for this block
- `admin.components.Block` - pass a component here to completely override the way the block is rendered in Lexical with your own component
This is super handy if you'd like to present your editors with a very deliberate and nicely designed block "preview" right in your rich text.
For example, if you have a `gallery` block, you might want to actually render the gallery of images directly in your Lexical block. With the `admin.components.Block` property, you can do exactly that!
<Banner type="success">
<strong>Tip:</strong><br/>
If you customize the way your block is rendered in Lexical, you can import utility components to easily edit / remove your block - so that you don't have to build all of this yourself.
</Banner>
To import these utility components for one of your custom blocks, you can import the following:
```ts
import {
// Edit block buttons (choose the one that corresponds to your usage)
// When clicked, this will open a drawer with your block's fields
// so your editors can edit them
InlineBlockEditButton,
BlockEditButton,
// Buttons that will remove this block from Lexical
// (choose the one that corresponds to your usage)
InlineBlockRemoveButton,
BlockRemoveButton,
// The label that should be rendered for an inline block
InlineBlockLabel,
// The default "container" that is rendered for an inline block
// if you want to re-use it
InlineBlockContainer,
// The default "collapsible" UI that is rendered for a regular block
// if you want to re-use it
BlockCollapsible,
} from '@payloadcms/richtext-lexical/client'
```
## Block Configs
Blocks are defined as separate configs of their own.

View File

@@ -86,7 +86,7 @@ _\* This property is passed directly to [react-datepicker](https://github.com/Ha
These properties only affect how the date is displayed in the UI. The full date is always stored in the format `YYYY-MM-DDTHH:mm:ss.SSSZ` (e.g. `1999-01-01T8:00:00.000+05:00`).
`displayFormat` determines how the date is presented in the field **cell**, you can pass any valid (unicode date format)[https://date-fns.org/v2.29.3/docs/format].
`displayFormat` determines how the date is presented in the field **cell**, you can pass any valid (unicode date format)[https://date-fns.org/v4.1.0/docs/format].
`pickerAppearance` sets the appearance of the **react datepicker**, the options available are `dayAndTime`, `dayOnly`, `timeOnly`, and `monthOnly`. By default, the datepicker will display `dayOnly`.

View File

@@ -126,12 +126,13 @@ powerful Admin UI.
| **`name`** \* | To be used as the property name when retrieved from the database. [More](/docs/fields/overview#field-names) |
| **`collection`** \* | The `slug`s having the relationship field. |
| **`on`** \* | The name of the relationship or upload field that relates to the collection document. Use dot notation for nested paths, like 'myGroup.relationName'. |
| **`where`** \* | A `Where` query to hide related documents from appearing. Will be merged with any `where` specified in the request. |
| **`maxDepth`** | Default is 1, Sets a maximum population depth for this field, regardless of the remaining depth when this field is reached. [Max Depth](/docs/getting-started/concepts#field-level-max-depth). |
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
| **`defaultLimit`** | The number of documents to return. Set to 0 to return all related documents. |
| **`defaultSort`** | The field name used to specify the order the joined documents are returned. |
| **`defaultLimit`** | The number of documents to return. Set to 0 to return all related documents. |
| **`defaultSort`** | The field name used to specify the order the joined documents are returned. |
| **`admin`** | Admin-specific configuration. [More details](#admin-config-options). |
| **`custom`** | Extension point for adding custom data (e.g. for plugins). |
| **`typescriptSchema`** | Override field type generation with providing a JSON schema. |
@@ -146,7 +147,7 @@ You can control the user experience of the join field using the `admin` config p
| Option | Description |
|------------------------|----------------------------------------------------------------------------------------|
| **`allowCreate`** | Set to `false` to remove the controls for making new related documents from this field. |
| **`components.Label`** | Override the default Label of the Field Component. [More details](#the-label-component). |
| **`components.Label`** | Override the default Label of the Field Component. [More details](../admin/fields#label) |
## Join Field Data
@@ -182,11 +183,11 @@ returning. This is useful for performance reasons when you don't need the relate
The following query options are supported:
| Property | Description |
|-------------|--------------------------------------------------------------|
| **`limit`** | The maximum related documents to be returned, default is 10. |
| **`where`** | An optional `Where` query to filter joined documents. |
| **`sort`** | A string used to order related results |
| Property | Description |
|-------------|-----------------------------------------------------------------------------------------------------|
| **`limit`** | The maximum related documents to be returned, default is 10. |
| **`where`** | An optional `Where` query to filter joined documents. Will be merged with the field `where` object. |
| **`sort`** | A string used to order related results |
These can be applied to the local API, GraphQL, and REST API.

View File

@@ -7,7 +7,7 @@ desc: The JSON field type will store any string in the Database. Learn how to us
keywords: json, jsonSchema, schema, validation, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
The JSON Field saves actual JSON in the database, which differs from the Code field that saves the value as a string in the database.
The JSON Field saves raw JSON to the database and provides the [Admin Panel](../admin/overview) with a code editor styled interface. This is different from the [Code Field](./code) which saves the value as a string in the database.
<LightDarkImage
srcLight="https://payloadcms.com/images/docs/fields/json.png"

View File

@@ -93,13 +93,14 @@ Presentational Fields do not store data in the database. Instead, they are used
Here are the available Presentational Fields:
- [Collapsible](/docs/fields/collapsible) - nests fields within a collapsible component
- [Join](/docs/fields/join) - achieves two-way data binding between fields
- [Row](/docs/fields/row) - aligns fields horizontally
- [Tabs (Unnamed)](/docs/fields/tabs) - nests fields within a tabbed layout
- [UI](/docs/fields/ui) - blank field for custom UI components
<Banner type="warning">
<strong>Tip:</strong>
Don't see a Field Type that fits your needs? You can build your own using a [Custom Field Component](../admin/fields#the-field-component).
Don't see a Field Type that fits your needs? You can build your own using a [Custom Field Component](../admin/fields#field).
</Banner>
## Field Options
@@ -123,7 +124,7 @@ export const MyField: Field = {
### Field Names
All [Data Fields](#data-fields) require a `name` property. This is the key that will be used to store and retrieve the field's value in the database. This property must be unique within the Collection, Global, or nested group that it is defined in.
All [Data Fields](#data-fields) require a `name` property. This is the key that will be used to store and retrieve the field's value in the database. This property must be unique amongst this field's siblings.
To set a field's name, use the `name` property in your Field Config:
@@ -205,7 +206,7 @@ export const MyField: Field = {
}
```
Default values can be defined as a static value or a function that returns a value. When a `defaultValue` is defined statically, Payload's DB adapters will apply it to the database schema or models.
Default values can be defined as a static value or a function that returns a value. When a `defaultValue` is defined statically, Payload's [Database Adapters](../database/overview) will apply it to the database schema or models.
Functions can be written to make use of the following argument properties:
@@ -264,7 +265,7 @@ The following arguments are provided to the `validate` function:
#### Validation Context
The `ctx` argument contains full document data, sibling field data, the current operation, and other useful information such as currently authenticated in user:
The `ctx` argument contains full document data, sibling field data, the current operation, and other useful information such as currently authenticated user:
```ts
import type { Field } from 'payload'
@@ -356,9 +357,9 @@ For full details on Admin Options, see the [Field Admin Options](../admin/fields
## Custom ID Fields
All [Collections](../configuration/collections) automatically generate their own ID field. If needed, you can override this behavior by providing an explicit ID field to your config. This will force users to provide a their own ID value when creating a record.
All [Collections](../configuration/collections) automatically generate their own ID field. If needed, you can override this behavior by providing an explicit ID field to your config. This field should either be required or have a hook to generate the ID dynamically.
To define a custom ID field, add a new field with the `name` property set to `id`:
To define a custom ID field, add a top-level field with the `name` property set to `id`:
```ts
import type { CollectionConfig } from 'payload'
@@ -368,6 +369,7 @@ export const MyCollection: CollectionConfig = {
fields: [
{
name: 'id', // highlight-line
required: true,
type: 'number',
},
],

View File

@@ -28,7 +28,7 @@ export const MyPointField: Field = {
<Banner type="warning">
<strong>Important:</strong>
The Point Field is currently only supported in MongoDB.
The Point Field currently is not supported in SQLite.
</Banner>
## Config
@@ -73,6 +73,59 @@ export const ExampleCollection: CollectionConfig = {
}
```
## Querying
## Querying - near
In order to do query based on the distance to another point, you can use the `near` operator. When querying using the near operator, the returned documents will be sorted by nearest first.
## Querying - within
In order to do query based on whether points are within a specific area defined in GeoJSON, you can use the `within` operator.
Example:
```ts
const polygon: Point[] = [
[9.0, 19.0], // bottom-left
[9.0, 21.0], // top-left
[11.0, 21.0], // top-right
[11.0, 19.0], // bottom-right
[9.0, 19.0], // back to starting point to close the polygon
]
payload.find({
collection: "points",
where: {
point: {
within: {
type: 'Polygon',
coordinates: [polygon],
},
},
},
})
```
## Querying - intersects
In order to do query based on whether points intersect a specific area defined in GeoJSON, you can use the `intersects` operator.
Example:
```ts
const polygon: Point[] = [
[9.0, 19.0], // bottom-left
[9.0, 21.0], // top-left
[11.0, 21.0], // top-right
[11.0, 19.0], // bottom-right
[9.0, 19.0], // back to starting point to close the polygon
]
payload.find({
collection: "points",
where: {
point: {
intersects: {
type: 'Polygon',
coordinates: [polygon],
},
},
},
})
```

View File

@@ -19,8 +19,8 @@ Payload's rich text field is built on an "adapter pattern" which lets you specif
Right now, Payload is officially supporting two rich text editors:
1. [SlateJS](/docs/rich-text/slate) - stable, backwards-compatible with 1.0
2. [Lexical](/docs/lexical/overview) - beta, where things will be moving
1. [SlateJS](/docs/rich-text/slate) - legacy, backwards-compatible with 1.0
2. [Lexical](/docs/lexical/overview) - recommended
<Banner type="success">
<strong>

View File

@@ -204,4 +204,4 @@ If you are looking to create a dynamic select field, the following tutorial will
drawerTitle="How to Create a Custom Select Field: A Step-by-Step Guide"
/>
If you want to learn more about custom components check out the [Admin > Custom Component](/docs/admin/components#field-component) docs.
If you want to learn more about custom components check out the [Admin > Custom Component](/docs/admin/components#field) docs.

View File

@@ -32,8 +32,8 @@ export const MyUIField: Field = {
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| **`name`** \* | A unique identifier for this field. |
| **`label`** | Human-readable label for this UI field. |
| **`admin.components.Field`** \* | React component to be rendered for this field within the Edit View. [More](../admin/components/#field-component) |
| **`admin.components.Cell`** | React component to be rendered as a Cell within collection List views. [More](../admin/components/#field-component) |
| **`admin.components.Field`** \* | React component to be rendered for this field within the Edit View. [More](../admin/components/#field) |
| **`admin.components.Cell`** | React component to be rendered as a Cell within collection List views. [More](../admin/components/#field) |
| **`admin.disableListColumn`** | Set `disableListColumn` to `true` to prevent the UI field from appearing in the list view column selector. |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |

View File

@@ -108,7 +108,7 @@ called with an argument object with the following properties:
| `id` | The `id` of the current document being edited. `id` is `undefined` during the `create` operation |
| `user` | An object containing the currently authenticated user |
## Example
### Example#filter-options-example
```ts
const uploadField = {

View File

@@ -34,7 +34,7 @@ Hooks allow you to execute your own side effects during specific events of the D
## Authentication
Payload provides a secure, portable way to manage user accounts out of the box. Payload Authentication is designed to be used in both the Admin Panel, all well as your own external applications. [More details](../authentication/overview).
Payload provides a secure, portable way to manage user accounts out of the box. Payload Authentication is designed to be used in both the Admin Panel, as well as your own external applications. [More details](../authentication/overview).
## Access Control
@@ -68,15 +68,10 @@ Here's a quick example of a React Server Component fetching data using the Local
```tsx
import React from 'react'
import config from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities'
import { getPayload } from 'payload'
const MyServerComponent: React.FC = () => {
// If you're working in Next.js, and you want HMR,
// you should get Payload via the `getPayloadHMR` function.
const payload = await getPayloadHMR({ config })
// If you are writing a standalone script and do not need HMR,
// you can get Payload via import { getPayload } from 'payload' instead.
const payload = await getPayload({ config })
// The `findResult` here will be fully typed as `PaginatedDocs<Page>`,
// where you will have the `docs` that are returned as well as
@@ -156,7 +151,7 @@ Whereas Payload itself is responsible for direct database access, and control ov
`@payloadcms/graphql`
All of Payload's GraphQL functionality is abstracted into a separate package. Payload, its Admin UI, and REST API have absolutely no overlap with GraphQL, and you will incur no performance overhead from GraphQL if you are not using it. However, it's installed within in the `@payloadcms/next` package so you don't have to install it manually. You do, however, need to have GraphQL installed separately in your `package.json` if you are using GraphQL.
All of Payload's GraphQL functionality is abstracted into a separate package. Payload, its Admin UI, and REST API have absolutely no overlap with GraphQL, and you will incur no performance overhead from GraphQL if you are not using it. However, it's installed within the `@payloadcms/next` package so you don't have to install it manually. You do, however, need to have GraphQL installed separately in your `package.json` if you are using GraphQL.
`@payloadcms/ui`

View File

@@ -10,9 +10,9 @@ keywords: documentation, getting started, guide, Content Management System, cms,
Payload requires the following software:
- Any JavaScript package manager (Yarn, NPM, or pnpm - pnpm is preferred)
- Any JavaScript package manager (pnpm, npm, or yarn - pnpm is preferred)
- Node.js version 20.9.0+
- Any [compatible database](/docs/database/overview) (MongoDB or Postgres)
- Any [compatible database](/docs/database/overview) (MongoDB, Postgres or SQLite)
<Banner type="warning">
<strong>Important:</strong>
@@ -24,28 +24,32 @@ Payload requires the following software:
To quickly scaffold a new Payload app in the fastest way possible, you can use [create-payload-app](https://npmjs.com/package/create-payload-app). To do so, run the following command:
```
npx create-payload-app@beta
npx create-payload-app
```
Then just follow the prompts! You'll get set up with a new folder and a functioning Payload app inside. You can then start [configuring your application](../configuration/overview).
## Adding to an existing app
Adding Payload to an existing Next.js app is super straightforward. You can either run the `npx create-payload-app@beta` command inside your Next.js project's folder, or manually install Payload by following the steps below.
Adding Payload to an existing Next.js app is super straightforward. You can either run the `npx create-payload-app` command inside your Next.js project's folder, or manually install Payload by following the steps below.
If you don't have a Next.js app already, but you still want to start a project from a blank Next.js app, you can create a new Next.js app using `npx create-next-app` - and then just follow the steps below to install Payload.
<Banner type="info">
<strong>Note:</strong> Next.js version 15 or higher is required for Payload.
</Banner>
#### 1. Install the relevant packages
First, you'll want to add the required Payload packages to your project and can do so by running the command below:
```bash
pnpm i payload@beta @payloadcms/next@beta @payloadcms/richtext-lexical@beta sharp graphql
pnpm i payload @payloadcms/next @payloadcms/richtext-lexical sharp graphql
```
<Banner type="warning">
<strong>Note:</strong>
Swap out `pnpm` for your package manager. If you are using NPM, you might need to install using legacy peer deps: `npm i --legacy-peer-deps`.
Swap out `pnpm` for your package manager. If you are using npm, you might need to install using legacy peer deps: `npm i --legacy-peer-deps`.
</Banner>
Next, install a [Database Adapter](/docs/database/overview). Payload requires a Database Adapter to establish a database connection. Payload works with all types of databases, but the most common are MongoDB and Postgres.
@@ -54,12 +58,12 @@ To install a Database Adapter, you can run **one** of the following commands:
- To install the [MongoDB Adapter](../database/mongodb), run:
```bash
pnpm i @payloadcms/db-mongodb@beta
pnpm i @payloadcms/db-mongodb
```
- To install the [Postgres Adapter](../database/postgres), run:
```bash
pnpm i @payloadcms/db-postgres@beta
pnpm i @payloadcms/db-postgres
```
<Banner type="success">
@@ -69,7 +73,7 @@ To install a Database Adapter, you can run **one** of the following commands:
#### 2. Copy Payload files into your Next.js app folder
Payload installs directly in your Next.js `/app` folder, and you'll need to place some files into that folder for Payload to run. You can copy these files from the [Blank Template](https://github.com/payloadcms/payload/tree/beta/templates/blank/src/app/(payload)) on GitHub. Once you have the required Payload files in place in your `/app` folder, you should have something like this:
Payload installs directly in your Next.js `/app` folder, and you'll need to place some files into that folder for Payload to run. You can copy these files from the [Blank Template](https://github.com/payloadcms/payload/tree/main/templates/blank/src/app/(payload)) on GitHub. Once you have the required Payload files in place in your `/app` folder, you should have something like this:
```plaintext
app/
@@ -177,6 +181,6 @@ Once you have a Payload Config, update your `tsconfig` to include a `path` that
#### 5. Fire it up!
After you've gotten this far, it's time to boot up Payload. Start your project in your application's folder to get going. By default, the Next.js dev script is `pnpm dev` (or `npm run dev` if using NPM).
After you've reached this point, it's time to boot up Payload. Start your project in your application's folder to get going. By default, the Next.js dev script is `pnpm dev` (or `npm run dev` if using npm).
After it starts, you can go to `http://localhost:3000/admin` to create your first Payload user!

View File

@@ -3,7 +3,7 @@ title: What is Payload?
label: What is Payload?
order: 10
desc: Payload is a next-gen application framework that can be used as a Content Management System, enterprise tool framework, headless commerce platform, or digital asset management tool.
keywords: documentation, getting started, guide, Content Management System, cms, headless, javascript, node, react, express
keywords: documentation, getting started, guide, Content Management System, cms, headless, javascript, node, react
---
<YouTube
@@ -24,7 +24,7 @@ keywords: documentation, getting started, guide, Content Management System, cms,
### Instant backend superpowers
No matter what you're building, Payload will give you backend superpowers. It can be installed in one line into any existing Next.js app, and is designed to catapult your development process. Payload takes the most complex and time-consuming parts of any modern web app and makes them simple.
No matter what you're building, Payload will give you backend superpowers. Your entire Payload config can be installed in one line into any existing Next.js app, and is designed to catapult your development process. Payload takes the most complex and time-consuming parts of any modern web app and makes them simple.
### Open source - deploy anywhere, including Vercel
@@ -95,12 +95,6 @@ Payload can integrate with any payment processor like Stripe and its content aut
If you can build your storefront with a single backend, and only offload things like payment processing, the code will be simpler and the editing experience will be significantly streamlined. Manage products, catalogs, page content, media, and more—all in one spot.
Payload's official Ecommerce template gives you everything you need for a storefront out of the box, including a Next.js frontend, product variations, and a full Stripe implementation:
```
npx create-payload-app@latest -t ecommerce
```
### Digital Asset Management
Payload's API-first tagging, sorting, and querying engine lends itself perfectly to all types of content that a CMS might ordinarily store, but these strong fundamentals also make it a formidable Digital Asset Management (DAM) tool as well.

View File

@@ -13,7 +13,7 @@ In Payload the schema is controlled by your collections and globals. All you nee
Install `@payloadcms/graphql` as a dev dependency:
```bash
pnpm add @payloadcms/graphql@beta -D
pnpm add @payloadcms/graphql -D
```
Run the following command to generate the schema:
@@ -62,7 +62,7 @@ type Collection1 {
The above example outputs all your definitions to a file relative from your payload config as `./graphql/schema.graphql`. By default, the file will be output to your current working directory as `schema.graphql`.
### Adding an NPM script
### Adding an npm script
<Banner type="warning">
<strong>Important</strong>
@@ -72,7 +72,7 @@ The above example outputs all your definitions to a file relative from your payl
Payload will automatically try and locate your config, but might not always be able to find it. For example, if you are working in a `/src` directory or similar, you need to tell Payload where to find your config manually by using an environment variable.
If this applies to you, create an NPM script to make generating types easier:
If this applies to you, create an npm script to make generating types easier:
```json
// package.json

View File

@@ -28,6 +28,8 @@ To pass data between hooks, you can assign values to context in an earlier hook
For example:
```ts
import type { CollectionConfig } from 'payload'
const Customer: CollectionConfig = {
slug: 'customers',
hooks: {
@@ -43,7 +45,6 @@ const Customer: CollectionConfig = {
},
],
afterChange: [
async ({ context, doc, req }) => {
// use context.customerData without needing to fetch it again
if (context.customerData.contacted === false) {
@@ -65,6 +66,8 @@ Let's say you have an `afterChange` hook, and you want to do a calculation insid
Bad example:
```ts
import type { CollectionConfig } from 'payload'
const Customer: CollectionConfig = {
slug: 'customers',
hooks: {
@@ -92,6 +95,8 @@ Instead of the above, we need to tell the `afterChange` hook to not run again if
Fixed example:
```ts
import type { CollectionConfig } from 'payload'
const MyCollection: CollectionConfig = {
slug: 'slug',
hooks: {
@@ -125,7 +130,7 @@ const MyCollection: CollectionConfig = {
The default TypeScript interface for `context` is `{ [key: string]: unknown }`. If you prefer a more strict typing in your project or when authoring plugins for others, you can override this using the `declare` syntax.
This is known as "type augmentation", a TypeScript feature which allows us to add types to existing objects. Simply put this in any `.ts` or `.d.ts` file:
This is known as "type augmentation", a TypeScript feature which allows us to add types to existing types. Simply put this in any `.ts` or `.d.ts` file:
```ts
import { RequestContext as OriginalRequestContext } from 'payload'

View File

@@ -37,7 +37,7 @@ Root Hooks are not associated with any specific Collection, Global, or Field. Th
To add Root Hooks, use the `hooks` property in your [Payload Config](/docs/configuration/config):
```ts
import { buildConfig } from 'payload'
import { buildConfig } from 'payload'
export default buildConfig({
// ...
@@ -60,7 +60,7 @@ The following options are available:
The `afterError` Hook is triggered when an error occurs in the Payload application. This can be useful for logging errors to a third-party service, sending an email to the development team, logging the error to Sentry or DataDog, etc. The output can be used to transform the result object / status code.
```ts
import { buildConfig } from 'payload'
import { buildConfig } from 'payload'
export default buildConfig({
// ...

46
docs/jobs-queue/jobs.mdx Normal file
View File

@@ -0,0 +1,46 @@
---
title: Jobs
label: Jobs
order: 40
desc: A Job is a set of work that is offloaded from your APIs and will be processed at a later date.
keywords: jobs queue, application framework, typescript, node, react, nextjs
---
Now that we have covered Tasks and Workflows, we can tie them together with a concept called a Job.
<Banner type="default">
Whereas you define Workflows and Tasks, which control your business logic, a <strong>Job</strong> is an individual instance of either a Task or a Workflow which contains many tasks.
</Banner>
For example, let's say we have a Workflow or Task that describes the logic to sync information from Payload to a third-party system. This is how you'd declare how to sync that info, but it wouldn't do anything on its own. In order to run that task or workflow, you'd create a Job that references the corresponding Task or Workflow.
Jobs are stored in the Payload database in the `payload-jobs` collection, and you can decide to keep a running list of all jobs, or configure Payload to delete the job when it has been successfully executed.
#### Queuing a new job
In order to queue a job, you can use the `payload.jobs.queue` function.
Here's how you'd queue a new Job, which will run a `createPostAndUpdate` workflow:
```ts
const createdJob = await payload.jobs.queue({
// Pass the name of the workflow
workflow: 'createPostAndUpdate',
// The input type will be automatically typed
// according to the input you've defined for this workflow
input: {
title: 'my title',
},
})
```
In addition to being able to queue new Jobs based on Workflows, you can also queue a job for a single Task:
```ts
const createdJob = await payload.jobs.queue({
task: 'createPost',
input: {
title: 'my title',
},
})
```

View File

@@ -1,382 +1,70 @@
---
title: Jobs Queue
label: Jobs Queue
label: Overview
order: 10
desc: Payload provides all you need to run job queues, which are helpful to offload long-running processes into separate workers.
keywords: jobs queue, application framework, typescript, node, react, nextjs
---
## Defining tasks
Payload's Jobs Queue gives you a simple, yet powerful way to offload large or future tasks to separate compute resources which is a very powerful feature of many application frameworks.
A task is a simple function that can be executed directly or within a workflow. The difference between tasks and functions is that tasks can be run in the background, and can be retried if they fail.
### Example use cases
Tasks can either be defined within the `jobs.tasks` array in your payload config, or they can be run inline within a workflow.
**Non-blocking workloads**
### Defining tasks in the config
You might need to perform some complex, slow-running logic in a Payload [Hook](/docs/hooks/overview) but you don't want that hook to "block" or slow down the response returned from the Payload API. Instead of running this logic directly in a hook, which would block your API response from returning until the expensive work is completed, you can queue a new Job and let it run at a later date.
Simply add a task to the `jobs.tasks` array in your Payload config. A task consists of the following fields:
Examples:
| Option | Description |
| --------------------------- | -------------------------------------------------------------------------------- |
| `slug` | Define a slug-based name for this job. This slug needs to be unique among both tasks and workflows.|
| `handler` | The function that should be responsible for running the job. You can either pass a string-based path to the job function file, or the job function itself. If you are using large dependencies within your job, you might prefer to pass the string path because that will avoid bundling large dependencies in your Next.js app. |
| `inputSchema` | Define the input field schema - payload will generate a type for this schema. |
| `interfaceName` | You can use interfaceName to change the name of the interface that is generated for this task. By default, this is "Task" + the capitalized task slug. |
| `outputSchema` | Define the output field schema - payload will generate a type for this schema. |
| `label` | Define a human-friendly label for this task. |
| `onFail` | Function to be executed if the task fails. |
| `onSuccess` | Function to be executed if the task fails. |
| `retries` | Specify the number of times that this step should be retried if it fails. |
- Create vector embeddings from your documents, and keep them in sync as your documents change
- Send data to a third-party API on document change
- Trigger emails based on customer actions
The handler is the function, or a path to the function, that will run once the job picks up this task. The handler function should return an object with an `output` key, which should contain the output of the task.
**Scheduled actions**
Example:
If you need to schedule an action to be run or processed at a certain date in the future, you can queue a job with the `waitUntil` property set. This will make it so the job is not "picked up" until that `waitUntil` date has passed.
```ts
export default buildConfig({
// ...
jobs: {
tasks: [
{
retries: 2,
slug: 'createPost',
inputSchema: [
{
name: 'title',
type: 'text',
required: true,
},
],
outputSchema: [
{
name: 'postID',
type: 'text',
required: true,
},
],
handler: async ({ input, job, req }) => {
const newPost = await req.payload.create({
collection: 'post',
req,
data: {
title: input.title,
},
})
return {
output: {
postID: newPost.id,
},
}
},
} as TaskConfig<'createPost'>,
]
}
})
```
Examples:
### Example: defining external tasks
- Process scheduled posts, where the scheduled date is at a time set in the future
- Unpublish posts at a given time
- Send a reminder email to a customer after X days of signing up for a trial
payload.config.ts:
**Periodic sync or similar scheduled action**
```ts
import { fileURLToPath } from 'node:url'
import path from 'path'
Some applications may need to perform a regularly scheduled operation of some type. Jobs are perfect for this because you can execute their logic using `cron`, scheduled nightly, every twelve hours, or some similar time period.
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
Examples:
export default buildConfig({
// ...
jobs: {
tasks: [
{
retries: 2,
slug: 'createPost',
inputSchema: [
{
name: 'title',
type: 'text',
required: true,
},
],
outputSchema: [
{
name: 'postID',
type: 'text',
required: true,
},
],
handler: path.resolve(dirname, 'src/tasks/createPost.ts') + '#createPostHandler',
}
]
}
})
```
- You'd like to send emails to all customers on a regular, scheduled basis
- Periodically trigger a rebuild of your frontend at night
- Sync resources to or from a third-party API during non-peak times
src/tasks/createPost.ts:
**Offloading complex operations**
```ts
import type { TaskHandler } from 'payload'
You may run into the need to perform computationally expensive functions which might slow down your main Payload API server(s). The Jobs Queue allows you to offload these tasks to a separate compute resource rather than slowing down the server(s) that run your Payload APIs. With Payload Task definitions, you can even keep large dependencies out of your main Next.js bundle by dynamically importing them only when they are used. This keeps your Next.js + Payload compilation fast and ensures large dependencies do not get bundled into your Payload production build.
export const createPostHandler: TaskHandler<'createPost'> = async ({ input, job, req }) => {
const newPost = await req.payload.create({
collection: 'post',
req,
data: {
title: input.title,
},
})
return {
output: {
postID: newPost.id,
},
}
}
```
Examples:
- You need to create (and then keep in sync) vector embeddings of your documents as they change, but you use an open source model to generate embeddings
- You have a PDF generator that needs to dynamically build and send PDF versions of documents to customers
- You need to use a headless browser to perform some type of logic
- You need to perform a series of actions, each of which depends on a prior action and should be run in as "durable" of a fashion as possible
## Defining workflows
### How it works
There are two types of workflows - JS-based workflows and JSON-based workflows.
There are a few concepts that you should become familiarized with before using Payload's Jobs Queue. We recommend learning what each of these does in order to fully understand how to leverage the power of Payload's Jobs Queue.
### Defining JS-based workflows
1. [Tasks](/docs/jobs-queue/tasks)
1. [Workflows](/docs/jobs-queue/workflows)
1. [Jobs](/docs/jobs-queue/jobs)
1. [Queues](/docs/jobs-queue/queues)
A JS-based function is a function in which you decide yourself when the tasks should run, by simply calling the `runTask` function. If the job, or any task within the job, fails, the entire function will re-run.
All of these pieces work together in order to allow you to offload long-running, expensive, or future scheduled work from your main APIs.
Tasks that have successfully been completed will simply re-return the cached output without running again, and failed tasks will be re-run.
Here's a quick overview:
Simply add a workflow to the `jobs.wokflows` array in your Payload config. A wokflow consists of the following fields:
| Option | Description |
| --------------------------- | -------------------------------------------------------------------------------- |
| `slug` | Define a slug-based name for this workflow. This slug needs to be unique among both tasks and workflows.|
| `handler` | The function that should be responsible for running the workflow. You can either pass a string-based path to the workflow function file, or workflow job function itself. If you are using large dependencies within your workflow, you might prefer to pass the string path because that will avoid bundling large dependencies in your Next.js app. |
| `inputSchema` | Define the input field schema - payload will generate a type for this schema. |
| `interfaceName` | You can use interfaceName to change the name of the interface that is generated for this workflow. By default, this is "Workflow" + the capitalized workflow slug. |
| `label` | Define a human-friendly label for this workflow. |
| `queue` | Optionally, define the queue name that this workflow should be tied to. Defaults to "default". |
Example:
```ts
export default buildConfig({
// ...
jobs: {
tasks: [
// ...
]
workflows: [
{
slug: 'createPostAndUpdate',
inputSchema: [
{
name: 'title',
type: 'text',
required: true,
},
],
handler: async ({ job, runTask }) => {
const output = await runTask({
task: 'createPost',
id: '1',
input: {
title: job.input.title,
},
})
await runTask({
task: 'updatePost',
id: '2',
input: {
post: job.taskStatus.createPost['1'].output.postID, // or output.postID
title: job.input.title + '2',
},
})
},
} as WorkflowConfig<'updatePost'>
]
}
})
```
#### Running tasks inline
In order to run tasks inline without predefining them, you can use the `runTaskInline` function.
The drawbacks of this approach are that tasks cannot be re-used as easily, and the **task data stored in the job** will not be typed. In the following example, the inline task data will be stored on the job under `job.taskStatus.inline['2']` but completely untyped, as types for dynamic tasks like these cannot be generated beforehand.
Example:
```ts
export default buildConfig({
// ...
jobs: {
tasks: [
// ...
]
workflows: [
{
slug: 'createPostAndUpdate',
inputSchema: [
{
name: 'title',
type: 'text',
required: true,
},
],
handler: async ({ job, runTask }) => {
const output = await runTask({
task: 'createPost',
id: '1',
input: {
title: job.input.title,
},
})
const { newPost } = await runTaskInline({
task: async ({ req }) => {
const newPost = await req.payload.update({
collection: 'post',
id: output.postID,
req,
retries: 3,
data: {
title: 'updated!',
},
})
return {
output: {
newPost
},
}
},
id: '2',
})
},
} as WorkflowConfig<'updatePost'>
]
}
})
```
### Defining JSON-based workflows
JSON-based workflows are a way to define the tasks the workflow should run in an array. The relationships between the tasks, their run order and their conditions are defined in the JSON object, which allows payload to statically analyze the workflow and will generate more helpful graphs.
This functionality is not available yet, but it will be available in the future.
## Queueing workflows and tasks
In order to queue a workflow or a task (= create them and add them to the queue), you can use the `payload.jobs.queue` function.
Example: queueing workflows:
```ts
const createdJob = await payload.jobs.queue({
workflows: 'createPostAndUpdate',
input: {
title: 'my title',
},
})
```
Example: queueing tasks:
```ts
const createdJob = await payload.jobs.queue({
task: 'createPost',
input: {
title: 'my title',
},
})
```
## Running workflows and tasks
Workflows and tasks added to the queue will not run unless a worker picks it up and runs it. This can be done in two ways:
### Endpoint
Make a fetch request to the `api/payload-jobs/run` endpoint:
```ts
await fetch('/api/payload-jobs/run', {
method: 'GET',
headers: {
'Authorization': `JWT ${token}`,
},
});
```
### Local API
Run the payload.jobs.run function:
```ts
const results = await payload.jobs.run()
// You can customize the queue name by passing it as an argument
await payload.jobs.run({ queue: 'posts' })
```
### Script
You can run the jobs:run script from the command line:
```sh
npx payload jobs:run --queue default --limit 10
```
#### Triggering jobs as cronjob
You can pass the --cron flag to the jobs:run script to run the jobs in a cronjob:
```sh
npx payload jobs:run --cron "*/5 * * * *"
```
### Vercel Cron
Vercel Cron allows scheduled tasks to be executed automatically by triggering specific endpoints. Below is a step-by-step guide to configuring Vercel Cron for running queued jobs on apps hosted on Vercel:
1. Add Vercel Cron Configuration: Place a vercel.json file at the root of your project with the following content:
```json
{
"crons": [
{
"path": "/api/payload-jobs/run",
"schedule": "*/5 * * * *"
}
]
}
```
This configuration schedules the endpoint `/api/payload-jobs/run` to be triggered every 5 minutes. This endpoint is added automatically by payload and is responsible for running the queued jobs.
2. Environment Variable Setup: By default, the endpoint may require a JWT token for authorization. However, Vercel Cron jobs cannot pass JWT tokens. Instead, you can use an environment variable to secure the endpoint:
Add a new environment variable named `CRON_SECRET` to your Vercel project settings. This should be a random string, ideally 16 characters or longer.
3. Modify Authentication for Job Running: Adjust the job running authorization logic in your project to accept the `CRON_SECRET` as a valid token. Modify your `payload.config.ts` file as follows:
```ts
export default buildConfig({
// Other configurations...
jobs: {
access: {
run: ({ req }: { req: PayloadRequest }): boolean => {
const authHeader = req.headers.get('authorization');
return authHeader === `Bearer ${process.env.CRON_SECRET}`;
},
},
// Other job configurations...
}
})
```
This code snippet ensures that the jobs can only be triggered if the correct `CRON_SECRET` is provided in the authorization header.
Vercel will automatically make the `CRON_SECRET` environment variable available to the endpoint when triggered by the Vercel Cron, ensuring that the jobs can be run securely.
After the project is deployed to Vercel, the Vercel Cron job will automatically trigger the `/api/payload-jobs/run` endpoint in the specified schedule, running the queued jobs in the background.
- A Task is a specific function that performs business logic
- Workflows are groupings of specific tasks which should be run in-order, and can be retried from a specific point of failure
- A Job is an instance of a single task or workflow which will be executed
- A Queue is a way to segment your jobs into different "groups" - for example, some to run nightly, and others to run every 10 minutes

133
docs/jobs-queue/queues.mdx Normal file
View File

@@ -0,0 +1,133 @@
---
title: Queues
label: Queues
order: 50
desc: A Queue is a specific group of jobs which can be executed in the order that they were added.
keywords: jobs queue, application framework, typescript, node, react, nextjs
---
Queues are the final aspect of Payload's Jobs Queue and deal with how to _run your jobs_. Up to this point, all we've covered is how to queue up jobs to run, but so far, we aren't actually running any jobs.
<Banner type="default">
A <strong>Queue</strong> is a grouping of jobs that should be executed in order of when they were added.
</Banner>
When you go to run jobs, Payload will query for any jobs that are added to the queue and then run them. By default, all queued jobs are added to the `default` queue.
**But, imagine if you wanted to have some jobs that run nightly, and other jobs which should run every five minutes.**
By specifying the `queue` name when you queue a new job using `payload.jobs.queue()`, you can queue certain jobs with `queue: 'nightly'`, and other jobs can be left as the default queue.
Then, you could configure two different runner strategies:
1. A `cron` that runs nightly, querying for jobs added to the `nightly` queue
2. Another that runs any jobs that were added to the `default` queue every ~5 minutes or so
## Executing jobs
As mentioned above, you can queue jobs, but the jobs won't run unless a worker picks up your jobs and runs them. This can be done in two ways:
#### Endpoint
You can execute jobs by making a fetch request to the `/api/payload-jobs/run` endpoint:
```ts
// Here, we're saying we want to run only 100 jobs for this invocation
// and we want to pull jobs from the `nightly` queue:
await fetch('/api/payload-jobs/run?limit=100&queue=nightly', {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
},
});
```
This endpoint is automatically mounted for you and is helpful in conjunction with serverless platforms like Vercel, where you might want to use Vercel Cron to invoke a serverless function that executes your jobs.
**Vercel Cron Example**
If you're deploying on Vercel, you can add a `vercel.json` file in the root of your project that configures Vercel Cron to invoke the `run` endpoint on a cron schedule.
Here's an example of what this file will look like:
```json
{
"crons": [
{
"path": "/api/payload-jobs/run",
"schedule": "*/5 * * * *"
}
]
}
```
The configuration above schedules the endpoint `/api/payload-jobs/run` to be invoked every 5 minutes.
The last step will be to secure your `run` endpoint so that only the proper users can invoke the runner.
To do this, you can set an environment variable on your Vercel project called `CRON_SECRET`, which should be a random string—ideally 16 characters or longer.
Then, you can modify the `access` function for running jobs by ensuring that only Vercel can invoke your runner.
```ts
export default buildConfig({
// Other configurations...
jobs: {
access: {
run: ({ req }: { req: PayloadRequest }): boolean => {
// Allow logged in users to execute this endpoint (default)
if (req.user) return true
// If there is no logged in user, then check
// for the Vercel Cron secret to be present as an
// Authorization header:
const authHeader = req.headers.get('authorization');
return authHeader === `Bearer ${process.env.CRON_SECRET}`;
},
},
// Other job configurations...
}
})
```
This works because Vercel automatically makes the `CRON_SECRET` environment variable available to the endpoint as the `Authorization` header when triggered by the Vercel Cron, ensuring that the jobs can be run securely.
After the project is deployed to Vercel, the Vercel Cron job will automatically trigger the `/api/payload-jobs/run` endpoint in the specified schedule, running the queued jobs in the background.
#### Local API
If you want to process jobs programmatically from your server-side code, you can use the Local API:
**Run all jobs:**
```ts
const results = await payload.jobs.run()
// You can customize the queue name and limit by passing them as arguments:
await payload.jobs.run({ queue: 'nightly', limit: 100 })
// You can provide a where clause to filter the jobs that should be run:
await payload.jobs.run({ where: { 'input.message': { equals: 'secret' } } })
```
**Run a single job:**
```ts
const results = await payload.jobs.runByID({
id: myJobID
})
```
#### Bin script
Finally, you can process jobs via the bin script that comes with Payload out of the box.
```sh
npx payload jobs:run --queue default --limit 10
```
In addition, the bin script allows you to pass a `--cron` flag to the `jobs:run` command to run the jobs on a scheduled, cron basis:
```sh
npx payload jobs:run --cron "*/5 * * * *"
```

143
docs/jobs-queue/tasks.mdx Normal file
View File

@@ -0,0 +1,143 @@
---
title: Tasks
label: Tasks
order: 20
desc: A Task is a distinct function declaration that can be run within Payload's Jobs Queue.
keywords: jobs queue, application framework, typescript, node, react, nextjs
---
<Banner type="default">
A <strong>"Task"</strong> is a function definition that performs business logic and whose input and output are both strongly typed.
</Banner>
You can register Tasks on the Payload config, and then create [Jobs](/docs/jobs-queue/jobs) or [Workflows](/docs/jobs-queue/workflows) that use them. Think of Tasks like tidy, isolated "functions that do one specific thing".
Payload Tasks can be configured to automatically retried if they fail, which makes them valuable for "durable" workflows like AI applications where LLMs can return non-deterministic results, and might need to be retried.
Tasks can either be defined within the `jobs.tasks` array in your payload config, or they can be defined inline within a workflow.
### Defining tasks in the config
Simply add a task to the `jobs.tasks` array in your Payload config. A task consists of the following fields:
| Option | Description |
| --------------------------- | -------------------------------------------------------------------------------- |
| `slug` | Define a slug-based name for this job. This slug needs to be unique among both tasks and workflows.|
| `handler` | The function that should be responsible for running the job. You can either pass a string-based path to the job function file, or the job function itself. If you are using large dependencies within your job, you might prefer to pass the string path because that will avoid bundling large dependencies in your Next.js app. Passing a string path is an advanced feature that may require a sophisticated build pipeline in order to work. |
| `inputSchema` | Define the input field schema - payload will generate a type for this schema. |
| `interfaceName` | You can use interfaceName to change the name of the interface that is generated for this task. By default, this is "Task" + the capitalized task slug. |
| `outputSchema` | Define the output field schema - payload will generate a type for this schema. |
| `label` | Define a human-friendly label for this task. |
| `onFail` | Function to be executed if the task fails. |
| `onSuccess` | Function to be executed if the task succeeds. |
| `retries` | Specify the number of times that this step should be retried if it fails. If this is undefined, the task will either inherit the retries from the workflow or have no retries. If this is 0, the task will not be retried. By default, this is undefined. |
The logic for the Task is defined in the `handler` - which can be defined as a function, or a path to a function. The `handler` will run once a worker picks picks up a Job that includes this task.
It should return an object with an `output` key, which should contain the output of the task as you've defined.
Example:
```ts
export default buildConfig({
// ...
jobs: {
tasks: [
{
// Configure this task to automatically retry
// up to two times
retries: 2,
// This is a unique identifier for the task
slug: 'createPost',
// These are the arguments that your Task will accept
inputSchema: [
{
name: 'title',
type: 'text',
required: true,
},
],
// These are the properties that the function should output
outputSchema: [
{
name: 'postID',
type: 'text',
required: true,
},
],
// This is the function that is run when the task is invoked
handler: async ({ input, job, req }) => {
const newPost = await req.payload.create({
collection: 'post',
req,
data: {
title: input.title,
},
})
return {
output: {
postID: newPost.id,
},
}
},
} as TaskConfig<'createPost'>,
]
}
})
```
In addition to defining handlers as functions directly provided to your Payload config, you can also pass an _absolute path_ to where the handler is defined. If your task has large dependencies, and you are planning on executing your jobs in a separate process that has access to the filesystem, this could be a handy way to make sure that your Payload + Next.js app remains quick to compile and has minimal dependencies.
Keep in mind that this is an advanced feature that may require a sophisticated build pipeline, especially when using it in production or within Next.js, e.g. by calling opening the `/api/payload-jobs/run` endpoint. You will have to transpile the handler files separately and ensure they are available in the same location when the job is run. If you're using an endpoint to execute your jobs, it's recommended to define your handlers as functions directly in your Payload Config, or use import paths handlers outside of Next.js.
In general, this is an advanced use case. Here's how this would look:
`payload.config.ts:`
```ts
import { fileURLToPath } from 'node:url'
import path from 'path'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
export default buildConfig({
jobs: {
tasks: [
{
// ...
// The #createPostHandler is a named export within the `createPost.ts` file
handler: path.resolve(dirname, 'src/tasks/createPost.ts') + '#createPostHandler',
}
]
}
})
```
Then, the `createPost` file itself:
`src/tasks/createPost.ts:`
```ts
import type { TaskHandler } from 'payload'
export const createPostHandler: TaskHandler<'createPost'> = async ({ input, job, req }) => {
const newPost = await req.payload.create({
collection: 'post',
req,
data: {
title: input.title,
},
})
return {
output: {
postID: newPost.id,
},
}
}
```

View File

@@ -0,0 +1,151 @@
---
title: Workflows
label: Workflows
order: 30
desc: A Task is a distinct function declaration that can be run within Payload's Jobs Queue.
keywords: jobs queue, application framework, typescript, node, react, nextjs
---
<Banner type="default">
A <strong>"Workflow"</strong> is an optional way to <em>combine multiple tasks together</em> in a way that can be gracefully retried from the point of failure.
</Banner>
They're most helpful when you have multiple tasks in a row, and you want to configure each task to be able to be retried if they fail.
If a task within a workflow fails, the Workflow will automatically "pick back up" on the task where it failed and **not re-execute any prior tasks that have already been executed**.
#### Defining a workflow
The most important aspect of a Workflow is the `handler`, where you can declare when and how the tasks should run by simply calling the `runTask` function. If any task within the workflow, fails, the entire `handler` function will re-run.
However, importantly, tasks that have successfully been completed will simply re-return the cached and saved output without running again. The Workflow will pick back up where it failed and only task from the failure point onward will be re-executed.
To define a JS-based workflow, simply add a workflow to the `jobs.wokflows` array in your Payload config. A workflow consists of the following fields:
| Option | Description |
| --------------------------- | -------------------------------------------------------------------------------- |
| `slug` | Define a slug-based name for this workflow. This slug needs to be unique among both tasks and workflows.|
| `handler` | The function that should be responsible for running the workflow. You can either pass a string-based path to the workflow function file, or workflow job function itself. If you are using large dependencies within your workflow, you might prefer to pass the string path because that will avoid bundling large dependencies in your Next.js app. Passing a string path is an advanced feature that may require a sophisticated build pipeline in order to work. |
| `inputSchema` | Define the input field schema - payload will generate a type for this schema. |
| `interfaceName` | You can use interfaceName to change the name of the interface that is generated for this workflow. By default, this is "Workflow" + the capitalized workflow slug. |
| `label` | Define a human-friendly label for this workflow. |
| `queue` | Optionally, define the queue name that this workflow should be tied to. Defaults to "default". |
| `retries` | You can define `retries` on the workflow level, which will enforce that the workflow can only fail up to that number of retries. If a task does not have retries specified, it will inherit the retry count as specified on the workflow. You can specify `0` as `workflow` retries, which will disregard all `task` retry specifications and fail the entire workflow on any task failure. You can leave `workflow` retries as undefined, in which case, the workflow will respect what each task dictates as their own retry count. By default this is undefined, meaning workflows retries are defined by their tasks |
Example:
```ts
export default buildConfig({
// ...
jobs: {
tasks: [
// ...
]
workflows: [
{
slug: 'createPostAndUpdate',
// The arguments that the workflow will accept
inputSchema: [
{
name: 'title',
type: 'text',
required: true,
},
],
// The handler that defines the "control flow" of the workflow
// Notice how it uses the `tasks` argument to execute your predefined tasks.
// These are strongly typed!
handler: async ({ job, tasks }) => {
// This workflow first runs a task called `createPost`.
// You need to define a unique ID for this task invocation
// that will always be the same if this workflow fails
// and is re-executed in the future. Here, we hard-code it to '1'
const output = await tasks.createPost('1', {
input: {
title: job.input.title,
},
})
// Once the prior task completes, it will run a task
// called `updatePost`
await tasks.updatePost('2', {
input: {
post: job.taskStatus.createPost['1'].output.postID, // or output.postID
title: job.input.title + '2',
},
})
},
} as WorkflowConfig<'updatePost'>
]
}
})
```
#### Running tasks inline
In the above example, our workflow was executing tasks that we already had defined in our Payload config. But, you can also run tasks without predefining them.
To do this, you can use the `inlineTask` function.
The drawbacks of this approach are that tasks cannot be re-used across workflows as easily, and the **task data stored in the job** will not be typed. In the following example, the inline task data will be stored on the job under `job.taskStatus.inline['2']` but completely untyped, as types for dynamic tasks like these cannot be generated beforehand.
Example:
```ts
export default buildConfig({
// ...
jobs: {
tasks: [
// ...
]
workflows: [
{
slug: 'createPostAndUpdate',
inputSchema: [
{
name: 'title',
type: 'text',
required: true,
},
],
handler: async ({ job, tasks, inlineTask }) => {
// Here, we run a predefined task.
// The `createPost` handler arguments and return type
// are both strongly typed
const output = await tasks.createPost('1', {
input: {
title: job.input.title,
},
})
// Here, this task is not defined in the Payload config
// and is "inline". Its output will be stored on the Job in the database
// however its arguments will be untyped.
const { newPost } = await inlineTask('2', {
task: async ({ req }) => {
const newPost = await req.payload.update({
collection: 'post',
id: '2',
req,
retries: 3,
data: {
title: 'updated!',
},
})
return {
output: {
newPost
},
}
},
})
},
} as WorkflowConfig<'updatePost'>
]
}
})
```

View File

@@ -77,13 +77,13 @@ export const MyFeature = createServerFeature({
This allows you to add i18n translations scoped to your feature. This specific example translation will be available under `lexical:myFeature:label` - `myFeature` being your feature key.
### Markdown Transformers
### Markdown Transformers#server-feature-markdown-transformers
The Server Feature, just like the Client Feature, allows you to add markdown transformers. Markdown transformers on the server are used when [converting the editor from or to markdown](/docs/lexical/converters#markdown-lexical).
```ts
import { createServerFeature } from '@payloadcms/richtext-lexical';
import type { ElementTransformer } from '@lexical/markdown'
import type { ElementTransformer } from '@payloadcms/richtext-lexical/lexical/markdown'
import {
$createMyNode,
$isMyNode,
@@ -120,7 +120,7 @@ export const MyFeature = createServerFeature({
In this example, the node will be outputted as `+++` in Markdown, and the markdown `+++` will be converted to a `MyNode` node in the editor.
### Nodes
### Nodes#server-feature-nodes
While nodes added to the server feature do not control how the node is rendered in the editor, they control other aspects of the node:
- HTML conversion
@@ -266,7 +266,7 @@ export const MyClientFeature = createClientFeature({
Explore the APIs available through ClientFeature to add the specific functionality you need. Remember, do not import directly from `'@payloadcms/richtext-lexical'` when working on the client-side, as it will cause errors with webpack or turbopack. Instead, use `'@payloadcms/richtext-lexical/client'` for all client-side imports. Type-imports are excluded from this rule and can always be imported.
### Nodes
### Nodes#client-feature-nodes
Add nodes to the `nodes` array in **both** your client & server feature. On the server side, nodes are utilized for backend operations like HTML conversion in a headless editor. On the client side, these nodes are integral to how content is displayed and managed in the editor, influencing how they are rendered, behave, and saved in the database.
@@ -299,9 +299,9 @@ import type {
EditorConfig,
LexicalNode,
SerializedLexicalNode,
} from 'lexical'
} from '@payloadcms/richtext-lexical/lexical'
import { $applyNodeReplacement, DecoratorNode } from 'lexical'
import { $applyNodeReplacement, DecoratorNode } from '@payloadcms/richtext-lexical/lexical'
// SerializedLexicalNode is the default lexical node.
// By setting your SerializedMyNode type to SerializedLexicalNode,
@@ -448,17 +448,17 @@ Example plugin.tsx:
'use client'
import type {
LexicalCommand,
} from 'lexical'
} from '@payloadcms/richtext-lexical/lexical'
import {
createCommand,
$getSelection,
$isRangeSelection,
COMMAND_PRIORITY_EDITOR
} from 'lexical'
} from '@payloadcms/richtext-lexical/lexical'
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext.js'
import { $insertNodeToNearestRoot } from '@lexical/utils'
import { useLexicalComposerContext } from '@payloadcms/richtext-lexical/lexical/react/LexicalComposerContext.js'
import { $insertNodeToNearestRoot } from '@payloadcms/richtext-lexical/lexical/utils'
import { useEffect } from 'react'
import type { PluginComponent } from '@payloadcms/richtext-lexical' // type imports can be imported from @payloadcms/richtext-lexical - even on the client
@@ -589,7 +589,7 @@ import { createClientFeature, toolbarAddDropdownGroupWithItems } from '@payloadc
import { IconComponent } from './icon';
import { $isHorizontalRuleNode } from './nodes/MyNode';
import { INSERT_MYNODE_COMMAND } from './plugin';
import { $isNodeSelection } from 'lexical'
import { $isNodeSelection } from '@payloadcms/richtext-lexical/lexical'
export const MyClientFeature = createClientFeature({
toolbarFixed: {
@@ -705,13 +705,13 @@ export const MyClientFeature = createClientFeature({
| **`keywords`** | Keywords are used to match the item for different texts typed after the '/'. E.g. you might want to show a horizontal rule item if you type both /hr, /separator, /horizontal etc. In addition to the keywords, the label and key will be used to find the right slash menu item. |
### Markdown Transformers
### Markdown Transformers#client-feature-markdown-transformers
The Client Feature, just like the Server Feature, allows you to add markdown transformers. Markdown transformers on the client are used to create new nodes when a certain markdown pattern is typed in the editor.
```ts
import { createClientFeature } from '@payloadcms/richtext-lexical/client';
import type { ElementTransformer } from '@lexical/markdown'
import type { ElementTransformer } from '@payloadcms/richtext-lexical/lexical/markdown'
import {
$createMyNode,
$isMyNode,
@@ -836,4 +836,4 @@ The reason the client feature does not have the same props available as the serv
## More information
Have a look at the [features we've already built](https://github.com/payloadcms/payload/tree/beta/packages/richtext-lexical/src/features) - understanding how they work will help you understand how to create your own. There is no difference between the features included by default and the ones you create yourself - since those features are all isolated from the "core", you have access to the same APIs, whether the feature is part of Payload or not!
Have a look at the [features we've already built](https://github.com/payloadcms/payload/tree/main/packages/richtext-lexical/src/features) - understanding how they work will help you understand how to create your own. There is no difference between the features included by default and the ones you create yourself - since those features are all isolated from the "core", you have access to the same APIs, whether the feature is part of Payload or not!

View File

@@ -6,14 +6,67 @@ desc: Conversion between lexical, markdown and html
keywords: lexical, rich text, editor, headless cms, convert, html, mdx, markdown, md, conversion, export
---
Lexical saves data in JSON - this is great for storage and flexibility and allows you to easily to convert it to other formats like JSX, HTML or Markdown.
## Lexical => JSX
If you have a React-based frontend, converting lexical to JSX is the recommended way to render rich text content in your frontend. To do that, import the `RichText` component from `@payloadcms/richtext-lexical/react` and pass the lexical content to it:
```tsx
import React from 'react'
import { RichText } from '@payloadcms/richtext-lexical/react'
export const MyComponent = ({ lexicalData }) => {
return (
<RichText data={lexicalData} />
)
}
```
The `RichText` component will come with the most common serializers built-in, though you can also pass in your own serializers if you need to.
<Banner type="default">
The JSX converter expects the input data to be fully populated. When fetching data, ensure the `depth` setting is high enough, to ensure that lexical nodes such as uploads are populated.
</Banner>
### Converting Lexical Blocks to JSX
In order to convert lexical blocks or inline blocks to JSX, you will have to pass the converter for your block to the RichText component. This converter is not included by default, as Payload doesn't know how to render your custom blocks.
```tsx
import React from 'react'
import {
type JSXConvertersFunction,
RichText,
} from '@payloadcms/richtext-lexical/react'
import type { SerializedEditorState } from '@payloadcms/richtext-lexical/lexical'
const jsxConverters: JSXConvertersFunction = ({ defaultConverters }) => ({
...defaultConverters,
blocks: {
// myTextBlock is the slug of the block
myTextBlock: ({ node }) => <div style={{ backgroundColor: 'red' }}>{node.fields.text}</div>,
},
})
export const MyComponent = ({ lexicalData }) => {
return (
<RichText
converters={jsxConverters}
data={lexicalData.lexicalWithBlocks as SerializedEditorState}
/>
)
}
```
## Lexical => HTML
Lexical saves data in JSON, but can also generate its HTML representation via two main methods:
If you don't have a React-based frontend, or if you need to send the content to a third-party service, you can convert lexical to HTML. There are two ways to do this:
1. **Outputting HTML from the Collection:** Create a new field in your collection to convert saved JSON content to HTML. Payload generates and outputs the HTML for use in your frontend.
2. **Generating HTML on any server** Convert JSON to HTML on-demand on the server.
The editor comes with built-in HTML serializers, simplifying the process of converting JSON to HTML.
In both cases, the conversion needs to happen on a server, as the HTML converter will automatically fetch data for nodes that require it (e.g. uploads and internal links). The editor comes with built-in HTML serializers, simplifying the process of converting JSON to HTML.
### Outputting HTML from the Collection
@@ -200,7 +253,7 @@ Lexical provides a seamless way to perform conversions between various other for
A headless editor can perform such conversions outside of the main editor instance. Follow this method to initiate a headless editor:
```ts
import { createHeadlessEditor } from '@lexical/headless' // <= make sure this package is installed
import { createHeadlessEditor } from '@payloadcms/richtext-lexical/lexical/headless'
import { getEnabledNodes, sanitizeServerEditorConfig } from '@payloadcms/richtext-lexical'
const yourEditorConfig // <= your editor config here
@@ -237,7 +290,7 @@ If you have access to the sanitized collection config, you can get access to the
```ts
import type { CollectionConfig, RichTextField } from 'payload'
import { createHeadlessEditor } from '@lexical/headless'
import { createHeadlessEditor } from '@payloadcms/richtext-lexical/lexical/headless'
import type { LexicalRichTextAdapter, SanitizedServerEditorConfig } from '@payloadcms/richtext-lexical'
import {
getEnabledNodes,
@@ -292,8 +345,8 @@ export const MyCollection: CollectionConfig = {
Once you have your headless editor instance, you can use it to convert HTML to Lexical:
```ts
import { $generateNodesFromDOM } from '@lexical/html'
import { $getRoot, $getSelection } from 'lexical'
import { $generateNodesFromDOM } from '@payloadcms/richtext-lexical/lexical/html'
import { $getRoot, $getSelection } from '@payloadcms/richtext-lexical/lexical'
import { JSDOM } from 'jsdom'
headlessEditor.update(
@@ -334,8 +387,7 @@ This has been taken from the [lexical serialization & deserialization docs](http
Convert markdown content to the Lexical editor format with the following:
```ts
import { $convertFromMarkdownString } from '@lexical/markdown'
import { sanitizeServerEditorConfig } from '@payloadcms/richtext-lexical'
import { sanitizeServerEditorConfig, $convertFromMarkdownString } from '@payloadcms/richtext-lexical'
const yourSanitizedEditorConfig = sanitizeServerEditorConfig(yourEditorConfig, payloadConfig) // <= your editor config & Payload Config here
const markdown = `# Hello World`
@@ -361,16 +413,21 @@ Export content from the Lexical editor into Markdown format using these steps:
Here's the code for it:
```ts
import { $convertToMarkdownString } from '@lexical/markdown'
import { $convertToMarkdownString } from '@payloadcms/richtext-lexical/lexical/markdown'
import { sanitizeServerEditorConfig } from '@payloadcms/richtext-lexical'
import type { SerializedEditorState } from 'lexical'
import type { SerializedEditorState } from '@payloadcms/richtext-lexical/lexical'
const yourSanitizedEditorConfig = sanitizeServerEditorConfig(yourEditorConfig, payloadConfig) // <= your editor config & Payload Config here
const yourEditorState: SerializedEditorState // <= your current editor state here
// Import editor state into your headless editor
try {
headlessEditor.setEditorState(headlessEditor.parseEditorState(yourEditorState)) // This should commit the editor state immediately
headlessEditor.update(
() => {
headlessEditor.setEditorState(headlessEditor.parseEditorState(yourEditorState))
},
{ discrete: true }, // This should commit the editor state immediately
)
} catch (e) {
logger.error({ err: e }, 'ERROR parsing editor state')
}
@@ -382,8 +439,6 @@ headlessEditor.getEditorState().read(() => {
})
```
The `.setEditorState()` function immediately updates your editor state. Thus, there's no need for the `discrete: true` flag when reading the state afterward.
## Lexical => Plain Text
Export content from the Lexical editor into plain text using these steps:
@@ -394,15 +449,20 @@ Export content from the Lexical editor into plain text using these steps:
Here's the code for it:
```ts
import type { SerializedEditorState } from 'lexical'
import { $getRoot } from 'lexical'
import type { SerializedEditorState } from '@payloadcms/richtext-lexical/lexical'
import { $getRoot } from '@payloadcms/richtext-lexical/lexical'
const yourEditorState: SerializedEditorState // <= your current editor state here
// Import editor state into your headless editor
try {
headlessEditor.setEditorState(headlessEditor.parseEditorState(yourEditorState)) // This should commit the editor state immediately
} catch (e) {
headlessEditor.update(
() => {
headlessEditor.setEditorState(headlessEditor.parseEditorState(yourEditorState))
},
{ discrete: true }, // This should commit the editor state immediately
)
} catch (e) {
logger.error({ err: e }, 'ERROR parsing editor state')
}

View File

@@ -10,12 +10,6 @@ One of Payload's goals is to build the best rich text editor experience that we
Classically, we've used SlateJS to work toward this goal, but building custom elements into Slate has proven to be more difficult than we'd like, and we've been keeping our options open.
<Banner type="warning">
Payload's Lexical rich text editor is currently in beta. It's stable enough to use as you build on
Payload, so if you're up for helping us fine-tune it, you should use it. But if you're looking for
stability, use Slate instead.
</Banner>
Lexical is extremely impressive and trivializes a lot of the hard parts of building new elements into a rich text editor. It has a few distinct advantages over Slate, including the following:
1. A "/" menu, which allows editors to easily add new elements while never leaving their keyboard

View File

@@ -52,7 +52,7 @@ _\* An asterisk denotes that a property is required._
### URL
The `url` property is a string that points to your front-end application. This value is used as the `src` attribute of the iframe rendering your front-end. Once loaded, the Admin Panel will communicate directly with your app through `window.postMessage` events.
The `url` property resolves to a string that points to your front-end application. This value is used as the `src` attribute of the iframe rendering your front-end. Once loaded, the Admin Panel will communicate directly with your app through `window.postMessage` events.
To set the URL, use the `admin.livePreview.url` property in your [Payload Config](../configuration/overview):
@@ -105,8 +105,16 @@ The following arguments are provided to the `url` function:
| Path | Description |
| ------------------ | ----------------------------------------------------------------------------------------------------------------- |
| **`data`** | The data of the Document being edited. This includes changes that have not yet been saved. |
| **`documentInfo`** | Information about the Document being edited like collection slug. [More details](../admin/hooks#usedocumentinfo). |
| **`locale`** | The locale currently being edited (if applicable). [More details](../configuration/localization). |
| **`collectionConfig`** | The Collection Admin Config of the Document being edited. [More details](../admin/collections). |
| **`globalConfig`** | The Global Admin Config of the Document being edited. [More details](../admin/globals). |
| **`req`** | The Payload Request object. |
If your application requires a fully qualified URL, such as within deploying to Vercel Preview Deployments, you can use the `req` property to build this URL:
```ts
url: (doc, { req }) => `${req.protocol}//${req.host}/${doc.slug}` // highlight-line
```
### Breakpoints

View File

@@ -34,11 +34,11 @@ Then, render the `RefreshRouteOnSave` component anywhere in your `page.tsx`. Her
```tsx
import { RefreshRouteOnSave } from './RefreshRouteOnSave.tsx'
import { getPayloadHMR } from '@payloadcms/next/utilities'
import { getPayload } from 'payload'
import config from '../payload.config'
export default async function Page() {
const payload = await getPayloadHMR({ config })
const payload = await getPayload({ config })
const page = await payload.findByID({
collection: 'pages',

View File

@@ -18,12 +18,9 @@ Payload can be used completely outside of Next.js which is helpful in cases like
Payload provides a convenient way to run standalone scripts, which can be useful for tasks like seeding your database or performing one-off operations.
In standalone scripts, can simply import the Payload Config and use it right away. If you need an initialized copy of Payload, you can then use the `getPayload` function. This can be useful for tasks like seeding your database or performing other one-off operations.
In standalone scripts, you can simply import the Payload Config and use it right away. If you need an initialized copy of Payload, you can then use the `getPayload` function. This can be useful for tasks like seeding your database or performing other one-off operations.
```ts
// We are importing `getPayload` because we don't need HMR
// for a standalone script. For usage of Payload inside Next.js,
// you should always use `import { getPayloadHMR } from '@payloadcms/next/utilities'` instead.
import { getPayload } from 'payload'
import config from '@payload-config'

View File

@@ -43,27 +43,6 @@ const afterChangeHook: CollectionAfterChangeHook = async ({ req: { payload } })
If you want to import Payload in places where you don't have the option to access it from function arguments or `req`, you can import it and initialize it.
There are two places to import Payload:
**Option 1 - using HMR, within Next.js**
```ts
import { getPayloadHMR } from '@payloadcms/next/utilities'
import config from '@payload-config'
const payload = await getPayloadHMR({ config })
```
You should import Payload using the first option (`getPayloadHMR`) if you are using Payload inside of Next.js (like route handlers, server components, and similar.)
This way, in Next.js development mode, Payload will work with Hot Module Replacement (HMR), and as you make changes to your Payload Config, your usage of Payload will always be in sync with your changes. In production, `getPayloadHMR` simply disables all HMR functionality so you don't need to write your code any differently. We handle optimization for you in production mode.
If you are accessing Payload via function arguments or `req.payload`, HMR is automatically supported if you are using it within Next.js.
**Option 2 - outside of Next.js**
If you are using Payload outside of Next.js, for example in standalone scripts or in other frameworks, you can import Payload with no HMR functionality. Instead of using `getPayloadHMR`, you can use `getPayload`.
```ts
import { getPayload } from 'payload'
import config from '@payload-config'
@@ -71,7 +50,11 @@ import config from '@payload-config'
const payload = await getPayload({ config })
```
Both options function in exactly the same way outside of one having HMR support and the other not. For more information about using Payload outside of Next.js, [click here](./outside-nextjs).
If you're working in Next.js' development mode, Payload will work with Hot Module Replacement (HMR), and as you make changes to your Payload Config, your usage of Payload will always be in sync with your changes. In production, `getPayload` simply disables all HMR functionality so you don't need to write your code any differently. We handle optimization for you in production mode.
If you are accessing Payload via function arguments or `req.payload`, HMR is automatically supported if you are using it within Next.js.
For more information about using Payload outside of Next.js, [click here](./outside-nextjs).
## Local options available
@@ -84,6 +67,7 @@ You can specify more options within the Local API vs. REST or GraphQL due to the
| `depth` | [Control auto-population](../queries/depth) of nested relationship and upload fields. |
| `locale` | Specify [locale](/docs/configuration/localization) for any returned documents. |
| `select` | Specify [select](../queries/select) to control which fields to include to the result. |
| `populate` | Specify [populate](../queries/select#populate) to control which fields to include to the result from populated documents. |
| `fallbackLocale` | Specify a [fallback locale](/docs/configuration/localization) to use for any returned documents. |
| `overrideAccess` | Skip access control. By default, this property is set to true within all Local API operations. |
| `overrideLock` | By default, document locks are ignored (`true`). Set to `false` to enforce locks and prevent operations when a document is locked by another user. [More details](../admin/locked-documents). |
@@ -96,6 +80,17 @@ You can specify more options within the Local API vs. REST or GraphQL due to the
_There are more options available on an operation by operation basis outlined below._
## Transactions
When your database uses transactions you need to thread req through to all local operations. Postgres uses transactions and MongoDB uses transactions when you are using replica sets. Passing req without transactions is still recommended.
```js
const post = await payload.find({
collection: 'posts',
req, // passing req is recommended
})
```
<Banner type="warning">
<strong>Note:</strong>
<br />
@@ -107,7 +102,7 @@ _There are more options available on an operation by operation basis outlined be
The following Collection operations are available through the Local API:
### Create
### Create#collection-create
```js
// The created Post document is returned
@@ -136,10 +131,13 @@ const post = await payload.create({
// Alternatively, you can directly pass a File,
// if file is provided, filePath will be omitted
file: uploadedFile,
// If you want to create a document that is a duplicate of another document
duplicateFromID: 'document-id-to-duplicate',
})
```
### Find
### Find#collection-find
```js
// Result will be a paginated set of Posts.
@@ -160,7 +158,7 @@ const result = await payload.find({
})
```
### Find by ID
### Find by ID#collection-find-by-id
```js
// Result will be a Post document.
@@ -176,7 +174,7 @@ const result = await payload.findByID({
})
```
### Count
### Count#collection-count
```js
// Result will be an object with:
@@ -192,7 +190,7 @@ const result = await payload.count({
})
```
### Update by ID
### Update by ID#collection-update-by-id
```js
// Result will be the updated Post document.
@@ -224,7 +222,7 @@ const result = await payload.update({
})
```
### Update Many
### Update Many#collection-update-many
```js
// Result will be an object with:
@@ -263,7 +261,7 @@ const result = await payload.update({
})
```
### Delete
### Delete#collection-delete
```js
// Result will be the now-deleted Post document.
@@ -280,7 +278,7 @@ const result = await payload.delete({
})
```
### Delete Many
### Delete Many#collection-delete-many
```js
// Result will be an object with:
@@ -309,6 +307,27 @@ const result = await payload.delete({
If a collection has [`Authentication`](/docs/authentication/overview) enabled, additional Local API operations will be
available:
### Auth
```js
// If you're using Next.js, you'll have to import headers from next/headers, like so:
// import { headers as nextHeaders } from 'next/headers'
// you'll also have to await headers inside your function, or component, like so:
// const headers = await nextHeaders()
// If you're using payload outside of Next.js, you'll have to provide headers accordingly.
// result will be formatted as follows:
// {
// permissions: { ... }, // object containing current user's permissions
// user: { ... }, // currently logged in user's document
// responseHeaders: { ... } // returned headers from the response
// }
const result = await payload.auth({headers})
```
### Login
```js
@@ -399,7 +418,7 @@ const result = await payload.verifyEmail({
The following Global operations are available through the Local API:
### Find
### Find#global-find
```js
// Result will be the Header Global.
@@ -414,7 +433,7 @@ const result = await payload.findGlobal({
})
```
### Update
### Update#global-update
```js
// Result will be the updated Header Global.

File diff suppressed because it is too large Load Diff

View File

@@ -84,7 +84,7 @@ cd dev
npx create-payload-app@latest
```
If you&apos;re using the plugin template, the dev folder is built out for you and the `samplePlugin` has already been installed in `dev/payload.config()`.
If you&apos;re using the plugin template, the dev folder is built out for you and the `samplePlugin` has already been installed in `dev/payload.config.ts`.
```
plugins: [
@@ -95,11 +95,11 @@ If you&apos;re using the plugin template, the dev folder is built out for you an
]
```
You can add to the `dev/payload.config` and build out the dev project as needed to test your plugin.
You can add to the `dev/payload.config.ts` and build out the dev project as needed to test your plugin.
When you&apos;re ready to start development, navigate into this folder with `cd dev`
And then start the project with `yarn dev` and pull up `http://localhost:3000` in your browser.
And then start the project with `pnpm dev` and pull up `http://localhost:3000` in your browser.
## Testing
@@ -112,7 +112,7 @@ Jest organizes tests into test suites and cases. We recommend creating tests bas
The plugin template provides a stubbed out test suite at `dev/plugin.spec.ts` which is ready to go - just add in your own test conditions and you&apos;re all set!
```
import payload from 'payload'
let payload: Payload
describe('Plugin tests', () => {
// Example test to check for seeded data
@@ -245,7 +245,7 @@ config.hooks = {
```
### Extending functions
Function properties cannot use spread syntax. The way to extend them is to execute the existing function if it exists and then run your additional functionality.
Function properties cannot use spread syntax. The way to extend them is to execute the existing function if it exists and then run your additional functionality.
Here is an example extending the `onInit` property:
@@ -285,11 +285,11 @@ For a better user experience, provide a way to disable the plugin without uninst
### Include tests in your GitHub CI workflow
If you&apos;ve configured tests for your package, integrate them into your workflow to run the tests each time you commit to the plugin repository. Learn more about [how to configure tests into your GitHub CI workflow.](https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs)
If you&apos;ve configured tests for your package, integrate them into your workflow to run the tests each time you commit to the plugin repository. Learn more about [how to configure tests into your GitHub CI workflow.](https://docs.github.com/en/actions/use-cases-and-examples/building-and-testing/building-and-testing-nodejs)
### Publish your finished plugin to NPM
### Publish your finished plugin to npm
The best way to share and allow others to use your plugin once it is complete is to publish an NPM package. This process is straightforward and well documented, find out more about [creating and publishing a NPM package here](https://docs.npmjs.com/creating-and-publishing-scoped-public-packages/).
The best way to share and allow others to use your plugin once it is complete is to publish an npm package. This process is straightforward and well documented, find out more about [creating and publishing a npm package here](https://docs.npmjs.com/creating-and-publishing-scoped-public-packages/).
### Add payload-plugin topic tag

View File

@@ -6,7 +6,7 @@ desc: Easily build and manage forms from the Admin Panel. Send dynamic, personal
keywords: plugins, plugin, form, forms, form builder
---
[![NPM](https://img.shields.io/npm/v/@payloadcms/plugin-form-builder)](https://www.npmjs.com/package/@payloadcms/plugin-form-builder)
[![npm](https://img.shields.io/npm/v/@payloadcms/plugin-form-builder)](https://www.npmjs.com/package/@payloadcms/plugin-form-builder)
This plugin allows you to build and manage custom forms directly within the [Admin Panel](../admin/overview). Instead of hard-coding a new form into your website or application every time you need one, admins can simply define the schema for each form they need on-the-fly, and your front-end can map over this schema, render its own UI components, and match your brand's design system.
@@ -33,10 +33,10 @@ Forms can be as simple or complex as you need, from a basic contact form, to a m
## Installation
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
Install the plugin using any JavaScript package manager like [pnpm](https://pnpm.io), [npm](https://npmjs.com), or [Yarn](https://yarnpkg.com):
```bash
pnpm add @payloadcms/plugin-form-builder@beta
pnpm add @payloadcms/plugin-form-builder
```
## Basic Usage
@@ -72,7 +72,7 @@ The `fields` property is an object of field types to allow your admin editors to
```ts
// payload.config.ts
formBuilder({
formBuilderPlugin({
// ...
fields: {
text: true,
@@ -95,7 +95,7 @@ The `redirectRelationships` property is an array of collection slugs that, when
```ts
// payload.config.ts
formBuilder({
formBuilderPlugin({
// ...
redirectRelationships: ['pages'],
})
@@ -103,11 +103,11 @@ formBuilder({
### `beforeEmail`
The `beforeEmail` property is a [beforeChange](<[beforeChange](https://payloadcms.com/docs/hooks/globals#beforechange)>) hook that is called just after emails are prepared, but before they are sent. This is a great place to inject your own HTML template to add custom styles.
The `beforeEmail` property is a [beforeChange](https://payloadcms.com/docs/hooks/globals#beforechange) hook that is called just after emails are prepared, but before they are sent. This is a great place to inject your own HTML template to add custom styles.
```ts
// payload.config.ts
formBuilder({
formBuilderPlugin({
// ...
beforeEmail: (emailsToSend, beforeChangeParams) => {
// modify the emails in any way before they are sent
@@ -142,7 +142,7 @@ Provide a fallback for the email address to send form submissions to. If the ema
```ts
// payload.config.ts
formBuilder({
formBuilderPlugin({
// ...
defaultToEmail: 'test@example.com',
})
@@ -160,7 +160,7 @@ Good to know: The form collection is publicly available to read by default. The
```ts
// payload.config.ts
formBuilder({
formBuilderPlugin({
// ...
formOverrides: {
slug: 'contact-forms',
@@ -196,7 +196,7 @@ Override anything on the `form-submissions` collection by sending a [Payload Col
```ts
// payload.config.ts
formBuilder({
formBuilderPlugin({
// ...
formSubmissionOverrides: {
slug: 'leads',
@@ -215,7 +215,7 @@ formBuilder({
### `handlePayment`
The `handlePayment` property is a [beforeChange](<[beforeChange](https://payloadcms.com/docs/hooks/globals#beforechange)>) hook that is called upon form submission. You can integrate into any third-party payment processing API here to accept payment based on form input. You can use the `getPaymentTotal` function to calculate the total cost after all conditions have been applied. This is only applicable if the form has enabled the `payment` field.
The `handlePayment` property is a [beforeChange](https://payloadcms.com/docs/hooks/globals#beforechange) hook that is called upon form submission. You can integrate into any third-party payment processing API here to accept payment based on form input. You can use the `getPaymentTotal` function to calculate the total cost after all conditions have been applied. This is only applicable if the form has enabled the `payment` field.
First import the utility function. This will execute all of the price conditions that you have set in your form's `payment` field and returns the total price.
@@ -228,7 +228,7 @@ Then in your plugin's config:
```ts
// payload.config.ts
formBuilder({
formBuilderPlugin({
// ...
handlePayment: async ({ form, submissionData }) => {
// first calculate the price
@@ -396,7 +396,7 @@ Then merging it into your own custom field:
```ts
// payload.config.ts
formBuilder({
formBuilderPlugin({
// ...
fields: {
text: {

View File

@@ -6,7 +6,7 @@ desc: Nested documents in a parent, child, and sibling relationship.
keywords: plugins, nested, documents, parent, child, sibling, relationship
---
[![NPM](https://img.shields.io/npm/v/@payloadcms/plugin-nested-docs)](https://www.npmjs.com/package/@payloadcms/plugin-nested-docs)
[![npm](https://img.shields.io/npm/v/@payloadcms/plugin-nested-docs)](https://www.npmjs.com/package/@payloadcms/plugin-nested-docs)
This plugin allows you to easily nest the documents of your application inside of one another. It does so by adding a
new `parent` field onto each of your documents that, when selected, attaches itself to the parent's tree. When you edit
@@ -44,11 +44,10 @@ but different parents.
## Installation
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com),
or [PNPM](https://pnpm.io):
Install the plugin using any JavaScript package manager like [pnpm](https://pnpm.io), [npm](https://npmjs.com), or [Yarn](https://yarnpkg.com):
```bash
pnpm add @payloadcms/plugin-nested-docs@beta
pnpm add @payloadcms/plugin-nested-docs
```
## Basic Usage
@@ -102,8 +101,8 @@ level and stores the following fields.
| Field | Description |
| ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `label` | The label of the breadcrumb. This field is automatically set to either the `collection.admin.useAsTitle` (if defined) or is set to the `ID` of the document. You can also dynamically define the `label` by passing a function to the options property of [`generateLabel`](#generateLabel). |
| `url` | The URL of the breadcrumb. By default, this field is undefined. You can manually define this field by passing a property called function to the plugin options property of [`generateURL`](#generateURL). |
| `label` | The label of the breadcrumb. This field is automatically set to either the `collection.admin.useAsTitle` (if defined) or is set to the `ID` of the document. You can also dynamically define the `label` by passing a function to the options property of [`generateLabel`](#generatelabel). |
| `url` | The URL of the breadcrumb. By default, this field is undefined. You can manually define this field by passing a property called function to the plugin options property of [`generateURL`](#generateurl). |
### Options
@@ -120,7 +119,7 @@ You can also pass a function to dynamically set the `label` of your breadcrumb.
```ts
// payload.config.ts
nestedDocs({
nestedDocsPlugin({
//...
generateLabel: (_, doc) => doc.title, // NOTE: 'title' is a hypothetical field
})
@@ -141,7 +140,7 @@ that point, like `/about-us/company/our-team`.
```ts
// payload.config.ts
nestedDocs({
nestedDocsPlugin({
//...
generateURL: (docs) => docs.reduce((url, doc) => `${url}/${doc.slug}`, ''), // NOTE: 'slug' is a hypothetical field
})
@@ -226,7 +225,7 @@ const examplePageConfig: CollectionConfig = {
This plugin supports localization by default. If the `localization` property is set in your Payload Config,
the `breadcrumbs` field is automatically localized. For more details on how localization works in Payload, see
the [Localization](https://payloadcms.com/docs/localization/overview) docs.
the [Localization](https://payloadcms.com/docs/configuration/localization) docs.
## TypeScript
@@ -238,10 +237,4 @@ import { PluginConfig, GenerateURL, GenerateLabel } from '@payloadcms/plugin-nes
## Examples
The [Examples Directory](https://github.com/payloadcms/payload/tree/main/examples) contains an
official [Nested Docs Plugin Example](https://github.com/payloadcms/payload/tree/main/examples/nested-docs) which
demonstrates exactly how to configure this plugin in Payload and implement it on your front-end.
The [Templates Directory](https://github.com/payloadcms/payload/tree/main/templates) also contains an
official [Website Template](https://github.com/payloadcms/payload/tree/main/templates/website)
and [E-commerce Template](https://github.com/payloadcms/payload/tree/main/templates/ecommerce), both of which use this
plugin.
The [Templates Directory](https://github.com/payloadcms/payload/tree/main/templates) also contains an official [Website Template](https://github.com/payloadcms/payload/tree/main/templates/website) and [E-commerce Template](https://github.com/payloadcms/payload/tree/main/templates/ecommerce), both of which use this plugin.

View File

@@ -8,7 +8,7 @@ keywords: plugins, config, configuration, extensions, custom, documentation, Con
Payload Plugins take full advantage of the modularity of the [Payload Config](../configuration/overview), allowing developers developers to easily inject custom—sometimes complex—functionality into Payload apps from a very small touch-point. This is especially useful is sharing your work across multiple projects or with the greater Payload community.
There are many [Official Plugins](#official-plugins) available that solve for some of the most common uses cases, such as the [Form Builder Plugin](./seo) or [SEO Plugin](./seo). There are also [Community Plugins](#community-plugins) available, maintained entirely by contributing members. To extend Payload's functionality in some other way, you can easily [build your own plugin](./build-your-own).
There are many [Official Plugins](#official-plugins) available that solve for some of the most common uses cases, such as the [Form Builder Plugin](./form-builder) or [SEO Plugin](./seo). There are also [Community Plugins](#community-plugins) available, maintained entirely by contributing members. To extend Payload's functionality in some other way, you can easily [build your own plugin](./build-your-own).
To configure Plugins, use the `plugins` property in your [Payload Config](../configuration/overview):
@@ -68,7 +68,7 @@ Plugins are changing every day, so be sure to check back often to see what new p
Community Plugins are those that are maintained entirely by outside contributors. They are a great way to share your work across the ecosystem for others to use. You can discover Community Plugins by browsing the `payload-plugin` topic on [GitHub](https://github.com/topics/payload-plugin).
Some plugins have become so widely used that they are adopted as an [Official Plugin](#official-plugin), such as the [Lexical Plugin](https://github.com/AlessioGr/payload-plugin-lexical). If you have a plugin that you think should be an Official Plugin, please feel free to start a new [Discussion](https://github.com/payloadcms/payload/discussions).
Some plugins have become so widely used that they are adopted as an [Official Plugin](#official-plugins), such as the [Lexical Plugin](https://github.com/AlessioGr/payload-plugin-lexical). If you have a plugin that you think should be an Official Plugin, please feel free to start a new [Discussion](https://github.com/payloadcms/payload/discussions).
<Banner type="warning">
For maintainers building plugins for others to use, please add the `payload-plugin` topic on [GitHub](https://github.com/topics/payload-plugin) to help others find it.

View File

@@ -6,7 +6,7 @@ desc: Automatically create redirects for your Payload application
keywords: plugins, redirects, redirect, plugin, payload, cms, seo, indexing, search, search engine
---
[![NPM](https://img.shields.io/npm/v/@payloadcms/plugin-redirects)](https://www.npmjs.com/package/@payloadcms/plugin-redirects)
[![npm](https://img.shields.io/npm/v/@payloadcms/plugin-redirects)](https://www.npmjs.com/package/@payloadcms/plugin-redirects)
This plugin allows you to easily manage redirects for your application from within your [Admin Panel](../admin/overview). It does so by adding a `redirects` collection to your config that allows you specify a redirect from one URL to another. Your front-end application can use this data to automatically redirect users to the correct page using proper HTTP status codes. This is useful for SEO, indexing, and search engine ranking when re-platforming or when changing your URL structure.
@@ -29,10 +29,10 @@ For example, if you have a page at `/about` and you want to change it to `/about
## Installation
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
Install the plugin using any JavaScript package manager like [pnpm](https://pnpm.io), [npm](https://npmjs.com), or [Yarn](https://yarnpkg.com):
```bash
pnpm add @payloadcms/plugin-redirects@beta
pnpm add @payloadcms/plugin-redirects
```
## Basic Usage
@@ -102,4 +102,4 @@ import { PluginConfig } from '@payloadcms/plugin-redirects/types'
## Examples
The [Examples Directory](https://github.com/payloadcms/payload/tree/main/examples) contains an official [Redirects Plugin Example](https://github.com/payloadcms/payload/tree/main/examples/redirects) which demonstrates exactly how to configure this plugin in Payload and implement it on your front-end. The [Templates Directory](https://github.com/payloadcms/payload/tree/main/templates) also contains an official [Website Template](https://github.com/payloadcms/payload/tree/main/templates/website) and [E-commerce Template](https://github.com/payloadcms/payload/tree/main/templates/ecommere), both of which use this plugin.
The [Templates Directory](https://github.com/payloadcms/payload/tree/main/templates) also contains an official [Website Template](https://github.com/payloadcms/payload/tree/main/templates/website) and [E-commerce Template](https://github.com/payloadcms/payload/tree/main/templates/ecommerce), both of which use this plugin.

View File

@@ -6,7 +6,7 @@ desc: Generates records of your documents that are extremely fast to search on.
keywords: plugins, search, search plugin, search engine, search index, search results, search bar, search box, search field, search form, search input
---
[![NPM](https://img.shields.io/npm/v/@payloadcms/plugin-search)](https://www.npmjs.com/package/@payloadcms/plugin-search)
[![npm](https://img.shields.io/npm/v/@payloadcms/plugin-search)](https://www.npmjs.com/package/@payloadcms/plugin-search)
This plugin generates records of your documents that are extremely fast to search on. It does so by creating a new `search` collection that is indexed in the database then saving a static copy of each of your documents using only search-critical data. Search records are automatically created, synced, and deleted behind-the-scenes as you manage your application's documents.
@@ -33,13 +33,14 @@ This plugin is a great way to implement a fast, immersive search experience such
- Allows you to query search results using first-party Payload APIs
- Allows you to query documents without triggering any of their underlying hooks
- Allows you to easily prioritize search results by collection or document
- Allows you to reindex search results by collection on demand
## Installation
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
Install the plugin using any JavaScript package manager like [pnpm](https://pnpm.io), [npm](https://npmjs.com), or [Yarn](https://yarnpkg.com):
```bash
pnpm add @payloadcms/plugin-search@beta
pnpm add @payloadcms/plugin-search
```
## Basic Usage
@@ -81,7 +82,7 @@ export default config
The `collections` property is an array of collection slugs to enable syncing to search. Enabled collections receive a `beforeChange` and `afterDelete` hook that creates, updates, and deletes its respective search record as it changes over time.
### `localize`
#### `localize`
By default, the search plugin will add `localization: true` to the `title` field of the newly added `search` collection if you have localization enabled. If you would like to disable this behavior, you can set this to `false`.
@@ -134,7 +135,7 @@ Note that the `fields` property is a function that receives an object with a `de
#### `beforeSync`
Before creating or updating a search record, the `beforeSync` function runs. This is an [afterChange](<[afterChange](https://payloadcms.com/docs/hooks/globals#afterchange)>) hook that allows you to modify the data or provide fallbacks before its search record is created or updated.
Before creating or updating a search record, the `beforeSync` function runs. This is an [afterChange](https://payloadcms.com/docs/hooks/globals#afterchange) hook that allows you to modify the data or provide fallbacks before its search record is created or updated.
```ts
// payload.config.ts
@@ -159,6 +160,14 @@ When `syncDrafts` is true, draft documents will be synced to search. This is fal
If true, will delete documents from search whose status changes to draft. This is true by default. You must have [Payload Drafts](https://payloadcms.com/docs/versions/drafts) enabled for this to apply.
#### `reindexBatchSize`
A number that, when specified, will be used as the value to determine how many search documents to fetch for reindexing at a time in each batch. If not set, this will default to `50`.
### Collection reindexing
Collection reindexing allows you to recreate search documents from your search-enabled collections on demand. This is useful if you have existing documents that don't already have search indexes, commonly when adding `plugin-search` to an existing project. To get started, navigate to your search collection and click the pill in the top right actions slot of the list view labelled `Reindex`. This will open a popup with options to select one of your search-enabled collections, or all, for reindexing.
## TypeScript
All types can be directly imported:

View File

@@ -6,7 +6,7 @@ desc: Integrate Sentry error tracking into your Payload application
keywords: plugins, sentry, error, tracking, monitoring, logging, bug, reporting, performance
---
[![NPM](https://img.shields.io/npm/v/@payloadcms/plugin-sentry)](https://www.npmjs.com/package/@payloadcms/plugin-sentry)
[![npm](https://img.shields.io/npm/v/@payloadcms/plugin-sentry)](https://www.npmjs.com/package/@payloadcms/plugin-sentry)
This plugin allows you to integrate [Sentry](https://sentry.io/) seamlessly with your [Payload](https://github.com/payloadcms/payload) application.
@@ -31,15 +31,15 @@ This multi-faceted software offers a range of features that will help you manage
- **Integrations**: Connects with various tools and services for enhanced workflow and issue management
<Banner type="info">
This plugin is completely open-source and the [source code can be found here](https://github.com/payloadcms/payload/tree/beta/packages/plugin-sentry). If you need help, check out our [Community Help](https://payloadcms.com/community-help). If you think you've found a bug, please [open a new issue](https://github.com/payloadcms/payload/issues/new?assignees=&labels=plugin%3A%20seo&template=bug_report.md&title=plugin-sentry%3A) with as much detail as possible.
This plugin is completely open-source and the [source code can be found here](https://github.com/payloadcms/payload/tree/main/packages/plugin-sentry). If you need help, check out our [Community Help](https://payloadcms.com/community-help). If you think you've found a bug, please [open a new issue](https://github.com/payloadcms/payload/issues/new?assignees=&labels=plugin%3A%20seo&template=bug_report.md&title=plugin-sentry%3A) with as much detail as possible.
</Banner>
## Installation
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
Install the plugin using any JavaScript package manager like [pnpm](https://pnpm.io), [npm](https://npmjs.com), or [Yarn](https://yarnpkg.com):
```bash
pnpm add @payloadcms/plugin-sentry@beta
pnpm add @payloadcms/plugin-sentry
```
## Sentry for Next.js setup

View File

@@ -6,7 +6,7 @@ desc: Manage SEO metadata from your Payload admin
keywords: plugins, seo, meta, search, engine, ranking, google
---
[![NPM](https://img.shields.io/npm/v/@payloadcms/plugin-seo)](https://www.npmjs.com/package/@payloadcms/plugin-seo)
[![npm](https://img.shields.io/npm/v/@payloadcms/plugin-seo)](https://www.npmjs.com/package/@payloadcms/plugin-seo)
This plugin allows you to easily manage SEO metadata for your application from within your [Admin Panel](../admin/overview). When enabled on your [Collections](../configuration/collections) and [Globals](../configuration/globals), it adds a new `meta` field group containing `title`, `description`, and `image` by default. Your front-end application can then use this data to render meta tags however your application requires. For example, you would inject a `title` tag into the `<head>` of your page using `meta.title` as its content.
@@ -34,10 +34,10 @@ To help you visualize what your page might look like in a search engine, a previ
## Installation
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
Install the plugin using any JavaScript package manager like [pnpm](https://pnpm.io), [npm](https://npmjs.com), or [Yarn](https://yarnpkg.com):
```bash
pnpm add @payloadcms/plugin-seo@beta
pnpm add @payloadcms/plugin-seo
```
## Basic Usage
@@ -171,7 +171,7 @@ A function that allows you to return any meta description, including from docume
}
```
For a full list of arguments, see the [`generateTitle`](#generateTitle) function.
For a full list of arguments, see the [`generateTitle`](#generatetitle) function.
##### `generateImage`
@@ -187,7 +187,7 @@ A function that allows you to return any meta image, including from document's c
}
```
For a full list of arguments, see the [`generateTitle`](#generateTitle) function.
For a full list of arguments, see the [`generateTitle`](#generatetitle) function.
##### `generateURL`
@@ -204,7 +204,7 @@ A function called by the search preview component to display the actual URL of y
}
```
For a full list of arguments, see the [`generateTitle`](#generateTitle) function.
For a full list of arguments, see the [`generateTitle`](#generatetitle) function.
#### `interfaceName`
@@ -277,7 +277,7 @@ Tip: You can override the length rules by changing the minLength and maxLength p
All types can be directly imported:
```ts
import {
import type {
PluginConfig,
GenerateTitle,
GenerateDescription
@@ -288,9 +288,9 @@ import {
You can then pass the collections from your generated Payload types into the generation types, for example:
```ts
import { Page } from './payload-types.ts';
import type { Page } from './payload-types.ts';
import { GenerateTitle } from '@payloadcms/plugin-seo/types';
import type { GenerateTitle } from '@payloadcms/plugin-seo/types';
const generateTitle: GenerateTitle<Page> = async ({ doc, locale }) => {
return `Website.com — ${doc?.title}`

View File

@@ -6,9 +6,9 @@ desc: Easily accept payments with Stripe
keywords: plugins, stripe, payments, ecommerce
---
[![NPM](https://img.shields.io/npm/v/@payloadcms/plugin-stripe)](https://www.npmjs.com/package/@payloadcms/plugin-stripe)
[![npm](https://img.shields.io/npm/v/@payloadcms/plugin-stripe)](https://www.npmjs.com/package/@payloadcms/plugin-stripe)
With this plugin you can easily integrate [Stripe](https://stripe.com) into Payload. Simply provide your Stripe credentials and this plugin will open up a two-way communication channel between the two platforms. This enables you to easily sync data back and forth, as well as proxy the Stripe REST API through Payload's [Access Control](../access-control). Use this plugin to completely offload billing to Stripe and retain full control over your application's data.
With this plugin you can easily integrate [Stripe](https://stripe.com) into Payload. Simply provide your Stripe credentials and this plugin will open up a two-way communication channel between the two platforms. This enables you to easily sync data back and forth, as well as proxy the Stripe REST API through Payload's [Access Control](../access-control/overview). Use this plugin to completely offload billing to Stripe and retain full control over your application's data.
For example, you might be building an e-commerce or SaaS application, where you have a `products` or a `plans` collection that requires either a one-time payment or a subscription. You can to tie each of these products to Stripe, then easily subscribe to billing-related events to perform your application's business logic, such as active purchases or subscription cancellations.
@@ -36,10 +36,10 @@ The beauty of this plugin is the entirety of your application's content and busi
## Installation
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
Install the plugin using any JavaScript package manager like [pnpm](https://pnpm.io), [npm](https://npmjs.com), or [Yarn](https://yarnpkg.com):
```bash
pnpm add @payloadcms/plugin-stripe@beta
pnpm add @payloadcms/plugin-stripe
```
## Basic Usage

View File

@@ -147,11 +147,11 @@ But, if you do, and you still want to use an ephemeral filesystem provider, you
Payload provides a list of official cloud storage adapters for you to use:
- [Azure Blob Storage](https://github.com/payloadcms/payload/tree/beta/packages/storage-azure)
- [Google Cloud Storage](https://github.com/payloadcms/payload/tree/beta/packages/storage-gcs)
- [AWS S3](https://github.com/payloadcms/payload/tree/beta/packages/storage-s3)
- [Uploadthing](https://github.com/payloadcms/payload/tree/beta/packages/storage-uploadthing)
- [Vercel Blob Storage](https://github.com/payloadcms/payload/tree/beta/packages/storage-vercel-blob)
- [Azure Blob Storage](https://github.com/payloadcms/payload/tree/main/packages/storage-azure)
- [Google Cloud Storage](https://github.com/payloadcms/payload/tree/main/packages/storage-gcs)
- [AWS S3](https://github.com/payloadcms/payload/tree/main/packages/storage-s3)
- [Uploadthing](https://github.com/payloadcms/payload/tree/main/packages/storage-uploadthing)
- [Vercel Blob Storage](https://github.com/payloadcms/payload/tree/main/packages/storage-vercel-blob)
Follow the docs to configure any one of these storage providers. For local development, it might be handy to simply store uploads on your own computer, and then when it comes to production, simply enable the plugin for the cloud storage vendor of your choice.
@@ -160,7 +160,19 @@ Follow the docs to configure any one of these storage providers. For local devel
This is an example of a multi-stage docker build of Payload for production. Ensure you are setting your environment
variables on deployment, like `PAYLOAD_SECRET`, `PAYLOAD_CONFIG_PATH`, and `DATABASE_URI` if needed.
In your Next.js config, set the `output` property `standalone`.
```js
// next.config.js
const nextConfig = {
output: 'standalone',
}
```
Dockerfile
```dockerfile
# Dockerfile
# From https://github.com/vercel/next.js/blob/canary/examples/with-docker/Dockerfile
FROM node:18-alpine AS base

View File

@@ -43,7 +43,9 @@ But with a `depth` of `1`, the response might look like this:
To specify depth in the [Local API](../local-api/overview), you can use the `depth` option in your query:
```ts
const getPosts = async () => {
import type { Payload } from 'payload'
const getPosts = async (payload: Payload) => {
const posts = await payload.find({
collection: 'posts',
depth: 2, // highlight-line

View File

@@ -19,7 +19,9 @@ Each of these APIs share the same underlying querying language, and fully suppor
To query your Documents, you can send any number of [Operators](#operators) through your request:
```ts
const query = {
import type { Where } from 'payload'
const query: Where = {
color: {
equals: 'blue',
},
@@ -37,21 +39,23 @@ _The exact query syntax will depend on the API you are using, but the concepts a
The following operators are available for use in queries:
| Operator | Description |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `equals` | The value must be exactly equal. |
| `not_equals` | The query will return all documents where the value is not equal. |
| `greater_than` | For numeric or date-based fields. |
| `greater_than_equal` | For numeric or date-based fields. |
| `less_than` | For numeric or date-based fields. |
| `less_than_equal` | For numeric or date-based fields. |
| `like` | Case-insensitive string must be present. If string of words, all words must be present, in any order. |
| `contains` | Must contain the value entered, case-insensitive. |
| `in` | The value must be found within the provided comma-delimited list of values. |
| `not_in` | The value must NOT be within the provided comma-delimited list of values. |
| `all` | The value must contain all values provided in the comma-delimited list. |
| `exists` | Only return documents where the value either exists (`true`) or does not exist (`false`). |
| `near` | For distance related to a [Point Field](../fields/point) comma separated as `<longitude>, <latitude>, <maxDistance in meters (nullable)>, <minDistance in meters (nullable)>`. |
| Operator | Description |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `equals` | The value must be exactly equal. |
| `not_equals` | The query will return all documents where the value is not equal. |
| `greater_than` | For numeric or date-based fields. |
| `greater_than_equal` | For numeric or date-based fields. |
| `less_than` | For numeric or date-based fields. |
| `less_than_equal` | For numeric or date-based fields. |
| `like` | Case-insensitive string must be present. If string of words, all words must be present, in any order. |
| `contains` | Must contain the value entered, case-insensitive. |
| `in` | The value must be found within the provided comma-delimited list of values. |
| `not_in` | The value must NOT be within the provided comma-delimited list of values. |
| `all` | The value must contain all values provided in the comma-delimited list. |
| `exists` | Only return documents where the value either exists (`true`) or does not exist (`false`). |
| `near` | For distance related to a [Point Field](../fields/point) comma separated as `<longitude>, <latitude>, <maxDistance in meters (nullable)>, <minDistance in meters (nullable)>`. |
| `within` | For [Point Fields](../fields/point) to filter documents based on whether points are inside of the given area defined in GeoJSON. [Example](../fields/point#querying-within) |
| `intersects` | For [Point Fields](../fields/point) to filter documents based on whether points intersect with the given area defined in GeoJSON. [Example](../fields/point#querying-intersects) |
<Banner type="success">
<strong>Tip:</strong>
@@ -65,7 +69,9 @@ In addition to defining simple queries, you can join multiple queries together u
To join queries, use the `and` or `or` keys in your query object:
```ts
const query = {
import type { Where } from 'payload'
const query: Where = {
or: [ // highlight-line
{
color: {
@@ -97,7 +103,9 @@ Written in plain English, if the above query were passed to a `find` operation,
When working with nested properties, which can happen when using relational fields, it is possible to use the dot notation to access the nested property. For example, when working with a `Song` collection that has a `artists` field which is related to an `Artists` collection using the `name: 'artists'`. You can access a property within the collection `Artists` like so:
```js
const query = {
import type { Where } from 'payload'
const query: Where = {
'artists.featured': {
// nested property name to filter on
exists: true, // operator to use and boolean value that needs to be true
@@ -114,7 +122,9 @@ Writing queries in Payload is simple and consistent across all APIs, with only m
The [Local API](../local-api/overview) supports the `find` operation that accepts a raw query object:
```ts
const getPosts = async () => {
import type { Payload } from 'payload'
const getPosts = async (payload: Payload) => {
const posts = await payload.find({
collection: 'posts',
where: {
@@ -151,23 +161,24 @@ With the [REST API](../rest-api/overview), you can use the full power of Payload
To understand the syntax, you need to understand that complex URL search strings are parsed into a JSON object. This one isn't too bad, but more complex queries get unavoidably more difficult to write.
For this reason, we recommend to use the extremely helpful and ubiquitous [`qs`](https://www.npmjs.com/package/qs) package to parse your JSON / object-formatted queries into query strings:
For this reason, we recommend to use the extremely helpful and ubiquitous [`qs-esm`](https://www.npmjs.com/package/qs-esm) package to parse your JSON / object-formatted queries into query strings:
```ts
import { stringify } from 'qs-esm'
import type { Where } from 'payload'
const query = {
const query: Where = {
color: {
equals: 'mint',
},
// This query could be much more complex
// and QS would handle it beautifully
// and qs-esm would handle it beautifully
}
const getPosts = async () => {
const stringifiedQuery = stringify(
{
where: query, // ensure that `qs` adds the `where` property, too!
where: query, // ensure that `qs-esm` adds the `where` property, too!
},
{ addQueryPrefix: true },
)

View File

@@ -6,15 +6,19 @@ desc: Payload select determines which fields are selected to the result.
keywords: query, documents, pagination, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
You may not need the full data from your Local API / REST queries, but only some specific fields. The select fields API can help you to optimize those cases.
By default, Payload's APIs will return _all fields_ for a given collection or global. But, you may not need all of that data for all of your queries. Sometimes, you might want just a few fields from the response, which can speed up the Payload API and reduce the amount of JSON that is sent to you from the API.
This is where Payload's `select` feature comes in. Here, you can define exactly which fields you'd like to retrieve from the API.
## Local API
To specify select in the [Local API](../local-api/overview), you can use the `select` option in your query:
To specify `select` in the [Local API](../local-api/overview), you can use the `select` option in your query:
```ts
import type { Payload } from 'payload'
// Include mode
const getPosts = async () => {
const getPosts = async (payload: Payload) => {
const posts = await payload.find({
collection: 'posts',
select: {
@@ -32,7 +36,7 @@ const getPosts = async () => {
}
// Exclude mode
const getPosts = async () => {
const getPosts = async (payload: Payload) => {
const posts = await payload.find({
collection: 'posts',
// Select everything except for array and group.number
@@ -51,7 +55,7 @@ const getPosts = async () => {
<Banner type="warning">
<strong>Important:</strong>
To perform querying with `select` efficiently, it works on the database level. Because of that, your `beforeRead` and `afterRead` hooks may not receive the full `doc`.
To perform querying with `select` efficiently, Payload implements your `select` query on the database level. Because of that, your `beforeRead` and `afterRead` hooks may not receive the full `doc`.
</Banner>
@@ -67,12 +71,13 @@ fetch('https://localhost:3000/api/posts?select[color]=true&select[group][number]
To understand the syntax, you need to understand that complex URL search strings are parsed into a JSON object. This one isn't too bad, but more complex queries get unavoidably more difficult to write.
For this reason, we recommend to use the extremely helpful and ubiquitous [`qs`](https://www.npmjs.com/package/qs) package to parse your JSON / object-formatted queries into query strings:
For this reason, we recommend to use the extremely helpful and ubiquitous [`qs-esm`](https://www.npmjs.com/package/qs-esm) package to parse your JSON / object-formatted queries into query strings:
```ts
import { stringify } from 'qs-esm'
import type { Where } from 'payload'
const select = {
const select: Where = {
text: true,
group: {
number: true
@@ -100,17 +105,20 @@ const getPosts = async () => {
</Banner>
## `defaultPopulate` collection config property
## defaultPopulate collection config property
The `defaultPopulate` property allows you specify which fields to select when populating the collection from another document.
This is especially useful for links where only the `slug` is needed instead of the entire document.
With this feature, you can dramatically reduce the amount of JSON that is populated from [Relationship](/docs/fields/relationship) or [Upload](/docs/fields/upload) fields.
For example, in your content model, you might have a `Link` field which links out to a different page. When you go to retrieve these links, you really only need the `slug` of the page.
Loading all of the page content, its related links, and everything else is going to be overkill and will bog down your Payload APIs. Instead, you can define the `defaultPopulate` property on your `Pages` collection, so that when Payload "populates" a related Page, it only selects the `slug` field and therefore returns significantly less JSON:
```ts
import type { CollectionConfig } from 'payload'
import { lexicalEditor, LinkFeature } from '@payloadcms/richtext-lexical'
import { slateEditor } from '@payloadcms/richtext-slate'
// The TSlug generic can be passed to have type safety for `defaultPopulate`.
// If avoided, the `defaultPopulate` type resolves to `SelectType`.
export const Pages: CollectionConfig<'pages'> = {
@@ -128,3 +136,37 @@ export const Pages: CollectionConfig<'pages'> = {
],
}
```
## populate
Setting `defaultPopulate` will enforce that each time Payload performs a "population" of a related document, only the fields specified will be queried and returned. However, you can override `defaultPopulate` with the `populate` property in the Local and REST API:
**Local API:**
```ts
import type { Payload } from 'payload'
const getPosts = async (payload: Payload) => {
const posts = await payload.find({
collection: 'posts',
populate: {
// Select only `text` from populated docs in the "pages" collection
// Now, no matter what the `defaultPopulate` is set to on the "pages" collection,
// it will be overridden, and the `text` field will be returned instead.
pages: {
text: true,
}, // highlight-line
},
})
return posts
}
```
**REST API:**
```ts
fetch('https://localhost:3000/api/posts?populate[pages][text]=true') // highlight-line
.then((res) => res.json())
.then((data) => console.log(data))
```

View File

@@ -20,7 +20,9 @@ Because sorting is handled by the database, the field cannot be a [Virtual Field
To sort Documents in the [Local API](../local-api/overview), you can use the `sort` option in your query:
```ts
const getPosts = async () => {
import type { Payload } from 'payload'
const getPosts = async (payload: Payload) => {
const posts = await payload.find({
collection: 'posts',
sort: '-createdAt', // highlight-line
@@ -33,7 +35,9 @@ const getPosts = async () => {
To sort by multiple fields, you can use the `sort` option with fields in an array:
```ts
const getPosts = async () => {
import type { Payload } from 'payload'
const getPosts = async (payload: Payload) => {
const posts = await payload.find({
collection: 'posts',
sort: ['priority', '-createdAt'], // highlight-line

View File

@@ -19,6 +19,7 @@ All Payload API routes are mounted and prefixed to your config's `routes.api` UR
- [locale](/docs/configuration/localization#retrieving-localized-docs) - retrieves document(s) in a specific locale
- [fallback-locale](/docs/configuration/localization#retrieving-localized-docs) - specifies a fallback locale if no locale value exists
- [select](../queries/select) - specifies which fields to include to the result
- [populate](../queries/select#populate) - specifies which fields to include to the result from populated documents
## Collections
@@ -500,7 +501,7 @@ Globals cannot be created or deleted, so there are only two REST endpoints opene
## Preferences
In addition to the dynamically generated endpoints above Payload also has REST endpoints to manage the admin user [preferences](/docs/admin/overview#preferences) for data specific to the authenticated user.
In addition to the dynamically generated endpoints above Payload also has REST endpoints to manage the admin user [preferences](/docs/admin/preferences) for data specific to the authenticated user.
<RestExamples
data={[

View File

@@ -9,7 +9,7 @@ keywords: slatejs, lexical, rich text, json, custom editor, javascript, typescri
Payload currently supports two official rich text editors and you can choose either one depending on your needs.
1. [SlateJS](/docs/rich-text/slate) - stable, backwards-compatible with 1.0
2. [Lexical](/docs/lexical/overview) - beta, where things will be moving in the future
2. [Lexical](/docs/lexical/overview) - recommended
These editors are built on an "adapter pattern" which means that you will need to install the editor you'd like to use. Take a look at the docs for the editor you'd like to use for instructions on how to install it.

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