Compare commits

...

152 Commits

Author SHA1 Message Date
Elliot DeNolf
69125504af chore(release): v3.0.0-beta.117 [skip ci] 2024-10-22 09:33:50 -04:00
Elliot DeNolf
74266bdd9a feat!: bump next.js to 15.0.0 (#8825)
Bump Next.js to 15.0.0
2024-10-21 23:12:22 -04:00
Elliot DeNolf
9fb86655a9 fix: drizzle init args (#8818)
Adjust drizzle init for changes in drizzle 0.35.0
https://github.com/drizzle-team/drizzle-orm/releases/tag/0.35.0

The pool/connection should now be passed as the `client` arg when
initializing drizzle.

```ts
this.drizzle = drizzle({
  client: this.poolOptions ? new VercelPool(this.poolOptions) : sql,
  logger,
  schema: this.schema,
})
```

This was causing an issue where running `payload migrate` on Vercel was
causing drizzle to attempt to `127.0.0.1:5432` instead of the specified
environment variable in the adapter 🤔
2024-10-21 21:39:37 -04:00
Patrik
2908c9adde fix(next, ui): ensures selectAll in the list view ignores locked documents (#8813)
Fixes #8783
2024-10-21 16:18:34 -04:00
Sasha
fe25b54fff fix(ui): unique ids for nested rows on row duplicate to prevent errors with postgres (#8790)
Fixes https://github.com/payloadcms/payload/issues/8784

This works for any deep level, both arrays and blocks.
2024-10-21 08:51:26 -04:00
Anders Semb Hermansen
ef8a5b1f3e perf: replace jsonwebtoken with jose (#8217)
The jose package has 0 dependencies and is tree shakable ESM.
So we get lower bundle size and get rid of 10 dependencies.
2024-10-18 10:04:37 -06:00
Sasha
197e3bc010 docs: corrects old imports (#8769)
1
`import type { Field } from 'payload/types'`
to
`import type { Field } from 'payload'`
2
`import { buildConfig } from 'payload/config'`
to
`import { buildConfig } from 'payload'`

3
```
import { SelectInput, useField } from 'payload/components/forms';
import { useAuth } from 'payload/components/utilities';
```
to
`import { SelectInput, useAuth, useField } from '@payloadcms/ui'`

4
uses `import type` for `import type { CollectionConfig } from 'payload'`
2024-10-18 10:47:47 +03:00
Because789
36acfee288 docs: fix missing TextField import (#8688)
Fixes a missing import in field prop example in
docs/beta/admin/fields.mdx.

<!--

For external contributors, please include:

- A summary of the pull request and any related issues it fixes.
- Reasoning for the changes made or any additional context that may be
useful.

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

 -->
2024-10-18 10:47:31 +03:00
Alessio Gravili
062a333779 perf: upgrade pino and pino-pretty, reducing the total amount of dependencies (#8776)
BEFORE:

22 dependencies, 2MB total size

![CleanShot 2024-10-18 at 00 11
48@2x](https://github.com/user-attachments/assets/abd20ac1-fc66-4d5b-bbda-bdcf89846a0a)

AFTER:

12 dependencies, 1MB total size

![CleanShot 2024-10-18 at 00 12
44@2x](https://github.com/user-attachments/assets/6f22e2e3-0eed-4b48-8e51-1f5156e9efd3)
2024-10-18 06:58:56 +00:00
Alessio Gravili
fa929120e7 chore: upgrade typescript from 5.6.2 to 5.6.3, upgrade playwright from 1.46.0 to 1.48.1 (#8775) 2024-10-18 06:09:51 +00:00
Alessio Gravili
f3bec93d76 fix(richtext-lexical): richtext fields in drawers aren't editable, inline toolbar artifacts are shown for readOnly editors (#8774)
Fixes this:


https://github.com/user-attachments/assets/cf78082d-9054-4324-90cd-c81219a4f26d
2024-10-18 05:38:48 +00:00
Germán Jabloñski
fa49215078 chore: add internationalization guidelines to CONTRIBUTING.md (#8755) 2024-10-18 03:50:31 +00:00
Alessio Gravili
aedf3c8a76 fix(richtext-*): ensure admin panel doesn't freeze with some field configurations consisting of 2+ richtext fields (#8773)
See comments in code for proper explanation. In some cases, where 2
richtext `editor`s referencing the same `editor` are used, the admin
panel will hang. That's because the server will send their client props
that have the same object reference down to the client twice.

Next.js sometimes does not like this and, ever since one of the v15
canaries, started to hang
2024-10-18 03:22:05 +00:00
Sasha
9056b9fe9b fix(db-mongodb): virtual fields within row / collapsible / tabs (#8733)
Fixes https://github.com/payloadcms/payload/issues/8674
2024-10-17 16:23:45 -04:00
Paul
82f278931b fix(ui): padding on relationship fields when no options or loading text is displayed (#8767) 2024-10-17 19:46:28 +00:00
Germán Jabloñski
a7895560a6 fix(richtext-lexical): fix CLS on collapsed/expanded state of Lexical blocks (#8029)
## Description

The goal is to reduce CLS on collapsed/expanded state of Lexical blocks.
That state is stored as "preferences" and is different for each user.
As Payload has been working so far, if the state of a Lexical block was
"collapsed", it was rendered expanded, and when the correct state was
obtained from the server, it was collapsed producing a CLS with a poor
UX.

My original idea was to get the correct state on the first render.
Talking to @AlessioGr and @jmikrut, we saw that this can be a bit
difficult or challenging, since the feature on the server does not have
access to the Payload object, nor to the user who is making the request.

I was instructed to mimic the behavior of blocks not in Lexial
(`\ui\src\fields\Collapsible\index.tsx`). There the blocks are rendered
after the collapse/expand state is obtained in a useEffect.

In the following video, the case where the first block is collapsed is
shown, rendering everything with a "fast 4G" connection throttle.


https://github.com/user-attachments/assets/078e37c7-6540-4183-a266-bd751cc9d78e

Yes, it's a slight improvement over current behavior. But it could be
much better. There are request waterfalls several levels deep, and
plenty of CLS still.

Unless there is some very big tradeoff that I'm not aware of, I think
it's worth exposing the Payload object and the user to the server in
order to get the correct state on the first render.

And if that's not possible and the request has to be made on the client,
I think initializing the state as collapsed and then expanding it is
better than not showing it at all.

Another observation that is evident from the video, is that there are
several sources or causes of CLS besides the expanded/collapsed state of
the blocks.

- [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] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
- [x] I have made corresponding changes to the documentation
2024-10-17 19:36:14 +00:00
Paul
1f0d8da182 fix(plugin-seo): description and title fields now respect given minLength and maxLength rules for passing validation (#8765)
Previously minLength or maxLength was not being respected and the
components would use default values only.
2024-10-17 19:28:54 +00:00
Said Akhrarov
c91f21bb78 docs: fix incorrect link for outside-nextjs in local-api importing it section (#8764)
Currently in the `beta` docs at the bottom of [Local API Overview Import
It
section](https://payloadcms.com/docs/beta/local-api/overview#importing-it)
there is a link for _Outside Nextjs_ which incorrectly sends you to
`/docs/beta/beta/local-api/outside-nextjs` instead of
`docs/beta/local-api/outside-nextjs`.

Interestingly enough, a `Not Found` component/message is not rendered
and instead you see a blank screen.

---------

Co-authored-by: Sasha <64744993+r1tsuu@users.noreply.github.com>
2024-10-17 13:13:37 -06:00
Elliot DeNolf
7136515f8d chore(release): v3.0.0-beta.116 [skip ci] 2024-10-17 09:05:45 -04:00
Sasha
73102e97fe fix(drizzle): bump drizzle-orm in drizzle package to 0.35.1 (#8759)
Fixes https://github.com/payloadcms/payload/issues/8757. Other packages
have `drizzle-orm` `0.35.1`, but this does not (`0.34.1-1f15bfd`).
2024-10-17 07:14:02 -04:00
Sasha
f37e476758 fix(db-postgres): esm compatible import of pg (#8758)
Fixes this error that doesn't occur in our monorepo but on users
projects.
<img width="1040" alt="image"
src="https://github.com/user-attachments/assets/6b410959-9108-4022-ae0a-64bc4be6de67">
2024-10-17 10:02:30 +00:00
Sasha
90bca15f52 fix(drizzle): enforce uniqueness on index names (#8754)
Fixes https://github.com/payloadcms/payload/issues/8752

Previously, trying to define a config like this:
```ts
{
  type: 'text',
  name: 'someText',
  index: true,
},
{
  type: 'array',
  name: 'some',
  index: true,
  fields: [
    {
      type: 'text',
      name: 'text',
      index: true,
    },
  ],
}
```

Lead to the error:
```
Warning  We've found duplicated index name across public schema. Please rename your index in either the demonstration table or the table with the duplicated index name
```

Now, if we encounter duplicates, we increment the name like this:
`collection_some_text_idx`
`collection_some_text_1_idx`

---------

Co-authored-by: Dan Ribbens <dan.ribbens@gmail.com>
2024-10-17 02:05:27 +00:00
Sasha
872b205acc fix(drizzle): select hasMany nested to array + tab/group (#8737)
Fixes https://github.com/payloadcms/payload/issues/8732
2024-10-16 21:52:48 -04:00
Jarrod Flesch
99b4359e89 fix: pg where filters not respected in policy generation (#8753)
Fixes https://github.com/payloadcms/payload/issues/8224

Fixes an issue with PG `where` filters not being respected when
generating doc policies/permissions by utilizing the combineQueries
function in getEntityPolicies function.
2024-10-16 16:54:23 -04:00
Dan Ribbens
b269d33278 chore: bump drizzle-kit 0.26.2 (#8750) 2024-10-16 16:32:54 -04:00
Patrik
c63b7bc606 fix: disables document locking of payload-locked-documents, preferences, & migrations collections (#8744)
Fixes #8589 

### Issue: 
There were problems with updating documents in
`payload-locked-documents` collection i.e when "taking over" a document
- a `patch` request is sent to `payload-locked-documents` to update the
user (owner).

However, as a result, this `update` operation would lock that
corresponding doc in `payload-locked-documents` and therefore error on
the `patch` request.

### Fix:
Disable document locking entirely from `payload-locked-documents` &
`preferences` & `migrations` collections
2024-10-16 14:46:39 -04:00
Elliot DeNolf
0fb92d3a0a chore(release): v3.0.0-beta.115 [skip ci] 2024-10-16 14:20:27 -04:00
Jarrod Flesch
99228b62ce chore: improves getLatestCollectionVersion where constraints (#8746) 2024-10-16 14:12:43 -04:00
Dan Ribbens
7019f22aad chore: bump drizzle-orm 0.35.1 (#8742) 2024-10-16 15:14:20 +00:00
Said Akhrarov
c4fa885e84 fix(ui): restrict file picking via upload config mimetypes (#8710)
Fixes #8673

This PR restricts inputs with `type="file"` to only those mimetypes
specified in collection upload configs. This also works for the input in
`bulkUpload` and drag-and-drop capabilities by omitting dropped files if
they do not conform to the upload config mimetypes. This PR also assumes
that an upload config with an empty mimetype array should accept all
files since the negation of that statement makes an upload collection
redundant.
2024-10-16 09:24:21 -04:00
Jarrod Flesch
a9c6a91c1c chore(examples): correct where path comes from in multi-tenant (#8698) 2024-10-16 08:28:28 -04:00
Jarrod Flesch
a19e8d382d fix: corrects schemaPath passing (#8726)
Fixes https://github.com/payloadcms/payload/issues/8690

Fixes an issue where unrelated schema paths were being joined and passed
into nested sanitizeFields function calls.
2024-10-16 06:59:37 -04:00
Paul
e6a1ca5049 fix(ui): add missing styles under the payload-default css layer (#8723) 2024-10-16 01:58:50 +00:00
Dan Ribbens
6d0676ab09 chore: bump drizzle-kit 0.26.1 (#8721) 2024-10-15 21:12:40 +00:00
Dan Ribbens
93545f3103 fix(db-postgres, db-sqlite)!: bump drizzle-kit drizzle-orm @libsql/client (#8617)
Inheriting all the fixes from drizzle moving to latest versions

## BREAKING CHANGES
If you have a prior version of @libsql/client installed in your project,
you must upgrade to 0.14.0
2024-10-15 20:20:50 +00:00
Patrik
8d10737b2f fix(ui): removes overflow: hidden style from actions wrapper (#8717)
`Before`:
![Screenshot 2024-10-15 at 2 55
22 PM](https://github.com/user-attachments/assets/39b261c4-bb72-4497-ab47-01d430255773)

`After`:
![Screenshot 2024-10-15 at 2 55
07 PM](https://github.com/user-attachments/assets/8913479a-9e0b-4edf-baa1-130dc179af34)
2024-10-15 15:58:36 -04:00
Paul
303a224072 fix(templates): website template issue with previewing drafts (#8714)
Fixes https://github.com/payloadcms/payload/issues/8708
2024-10-15 15:10:34 +00:00
Elliot DeNolf
2315719c28 chore(deps): bump turbo 2024-10-15 09:58:28 -04:00
Elliot DeNolf
85e87c15fa chore(release): v3.0.0-beta.114 [skip ci] 2024-10-15 09:51:54 -04:00
Jacob Fletcher
35a5199c87 fix(next): returns proper document id type from init page result (#8700) 2024-10-14 19:39:30 -04:00
Patrik
3f2b828298 fix(drizzle, ui): properly filters out number field values with the exists operator filter (#8706)
Filtering by `null` `number` field values or normal values with the
`exists` operator was not working in `postgres` & `sqlite`.

Was previously fixed for `mongodb`
[here](https://github.com/payloadcms/payload/pull/8416)

Now fixed for `postgres` & `sqlite` adapters as well.
2024-10-14 19:23:22 -04:00
Jessica Chowdhury
a9e7d4884e fix: ensure beforeValidate hook runs for reset password (#8433)
Adds `beforeValidate` hook to reset password operation.

**Closes** https://github.com/payloadcms/payload/issues/7582
2024-10-14 19:12:28 -04:00
Dan Ribbens
3110c1b01b fix: local update limit (#8704) 2024-10-14 19:05:13 -04:00
Alessio Gravili
f1ebf56691 chore: when dev server restarts, ensure exiting the parent process kills child process (#8703) 2024-10-14 20:02:26 +00:00
Paul
e3957d746b fix(ui): react-datepicker in number fields and datepicker visual issues (#8694)
Fixes https://github.com/payloadcms/payload/issues/8683

Number fields now display correctly 

![image](https://github.com/user-attachments/assets/edf5e8d9-2292-49d2-99b7-c10e940b2a49)

Fixes issue with react-datepicker by manually copying in the css of the
library so it can be layered in

![image](https://github.com/user-attachments/assets/fcaec51a-5914-4fb5-be12-7fa2cf44a176)
2024-10-14 13:20:52 -06:00
Dan Ribbens
d781624a86 feat: add disableTransaction to local api (#8697) 2024-10-14 14:39:20 -04:00
Patrik
08f883166e fix(ui): prevents react-select styles being injected into hasMany number fields (#8695)
Before:
![Screenshot 2024-10-14 at 1 35
16 PM](https://github.com/user-attachments/assets/0b121693-1160-493c-8c73-638394fe542e)

After:
![Screenshot 2024-10-14 at 1 35
30 PM](https://github.com/user-attachments/assets/2c73d345-30b1-49ba-826b-fb20066a2e43)
2024-10-14 14:36:47 -04:00
Jarrod Flesch
8bbc833812 fix: validate should not always equate valid to true (#8696)
In some instances, form states incorrectly setting valid to true even
when they should not be, just because no validate function is present.

This was apparent when using bulk upload drawers inside the multi-tenant
example which inserts a custom field for the TenantSelector on
documents.

Reported internally
https://payloadcms.slack.com/archives/C079W6WT0R1/p1726670927732309
2024-10-14 14:29:37 -04:00
Said Akhrarov
65d4f37dfb fix(ui): show configured description on active tabs (#8675)
<!--

For external contributors, please include:

- A summary of the pull request and any related issues it fixes.
- Reasoning for the changes made or any additional context that may be
useful.

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

 -->
fixes #8672
2024-10-14 17:51:52 +00:00
Jarrod Flesch
3f128c5b28 fix: ensure dropzone respects canCreate permissions (#8689)
Prioritize user permissions for upload creation instead of only looking
at `admin.allowCreate`
2024-10-14 11:35:51 -04:00
Dan Ribbens
e4fd1e3e0b fix(ui): export useListRelationships (#8658)
fixes https://github.com/payloadcms/payload/issues/8568
2024-10-14 11:25:49 -04:00
Paul
a4f1af48b4 fix(ui): hasMany selecting single docs clicking through instead of selecting a document (#8624)
Fixes https://github.com/payloadcms/payload/issues/8500
2024-10-14 08:52:00 -06:00
Alessio Gravili
793dfe87e7 fix: ensure Component is not required in MappedComponent types (#8687)
Component is not required if RenderedComponent is passed
2024-10-14 14:46:29 +00:00
Because789
855fcf34bc docs: correct useTranslation import in i18n docs (#8645) 2024-10-14 09:08:09 -04:00
Sasha
6be665f887 chore(eslint): payload-logger proper usage works with template literals (#8660)
Improves this https://github.com/payloadcms/payload/pull/8578 to work
with template literals as well,

This is invalid:
```ts
this.payload.logger.error('Failed to create database', err)
```

But this is valid:
```ts
this.payload.logger.error(`Failed to create database ${dbName}`, err)
```

This PR fixes that

<img width="577" alt="image"
src="https://github.com/user-attachments/assets/b867da53-4f81-4b1b-8423-598e0419946a">
2024-10-13 19:10:47 -04:00
Alessio Gravili
c731940239 chore: use custom jest reporter to achieve sane jest logs (#8607)
The default jest log reporter is garbage. Webstorm replaces it with
their own (which is pretty good), but vscode unfortunately uses the
default one.

This PR does the following to the jest reporter

**1. Replace the default reporter with the jest-ci-spec-reporter
reporter.**

The default reporter is hiding console logs and incorrectly rewriting
console history. Now, logs like these:

```
[20:56:16] INFO: ---- DROPPING DATABASE ----
[20:56:17] INFO: ---- DROPPED DATABASE ----
```

will be visible again. The default reporter was showing them for half a
second, then rewrites log history and hides them.

**2. add custom logger to showcase hidden error messages**

Some error messages are hidden and are only displayed at the end of the
test, in a very ugly way. If the test hangs, you might have to wait a
long time to see those errors. This PR makes sure that errors are logged
when they were intended to be logged.

They will not be printed in a pretty way (Webstorm for example prints
them in red and clickable, like a proper error message) - but at least
they will be printed instead of leaving you in the dark

**Override console global in jest setup to hide console log spam**

This turns logs like

```
  console.log
    initPayloadInt done

      at log (helpers/initPayloadInt.ts:27:11)
```
      
 into
 
`initPayloadInt done`

## CI

Yes, CI logs are actually usable now. We no longer have random console
logs floating around! It was horrible!

Finally, you can actually see which console logs belong to which test.

Before:
https://github.com/payloadcms/payload/actions/runs/11241674859/job/31253918825
After:
https://github.com/payloadcms/payload/actions/runs/11242035327/job/31255031760?pr=8607

**BEFORE**
![CleanShot 2024-10-08 at 21 27
23@2x](https://github.com/user-attachments/assets/7c83ced7-b4fd-4e90-95ff-2c240829c3cd)

What test triggered this "ValidationError: The following field is
invalid: filteredRelation" error? Who knows!! Could have been any test.
We will never know...

**AFTER:**

Finally, clarity 

![CleanShot 2024-10-08 at 21 28
15@2x](https://github.com/user-attachments/assets/a259950e-3213-4faa-9f87-e54fd970f6dc)

## Screenshots - Passing database test suite

## Passing database test suite

### Before
![CleanShot 2024-10-08 at 21 07
39@2x](https://github.com/user-attachments/assets/00a07d30-fbeb-4a52-8982-3e0bc198e278)
![CleanShot 2024-10-08 at 21 08
05@2x](https://github.com/user-attachments/assets/0bc02982-83e4-4205-a1e9-0c0277390ab2)

### After
![CleanShot 2024-10-08 at 21 06
52@2x](https://github.com/user-attachments/assets/cd1e6ac1-17c0-4859-a374-2176e698784e)

## Screenshots - Failing test

### Before - that's where it hangs
![CleanShot 2024-10-08 at 21 09
52@2x](https://github.com/user-attachments/assets/088b1074-bd57-4d9d-8de4-81f1a5edf407)

### After - that's where it hangs

Actually shows me the error without having to wait 3 minutes for test to
timeout:

![CleanShot 2024-10-08 at 21 13
13@2x](https://github.com/user-attachments/assets/ec91f530-2f5e-4b6d-872a-f483b9a421f4)


### Before - after waiting for 3 minutes for test to timeout:

![CleanShot 2024-10-08 at 21 12
08@2x](https://github.com/user-attachments/assets/64ac9945-3a3c-4eb5-991c-943859500bb5)
(1000 lines of same error spam...)
![CleanShot 2024-10-08 at 21 19
28@2x](https://github.com/user-attachments/assets/ccd33c38-f6d9-47a8-8c5a-41c118cfe849)

### After - after waiting for 3 minutes for test to timeout:

![CleanShot 2024-10-08 at 21 14
54@2x](https://github.com/user-attachments/assets/c2240305-21da-4b4c-9e28-ee68f8b2899d)
![CleanShot 2024-10-08 at 21 15
09@2x](https://github.com/user-attachments/assets/d6f7fab6-acd4-4bcc-a560-9e86792fdbbf)
(Error spam)
![CleanShot 2024-10-08 at 21 15
20@2x](https://github.com/user-attachments/assets/6be43e88-f881-4598-bb32-d7cfc90ef710)
2024-10-11 18:54:39 +00:00
Patrik
21606ded08 fix(db-mongodb): add validation to relationship ids (#8395)
fixes https://github.com/payloadcms/payload/issues/8652
2024-10-11 13:49:07 -04:00
Sasha
7a0b419c10 feat: add limit property to bulk update operation (#8656)
Adds `limit` to `payload.update`(bulk)  / REST
2024-10-11 13:14:18 -04:00
Sasha
1ffb6c3e13 fix(drizzle): indexes / unique with relationships (#8432)
Fixes https://github.com/payloadcms/payload/issues/8413 and
https://github.com/payloadcms/payload/issues/6460

- Builds indexes for relationships by default in the SQL schema
- Fixes `unique: true` handling with Postgres / SQLite for every type of
relationships (non-polymorphic. hasMany, polymorphic, polymorphic
hasMany) _note_: disables unique for nested to arrays / blocks
relationships in the `_rels` table.
- adds tests

2.0 PR tha ports only indexes creation
https://github.com/payloadcms/payload/pull/8446, because `unique: true`
could be a breaking change if someone has incosistent unique data in the
database.
2024-10-11 13:13:54 -04:00
Sasha
256949e331 feat(db-postgres, db-vercel-postgres): createDatabase, auto database create if it does not exist (#8655)
Adds `createDatabase` method to Postgres adapters which can be used
either independently like this:
```ts
payload.db.createDatabase({
  name: "some-database",
  schemaName: "custom-schema"
})
```
Or
```ts
payload.db.createDatabase()
```
Which creates a database from the current configuration, this is used in
`connect` if `autoDatabaseCreate` is set to `true` (default).
You can disable this behaviour with:
```ts
postgresAdapter({ autoDatabaseCreate: false })
```

Example:
<img width="470" alt="image"
src="https://github.com/user-attachments/assets/8d08c79d-9672-454c-af0f-eb802f9dcd99">
2024-10-11 14:17:32 +00:00
Sasha
8daac4e670 fix: properly store timestamps in versions (#8646)
This PR makes a more clear gap between `version_createdAt` /
`version_updatedAt` and `createdAt` / `updatedAt` columns / fields in
mongodb.

- `createdAt` - This should be a new value in a new version. Before this
change it was the same all the time. Should remain the same on autosave.
- The same for `updatedAt`, but it should be updated on every change
(including autosave)
- `version_createdAt` - Should remain equal to `createdAt` from the
parent collection / table
- `version_updatedAt` - On a latest version it makes sense this be the
same as `updatedAt` from the parent collection / table, as all the
`version_*` fields should be just synced with it
2024-10-11 10:01:21 -04:00
Elliot DeNolf
067d353cdd chore(release): v3.0.0-beta.113 [skip ci] 2024-10-10 16:42:05 -04:00
Elliot DeNolf
a15765395d chore: add plugin-sentry to publish list 2024-10-10 16:40:01 -04:00
Paul
d0a5560629 fix: commonjs exports missing for withPayload (#8643)
Closes https://github.com/payloadcms/payload/issues/8635

`withPayload.cjs` is now correctly named in the exports

The final exports in package.json looks like this
```
"./withPayload": {
  "import": "./dist/withPayload.js",
  "require": "./dist/cjs/withPayload.cjs",
  "default": "./dist/withPayload.js"
},
```

You can now use withPayload with require inside `next.config.js` files
```
const { withPayload } = require('@payloadcms/next/withPayload')

const nextConfig = {
  // Your Next.js config here
  experimental: {
    reactCompiler: false,
  },
}

module.exports = withPayload(nextConfig)
```
2024-10-10 15:25:17 -04:00
Jarrod Flesch
aec4d7b8d5 chore: improve i18n docs (#8638)
Fixes https://github.com/payloadcms/payload/issues/8632
2024-10-10 13:12:54 -04:00
Sasha
fdebc84d4f fix: sanitize virtual fields in admin.useAsTitle (#8620)
Fixes https://github.com/payloadcms/payload/issues/8525. Disallows to
set `admin.useAsTitle` with a virtual field. Updates docs / jsdoc
accordingly
2024-10-10 13:11:44 -04:00
Sasha
f6acfdb1f5 fix(drizzle): hasMany joins - localized, limit and schema paths (#8633)
Fixes https://github.com/payloadcms/payload/issues/8630

- Fixes `hasMany: true` and `localized: true` on the foreign field
- Adds `limit` to the subquery instead of hardcoded `11`.
- Adds the schema path `field.on` to the subquery, without this having 2
or more relationship fields to the same collection breaks joins
- Properly checks if the field is `hasMany`
2024-10-10 12:58:30 -04:00
Germán Jabloñski
1dcae37e58 chore: add instructions to run the examples to the readme (#8623) 2024-10-10 09:50:32 -04:00
Sasha
a3bf938862 chore: do not use withSentryConfig for dev/e2e next config if not needed (#8629)
This fixes this error when executing particular e2e test with playwright
extension
<img width="347" alt="image"
src="https://github.com/user-attachments/assets/5f7608fc-7896-4014-b312-a0c36f21b7d1">
2024-10-10 16:10:11 +03:00
Jarrod Flesch
a70b193527 chore: improve setUser type, uses generic from useAuth (#8636)
Create specific type for setUser in auth provider that uses the generic.
2024-10-10 08:47:15 -04:00
Jarrod Flesch
5e94d9b1ca fix: corrects useAuth generics (#8627)
Corrects AuthContext and useAuth generics due to regression in
https://github.com/payloadcms/payload/pull/8600
2024-10-09 23:36:18 -04:00
Elliot DeNolf
f8bae0e7b0 ci: remove payload as valid scope, payload is implied if no scope 2024-10-09 22:09:31 -04:00
Gregor Gabrič
28c3a770ee feat(translations): add slovenian lang (#8603)
Add Slovenian language
2024-10-09 20:23:05 +00:00
Elliot DeNolf
123022969f chore(release): eslint/3.0.0-beta.112 2024-10-09 15:48:40 -04:00
Patrik
4343b970eb chore(examples): adds optional tenant-based cookie handling by domain in multi-tenant example (#8490)
- Adds optional tenant-based cookie handling based by domain (commented
out to leave functionality out by default)

- Removes 2.0 multi-tenant example

- Updates `examples/multi-tenant-single-domain` -->
`examples/multi-tenant`
2024-10-09 14:32:48 -04:00
Sasha
0b2a7a3606 feat(plugin-sentry): update plugin to 3.0 (#8613)
Updates the plugin to 3.0

Test:
```sh
NEXT_PUBLIC_SENTRY_DSN=<DSN here> pnpm dev plugin-sentry
```

Example:
```ts
sentryPlugin({
  options: {
    captureErrors: [400, 403],
    context: ({ defaultContext, req }) => {
      return {
        ...defaultContext,
        tags: {
          locale: req.locale,
        },
      }
    },
    debug: true,
  },
  Sentry,
})
```
2024-10-09 14:26:58 -04:00
Jarrod Flesch
769c94b4fd chore: clarifies i18n docs (#8619)
Fixes https://github.com/payloadcms/payload/issues/8562

Removes `debug` option from i18n docs - never existed.

Corrects hallucinations in the docs and lays out exactly how custom
translations should be used when you want to use them in custom
components.
2024-10-09 14:15:45 -04:00
Paul
f507530214 fix(ui): react select fields not increasing height when items overflow (#8618)
Fixes this instance 

![image](https://github.com/user-attachments/assets/b2acc493-727e-4a26-8623-de28ff1dbe3c)
2024-10-09 18:00:03 +00:00
Elliot DeNolf
39825dfce5 chore(release): v3.0.0-beta.112 [skip ci] 2024-10-09 09:56:36 -04:00
Patrik
e904981943 chore(next): adds export for mergeHeaders utility function (#8609)
Need this utility function exported for this PR: #8490
2024-10-08 15:45:50 -04:00
Patrik
c14c4298e2 fix(payload): calculates correct aspect ratio dimensions on sharp based files (#8537)
v2 PR [here](https://github.com/payloadcms/payload/pull/8510)
2024-10-08 14:45:51 -04:00
Patrik
9a0568c72e fix(payload): applies resize after cropping if resizeOptions are defined (#8528)
Fixes #7592
2024-10-08 14:40:41 -04:00
Jarrod Flesch
829996a126 chore!: improve auth provider setting user and user cookie (#8600)
### Improvements
- Uses overlay modal for "logging out..." display on logout view
- If user manually logs out it takes them directly to the login page
after logout, if caused by inactivity then they will see the logout page
that explains that they were logged out due to inactivity
- Fixes issue with cookie refresh triggering even after the user logs
out
- Cleans up auth provider timeouts for refresh and force logout
- `setUser` now expects the result similar to the response from the
`/me` endpoint, which includes the token, exp, and user

### BREAKING CHANGE

If you are using the `setUser` function exposed from the `useAuth()`
provider, then you will need to make some adjustments.

`setUser` now expects the response data from auth enabled endpoints, ie
the `/me` route. This is so the cookie and expiration can be properly
set in sync when a new user is set on the provider.
```ts
// before
setUser({
  id: 670524817048be0fa222fc01,
  email: dev@payloadcms.com,
  // ... other user properties
})

// new
setUser({
  user: {
    id: 670524817048be0fa222fc01,
    email: dev@payloadcms.com,
    // ... other user properties
  },
  exp: 1728398351,
  token: "....eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVC...."
})
```
2024-10-08 11:49:18 -04:00
Jessica Chowdhury
f6e5244204 chore(templates): hide admin bar for website template on small screens (#8601)
Hides the admin bar component for website template on mobile.
2024-10-08 15:45:43 +00:00
Dan Ribbens
1bf580fac3 feat: join field works with hasMany relationships (#8493)
Join field works on relationships and uploads having `hasMany: true`

---------

Co-authored-by: Sasha <64744993+r1tsuu@users.noreply.github.com>
2024-10-08 11:40:34 -04:00
Elliot DeNolf
ca779441a3 fix(db-vercel-postgres): add pg dep (#8598) 2024-10-08 10:32:28 -04:00
Elliot DeNolf
7e2f4e62de chore(templates): more next.js promises (#8599)
Continuation of #8547 . Missed some type updates.
2024-10-08 09:50:31 -04:00
Jarrod Flesch
1b63ad4cb3 fix: verify view is inaccessible (#8557)
Fixes https://github.com/payloadcms/payload/issues/8470

Cleans up the way we redirect and where it happens.

## Improvements
- When you verify, the admin panel will display a toast when it
redirects you to the login route. This is contextually helpful as to
what is happening.
- Removes dead code path, as we always set the _verifiedToken to null
after it is used.

## `handleAdminPage` renamed to `getRouteInfo`
This function no longer handles routing. It kicks that responsibility
back up to the initPage function.

## `isAdminAuthRoute` renamed to `isPublicAdminRoute`
This was inversely named as it determines if a given route is public.
Also simplifies deterministic logic here.

## `redirectUnauthenticatedUser` argument
This is no longer used or needed. We can determine these things by using
the `isPublicAdminRoute` function.

## View Style fixes
- Reset Password
- Forgot Password
- Unauthorized
2024-10-07 14:20:07 -04:00
Paul
2a1321c813 fix(ui): add unstyled prop to react-select so that payload styles take priority (#8572)
Adds `unstyled={true}` prop to the react-select element so that
payload's styles take priority. Due to the way react-select adds its own
styles (injected into the page) they were higher specificity due to not
being in a layer.

Fixes this bug with our styles' specificity not being applied 

![image](https://github.com/user-attachments/assets/1cd216a4-8125-4312-949e-168c7eb96186)


Also fixes https://github.com/payloadcms/payload/issues/8507
2024-10-07 10:36:15 -06:00
Patrik
67e6ad8168 docs: specifies defaultLocale as a required property for localization (#8585)
Fixes #8567
2024-10-07 12:07:03 -04:00
Germán Jabloñski
6cb128aa60 fix(richtext-lexical): linkFeature doesn't respect admin.routes from payload.config.ts (#8513)
Fix #8452
2024-10-07 09:48:04 -04:00
Because789
bb3496d7b5 feat(plugin-seo): adds german translation (#8580)
Adds a a German translation to the SEO Plugin. Credits to @fsyntax, since he got it rolling.
2024-10-06 22:07:32 -04:00
Sasha
bf50716fc5 docs: updates array field admin.components.RowLabel example (#8548)
Fixes https://github.com/payloadcms/payload/issues/8536
2024-10-06 21:37:59 -04:00
Because789
c473db7879 docs: fix typo in array.mdx (#8561) 2024-10-06 21:34:23 -04:00
Elliot DeNolf
7aed0d7c2e chore(eslint): payload logger usage (#8578)
Payload uses `pino` for a logger. When using the error logger
`payload.logger.error` it is possible to pass any number of arguments
like this: `payload.logger.error('Some error ocurred', err)`. However,
in this scenario, the full error will not be serialized by `pino`. It
must be passed as the `err` property inside of an object in order to be
properly serialized.

This rule ensures that a user is using this function call to properly serialize the error.
2024-10-06 13:23:30 -07:00
Elliot DeNolf
d88e0617d6 chore: sort release note sections 2024-10-06 09:59:51 -07:00
Jacob Fletcher
2ba40f3335 chore: removes duplicative join field test (#8558)
There are two of the exact same e2e tests for the join field, which
throws an error when running these tests locally because they have
identical names.
2024-10-05 09:13:43 -04:00
Elliot DeNolf
463490f670 fix(templates): await params/cookies properly (#8560) 2024-10-04 18:38:27 -07:00
Jacob Fletcher
d564cd44e9 chore: deflakes lexical e2e test (#8559)
This has caused me great pain. The problem with this test is that the
page was waiting for a URL which includes a search query that never
arrives. This moves the check into a regex pattern for a more accurate
catch.
2024-10-04 21:29:38 +00:00
Paul
7c62e2a327 feat(ui)!: scope all payload css to payload-default layer (#8545)
All payload css is now encapsulated inside CSS layers under `@layer
payload-default`

Any custom css will now have the highest possible specificity.
We have also provided a new layer `@layer payload` if you want to use
layers and ensure that your styles are applied after payload.

To override existing styles in a way that the existing rules of
specificity would be respected you can use the default layer like so
```css
@layer payload-default {
  // my styles within the payload specificity
}
```
2024-10-04 13:02:56 -06:00
Sasha
400293b8ee fix: duplicate with upload collections (#8552)
Fixes the duplicate operation with uploads
Enables duplicate for upload collections by default
2024-10-04 21:46:41 +03:00
Elliot DeNolf
e4a413eb9a chore(release): v3.0.0-beta.111 [skip ci] 2024-10-04 11:31:06 -07:00
Sasha
b99590f477 chore(templates): update templates with next.js promises (#8547)
Updates templates according to this PR
https://github.com/payloadcms/payload/pull/8489
2024-10-04 21:28:43 +03:00
Alessio Gravili
0d3416c96d fix(db-postgres): missing types for db.pool by moving @types/pg from devDependencies to dependencies (#8556)
Fixes lack of types in installed project:

![CleanShot 2024-10-04 at 19 18
58@2x](https://github.com/user-attachments/assets/e7c519ee-72fd-424b-8f6c-41032322fa5e)

Since we expose stuff from @types/pg to the end user, we need it to be
installed in the end users project => move to dependencies.
2024-10-04 17:39:03 +00:00
Sasha
0128eedf70 fix(drizzle)!: make radio and select column names to snake_case (#8439)
Fixes https://github.com/payloadcms/payload/issues/8402 and
https://github.com/payloadcms/payload/issues/8027

Before DB column names were camelCase:

![image](https://github.com/user-attachments/assets/d2965bcf-290a-4f86-9bf4-dfe7e8613934)

After this change, they are snake_case:
![Screenshot 2024-10-04
114226](https://github.com/user-attachments/assets/bbc8c20b-6745-4dd3-b0c8-56263a4e37b1)

#### Breaking SQLite / Postgres ⚠️  
If you had any select (not `hasMany: true`) or radio fields with the
name in camelCase, for example:
```ts
{
  name: 'selectReadOnly',
  type: 'select',
  admin: {
    readOnly: true,
  },
  options: [
    {
      label: 'Value One',
      value: 'one',
    },
    {
      label: 'Value Two',
      value: 'two',
    },
  ],
},
```
This previously was mapped to the db column name `"selectReadOnly"`. Now
it's `select_read_only`.
Generate a new migration to rename your columns.
```sh
pnpm payload migrate:create
```
Then select "rename column" for targeted columns and Drizzle will handle
the migration.

---------

Co-authored-by: Dan Ribbens <dan.ribbens@gmail.com>
2024-10-04 16:25:05 +00:00
Sasha
414030e1f1 fix(drizzle): row / collapsible inside of localized fields (#8539)
Fixes https://github.com/payloadcms/payload/issues/8405
2024-10-04 11:48:54 -04:00
Jacob Fletcher
f6eb027f23 chore: repairs auto-generated file comments (#8549)
The comments injected into auto-generated files have gotten misformatted
due to linting. Here is the proper format, where both comments are
adjacent to one another:

```js
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
```

Some comments were also written with casing issues, here's an example:

```js
/* DO NOT MODIFY it because it could be re-written at any time. */
```
2024-10-03 23:37:08 -04:00
Sasha
cf8347f208 fix(ui): disableBulkEdit on ui fields, defaults to true (#8540)
Fixes https://github.com/payloadcms/payload/issues/8534

UI fields are now excluded by default from the bulk edit view fields
options.
If you need to have the UI field there, you can provide:
```ts
admin: {
  disableBulkEdit: false
}
```
2024-10-04 01:07:47 +00:00
Paul
157b1e13ac fix(plugin-seo): now respects serverURL and api routes configuration (#8546) 2024-10-04 00:04:20 +00:00
Sasha
a735f40310 docs: change req.params to req.routeParams (#8380)
`req.params` is an old notation, now we use `req.routeParams`
2024-10-04 02:59:05 +03:00
Alessio Gravili
e306c927a8 docs: fix broken link (#8543) 2024-10-03 18:46:23 +00:00
Paul
dffdb22a69 fix(templates): dark and light mode not correctly working on website template's header (#8542) 2024-10-03 17:42:20 +00:00
Alessio Gravili
8789b0b20d chore: enable databaseAdapter writing again for pnpm dev (#8532) 2024-10-02 23:08:49 +00:00
Paul
eb4e3711ac fix(templates): add no search results text to website template search page (#8531) 2024-10-02 22:16:29 +00:00
Paul
132131a4b9 fix(templates): static generation from incorrect params provided (#8530) 2024-10-02 21:36:46 +00:00
Dan Ribbens
9ef4fab65d fix: ui crashes editing doc with deleted upload (#8526)
fix #8133

UI of a deleted item (will need another iteration).

![clear-upload-state](https://github.com/user-attachments/assets/2d9baebe-9a12-4905-9449-457972f4505b)
2024-10-02 15:22:57 -04:00
Paul
65015aa750 fix(templates): add force-static to pages and posts (#8527) 2024-10-02 18:41:54 +00:00
Patrik
0f7d444e6d fix(next): safely checks user within useEffect (#8524) 2024-10-02 14:17:15 -04:00
Jacob Fletcher
ca90d2b1c9 fix: properly resolves cjs withPayload export (#8521)
Importing `withPayload` as CommonJS using `require` does not properly
resolve. This was because the exported file path was using the `.cjs`
extension instead of `.js`.
2024-10-02 12:38:49 -04:00
Elliot DeNolf
ecfd90bc58 fix(templates): remove lock from website template (#8520)
Including this file was causing the dependency checker to error because
it was installing all `@lexical` packages on version 0.17.0, instead of
0.18.0.

![Uploading image.png…]()
2024-10-02 10:59:26 -04:00
Paul
86371449b8 fix(templates): fixed drafts not being unpublished in the frontend of the website template (#8514) 2024-10-01 23:01:06 +00:00
Paul
69203c5515 docs: fix formatting on join field specifying additional fields section (#8509) 2024-10-01 20:24:26 +00:00
Sasha
a8eceb03b6 fix(next): current published version label (#8505)
Fixes https://github.com/payloadcms/payload/issues/8502

includes `parent` to the `getLatestVersion` query
2024-10-01 21:22:00 +03:00
Sasha
fa59d4c0b2 feat!: update next@15.0.0-canary.173, react@19.0.0-rc-3edc000d-20240926 (#8489)
Updates the minimal supported versions of next.js to
[`15.0.0-canary.173`](https://github.com/vercel/next.js/releases/tag/v15.0.0-canary.173)
and react to `19.0.0-rc-3edc000d-20240926`.

Adds neccessary awaits according to this breaking change
https://github.com/vercel/next.js/pull/68812

## Breaking Changes

The `params` and `searchParams` types in
`app/(payload)/admin/[[...segments]]/page.tsx` and
`app/(payload)/admin/[[...segments]]/not-found.tsx` must be changed to
promises:

```diff
- type Args = {
-   params: {
-     segments: string[]
-   }
-   searchParams: {
-     [key: string]: string | string[]
-   }
- }

+ type Args = {
+   params: Promise<{
+     segments: string[]
+   }>
+   searchParams: Promise<{
+     [key: string]: string | string[]
+   }>
+ }

```
2024-10-01 13:16:11 -04:00
Germán Jabloñski
d80410b228 fix(ui): admin.allowCreate in upload field (#8484)
The admin.allowCreate property on the upload field was not doing what it
was supposed to do. In fact, it was doing nothing.

From now on, when set to false, the option to create a new upload from
the UI disappears.


![image](https://github.com/user-attachments/assets/f6776c4e-833c-4a65-8ea0-68edc0a57235)


![image](https://github.com/user-attachments/assets/b99f1969-1a07-4f9f-8b5e-0d5a708f7802)


![image](https://github.com/user-attachments/assets/519e19ea-f0ba-410e-8930-dd5231556bf5)


The tests cover:
- the create new button disappears.
- the option to create one by drag and drop disappears.
- the create new button inside the drawer disappears.
2024-09-30 18:54:34 -03:00
Dan Ribbens
27b1629927 fix(db-postgres, db-sqlite): joins relation name (#8491)
A join field on a relationship with a camelCase name would cause an
error from drizzle due to the relation name not matching.
2024-09-30 16:47:21 -04:00
Paul
dfdea0d4eb chore: update vscode launch settings for debugging test suites (#8399) 2024-09-30 15:18:03 -04:00
Elliot DeNolf
96d99cb361 chore(release): v3.0.0-beta.110 [skip ci] 2024-09-30 13:19:32 -04:00
Dan Ribbens
3f375cc6ee feat: join field on upload fields (#8379)
This PR makes it possible to use the new `join` field in connection with
an `upload` field. Previously `join` was reserved only for
relationships.
2024-09-30 13:12:30 -04:00
Dan Ribbens
3847428f0a fix(db-*): make db.begintransactions required (#8419)
Changing the transcations functions on the db so that projects using
typescript `strict: true` do not need to type narrow before using it.
2024-09-30 13:12:07 -04:00
Sasha
7b6a760e97 fix(db-mongodb): duplicate versions with parent string ids (#8487)
Fixes https://github.com/payloadcms/payload/issues/8441

we may want to add a predifined migration that changes all what's needed
to ObjectIDs @DanRibbens
2024-09-30 13:06:48 -04:00
James Mikrut
0c1004537d fix: draft status access control checks (#8486) 2024-09-30 16:41:54 +00:00
Sasha
e765a5e866 fix: reset password link extra slash and thread admin.routes.reset property (#8448)
Removes extra slash
from: 
`http://host/admin//reset/token`
to:
`http://host/admin/reset/token`

Threads `admin.routes.reset`:
```ts
const config: Config = {
  admin: {
    routes: {
      reset: '/custom-reset',
    },
  },
}
```
2024-09-30 19:06:19 +03:00
Ante
f543e8963e chore(translations): croatian translation improvements (#7413)
Croatian translation is improved for better choice of words in given
context, typos, extra whitespaces and consistency of formal pronouns
use.
2024-09-30 12:04:34 -04:00
Alessio Gravili
163f3c0692 feat(richtext-lexical): export $createInlineBlockNode, $isInlineBlockNode and InlineBlockNode (#8480) 2024-09-30 13:42:49 +00:00
Dan Ribbens
4241811fa9 fix: sorting by id incorrectly orders by version.id (#8442)
fixes #7187
2024-09-30 09:16:41 -04:00
Patrik
8110cb9956 fix(db-mongodb): properly filters out number field values with the exists operator filter (#8416)
V2 PR [here](https://github.com/payloadcms/payload/pull/8415)
2024-09-30 08:59:58 -04:00
Paul
e5ca476d7f fix(ui): RTL not applying for localised textarea fields (#8474) 2024-09-29 19:25:09 +00:00
Alessio Gravili
161749bde9 chore: fix build by adding missing translation keys (#8471) 2024-09-29 10:56:36 +00:00
Mike Bailey
22f120dc85 feat(translations): add danish translations (#7809)
Added Danish (da-DK) translations for Beta
2024-09-28 18:13:31 -04:00
Alessio Gravili
e7b44dc545 chore: add workaround for unsettled top-level await script failures (#8467)
currently only for pnpm dev
2024-09-28 17:53:45 +00:00
Germán Jabloñski
8b44676b0d feat(richtext-lexical)!: upgrade lexical from 0.17.0 to 0.18.0, make tables more reliable (#8444)
This PR

- Introduces multiline markdown transformers / mdx support
- Introduce `shouldMergeAdjacentLines` option in
`$convertFromMarkdownString`. If true, merges adjacent lines as per
commonmark spec. This would allow to close:
https://github.com/payloadcms/payload/issues/8049
- Many new features and bug fixes!
- Ports over changes from the lexical playground. Most notably:
  - add support for enabling table row stripping
  - make table resizing & table cell selection more reliable

**BREAKING**: This upgrades lexical from 0.17.0 to 0.18.0. If you have
any lexical packages installed in your project, please update them
accordingly. Additionally, if you depend on the lexical APIs, please
consult their changelog, as lexical may introduce breaking changes:
https://github.com/facebook/lexical/releases/tag/v0.18.0

---------

Co-authored-by: Alessio Gravili <alessio@gravili.de>
2024-09-28 13:10:44 -04:00
Sasha
613d3b090e fix(drizzle): hasMany / poly relationships nested to localized fields / nested blocks to localized fields (#8456)
fixes https://github.com/payloadcms/payload/issues/8455 and
https://github.com/payloadcms/payload/issues/8462

- Builds the `_locale` column for the `_rels` when it's inside of a
localized group / tab
- Properly builds `sanitizedPath` for blocks in the transform-read
function when it's inside of a localized field. This fixes with fields
inside that have its own table (like `_rels`, select `hasMany: true`
etc)

Adds _more_ tests!
2024-09-28 17:33:50 +03:00
Paul
fb603448d8 feat(templates): add search functionality to the website template (#8454)
- adds a /search and search plugin example to website template
- adds an additional check for valid paths on /preview
- fixes a few bugs around the site
2024-09-27 18:01:58 -06:00
Germán Jabloñski
f50174f5b8 fix(richtext-lexical): match the indentation spacing of paragraphs and lists (#8437)
Before this, indented paragraphs, un/ordered list-items, and checkbox
list-items had 3 different sizes.

This PR unifies all 3 to match.

Related:
- https://github.com/payloadcms/payload/pull/8138
- https://github.com/facebook/lexical/pull/4025

List-items were using a custom indentation size, instead of the
browser's default. The reason I'm adapting list-items to this default
size and not the paragraphs to list-items, is because when
importing/exporting html in contexts where our CSS isn't present, visual
consistency is maintained.

Also, the browsers' default looks fine to me.

Note: Lexical's detection of whether the checkbox is clicked is a bit
hacky. I've made sure it doesn't break and added an explanatory comment
to prevent anyone from breaking it in the future.

## Before


![image](https://github.com/user-attachments/assets/7195a592-a695-4131-af1a-df016c215758)

## After


![image](https://github.com/user-attachments/assets/ef3b708f-2ce6-4bf0-951e-15c550cdcc65)
2024-09-27 13:28:36 -03:00
Germán Jabloñski
17e0547db3 feat(payload, ui): add admin.allowEdit relationship field (#8398)
This PR adds a new property `allowEdit` to the admin of the relationship
field. It is very similar to the existing `allowCreate`, only in this
case it hides the edit icon:

<img width="796" alt="image"
src="https://github.com/user-attachments/assets/bbe79bb2-db06-4ec4-b023-2f1c53330fcb">
2024-09-27 09:22:03 -04:00
Elliot DeNolf
e900e8974b chore(release): v3.0.0-beta.109 [skip ci] 2024-09-26 14:00:43 -04:00
Dan Ribbens
adc9bb5cbd fix(db-mongodb): docs duplicated in list view with drafts (#8435)
fixes #8430
2024-09-26 13:57:07 -04:00
Germán Jabloñski
a09811f5d4 fix(richtext-lexical): add max-width to tables (temporary fix for overflow). (#8431)
This is a half-baked and temporary solution to
https://github.com/payloadcms/payload/issues/8036

As I said there:
- ideally tables would have a horizontal scroll independent of the rest
of the document, just like Notion does.
- the solution in this PR can make the experience of resizing columns
frustrating

However, despite that drawback, it is arguably a better behavior than
the current one where they can have overflow over the editor container.

Until the ideal solution is implemented, let's default to this behavior.

## Before


![image](https://github.com/user-attachments/assets/b2856a3f-4b43-45f0-a7db-00c53fe5c980)


## After

![image](https://github.com/user-attachments/assets/2f60d186-d614-4c72-968c-137820812e11)
2024-09-26 14:37:25 -03:00
Paul
c73f6c74b3 fix(ui): autosave and preventLeaveWithoutSaving interfering with fetching form-state reliably (#8434)
Removes the setModified call from Autosave logic and updates the
`preventLeaveWithoutSaving` logic in Document info to actually disable
if autosave is enabled (previously it always resolved to true)

Fixes https://github.com/payloadcms/payload/issues/8072
2024-09-26 11:22:36 -06:00
Paul
5c2e39ef0c fix(ui): number field not being able to clear out the field's value (#8425)
Fixes #7780 

Fixes a bug where the number field won't save data if it's being removed
entirely (should be null) instead of changed to another value.

---------

Co-authored-by: PatrikKozak <patrik@payloadcms.com>
2024-09-26 08:37:23 -06:00
Riley Pearce
84d2026330 feat: preselected theme (#8354)
This PR implements the ability to attempt to force the use of light/dark
theme in the admin panel. While I am a big advocate for the benefits
that dark mode can bring to UX, it does not always suit a clients
branding needs.

Open to discussion on whether we consider this a suitable feature for
the platform. Please feel free to add to this PR as needed.

TODO:

- [x] Implement tests (I'm open to guidance on this from the Payload
team as currently it doesn't look like it's possible to adjust the
payload config file on the fly - meaning it can't be easily placed in
the admin folder tests).

---------

Co-authored-by: Germán Jabloñski <43938777+GermanJablo@users.noreply.github.com>
2024-09-26 11:09:29 -03:00
928 changed files with 32926 additions and 33032 deletions

View File

@@ -47,7 +47,6 @@ jobs:
live-preview
live-preview-react
next
payload
plugin-cloud
plugin-cloud-storage
plugin-form-builder

3
.gitignore vendored
View File

@@ -314,3 +314,6 @@ test/app/(payload)/admin/importMap.js
/test/app/(payload)/admin/importMap.js
test/pnpm-lock.yaml
test/databaseAdapter.js
/filename-compound-index
/media-with-relation-preview
/media-without-relation-preview

30
.vscode/launch.json vendored
View File

@@ -10,14 +10,14 @@
"cwd": "${workspaceFolder}"
},
{
"command": "node --no-deprecation test/dev.js _community",
"command": "pnpm tsx --no-deprecation test/dev.ts _community",
"cwd": "${workspaceFolder}",
"name": "Run Dev Community",
"request": "launch",
"type": "node-terminal"
},
{
"command": "node --no-deprecation test/dev.js storage-uploadthing",
"command": "pnpm tsx --no-deprecation test/dev.ts storage-uploadthing",
"cwd": "${workspaceFolder}",
"name": "Uploadthing",
"request": "launch",
@@ -25,7 +25,7 @@
"envFile": "${workspaceFolder}/test/storage-uploadthing/.env"
},
{
"command": "node --no-deprecation test/dev.js live-preview",
"command": "pnpm tsx --no-deprecation test/dev.ts live-preview",
"cwd": "${workspaceFolder}",
"name": "Run Dev Live Preview",
"request": "launch",
@@ -43,28 +43,28 @@
}
},
{
"command": "node --no-deprecation test/dev.js admin",
"command": "pnpm tsx --no-deprecation test/dev.ts admin",
"cwd": "${workspaceFolder}",
"name": "Run Dev Admin",
"request": "launch",
"type": "node-terminal"
},
{
"command": "node --no-deprecation test/dev.js auth",
"command": "pnpm tsx --no-deprecation test/dev.ts auth",
"cwd": "${workspaceFolder}",
"name": "Run Dev Auth",
"request": "launch",
"type": "node-terminal"
},
{
"command": "node --no-deprecation test/dev.js fields-relationship",
"command": "pnpm tsx --no-deprecation test/dev.ts fields-relationship",
"cwd": "${workspaceFolder}",
"name": "Run Dev Fields-Relationship",
"request": "launch",
"type": "node-terminal"
},
{
"command": "node --no-deprecation test/dev.js login-with-username",
"command": "pnpm tsx --no-deprecation test/dev.ts login-with-username",
"cwd": "${workspaceFolder}",
"name": "Run Dev Login-With-Username",
"request": "launch",
@@ -81,21 +81,21 @@
}
},
{
"command": "node --no-deprecation test/dev.js collections-graphql",
"command": "pnpm tsx --no-deprecation test/dev.ts collections-graphql",
"cwd": "${workspaceFolder}",
"name": "Run Dev GraphQL",
"request": "launch",
"type": "node-terminal"
},
{
"command": "node --no-deprecation test/dev.js fields",
"command": "pnpm tsx --no-deprecation test/dev.ts fields",
"cwd": "${workspaceFolder}",
"name": "Run Dev Fields",
"request": "launch",
"type": "node-terminal"
},
{
"command": "node --no-deprecation test/dev.js versions",
"command": "pnpm tsx --no-deprecation test/dev.ts versions",
"cwd": "${workspaceFolder}",
"name": "Run Dev Postgres",
"request": "launch",
@@ -105,35 +105,35 @@
}
},
{
"command": "node --no-deprecation test/dev.js versions",
"command": "pnpm tsx --no-deprecation test/dev.ts versions",
"cwd": "${workspaceFolder}",
"name": "Run Dev Versions",
"request": "launch",
"type": "node-terminal"
},
{
"command": "node --no-deprecation test/dev.js localization",
"command": "pnpm tsx --no-deprecation test/dev.ts localization",
"cwd": "${workspaceFolder}",
"name": "Run Dev Localization",
"request": "launch",
"type": "node-terminal"
},
{
"command": "node --no-deprecation test/dev.js locked-documents",
"command": "pnpm tsx --no-deprecation test/dev.ts locked-documents",
"cwd": "${workspaceFolder}",
"name": "Run Dev Locked Documents",
"request": "launch",
"type": "node-terminal"
},
{
"command": "node --no-deprecation test/dev.js uploads",
"command": "pnpm tsx --no-deprecation test/dev.ts uploads",
"cwd": "${workspaceFolder}",
"name": "Run Dev Uploads",
"request": "launch",
"type": "node-terminal"
},
{
"command": "node --no-deprecation test/dev.js field-error-states",
"command": "pnpm tsx --no-deprecation test/dev.ts field-error-states",
"cwd": "${workspaceFolder}",
"name": "Run Dev Field Error States",
"request": "launch",

View File

@@ -122,3 +122,19 @@ This is how you can preview changes you made locally to the docs:
4. Add a `DOCS_DIR` environment variable to the `.env` file which points to the absolute path of your modified docs folder. For example `DOCS_DIR=/Users/yourname/Documents/GitHub/payload/docs`
5. Run `yarn run fetchDocs:local`. If this was successful, you should see no error messages and the following output: _Docs successfully written to /.../website/src/app/docs.json_. There could be error messages if you have incorrect markdown in your local docs folder. In this case, it will tell you how you can fix it
6. You're done! Now you can start the website locally using `yarn run dev` and preview the docs under [http://localhost:3000/docs/](http://localhost:3000/docs/)
## Internationalization (i18n)
If your PR adds a string to the UI, we need to make sure to translate it into all the languages that Payload supports. To do that:
- Find the appropriate internationalization file for your package. These are typically located in `packages/translations/src/languages`, although some packages (e.g., richtext-lexical) have separate i18n files for each feature.
- Add the string to the English locale "en".
- Translate it to other languages. You can use the `translateNewKeys` script if you have an OpenAI API key in your `.env` (under `OPENAI_KEY`), or you can use ChatGPT or Google translate - whatever is easier for you. For payload core translations (in packages/translations) you can run the `translateNewKeys` script using `cd packages/translations && pnpm translateNewKeys`. For lexical translations, you can run it using `cd packages/richtext-lexical && pnpm translateNewKeys`. External contributors can skip this step and leave it to us.
To display translation strings in the UI, make sure to use the `t` utility of the `useTranslation` hook:
```ts
const { t } = useTranslation()
// ...
t('yourStringKey')
```

View File

@@ -100,6 +100,10 @@ If you want to add contributions to this repository, please follow the instructi
The [Examples Directory](./examples) is a great resource for learning how to setup Payload in a variety of different ways, but you can also find great examples in our blog and throughout our social media.
If you'd like to run the examples, you can either copy them to a folder outside this repo or run them directly by (1) navigating to the example's subfolder (`cd examples/your-example-folder`) and (2) using the `--ignore-workspace` flag to bypass workspace restrictions (e.g., `pnpm --ignore-workspace install` or `pnpm --ignore-workspace dev`).
You can see more examples at:
- [Examples Directory](./examples)
- [Payload Blog](https://payloadcms.com/blog)
- [Payload YouTube](https://www.youtube.com/@payloadcms)

View File

@@ -1,19 +1,19 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import type { Metadata } from 'next'
import config from '@payload-config'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import { NotFoundPage, generatePageMetadata } from '@payloadcms/next/views'
import { generatePageMetadata, NotFoundPage } from '@payloadcms/next/views'
import { importMap } from '../importMap.js'
type Args = {
params: {
params: Promise<{
segments: string[]
}
searchParams: {
}>
searchParams: Promise<{
[key: string]: string | string[]
}
}>
}
export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>

View File

@@ -1,19 +1,19 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import type { Metadata } from 'next'
import config from '@payload-config'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import { RootPage, generatePageMetadata } from '@payloadcms/next/views'
import { generatePageMetadata, RootPage } from '@payloadcms/next/views'
import { importMap } from '../importMap.js'
type Args = {
params: {
params: Promise<{
segments: string[]
}
searchParams: {
}>
searchParams: Promise<{
[key: string]: string | string[]
}
}>
}
export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>

View File

@@ -1,5 +1,5 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import { REST_DELETE, REST_GET, REST_OPTIONS, REST_PATCH, REST_POST } from '@payloadcms/next/routes'

View File

@@ -1,5 +1,5 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import { GRAPHQL_PLAYGROUND_GET } from '@payloadcms/next/routes'

View File

@@ -1,5 +1,5 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import { GRAPHQL_POST } from '@payloadcms/next/routes'

View File

@@ -1,13 +1,11 @@
/* 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 { importMap } from './admin/importMap.js'
// import '@payloadcms/ui/styles.css' // Uncomment this line if `@payloadcms/ui` in `tsconfig.json` points to `/ui/dist` instead of `/ui/src`
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import React from 'react'
import { importMap } from './admin/importMap.js'
import './custom.scss'
type Args = {

27
app/global-error.tsx Normal file
View File

@@ -0,0 +1,27 @@
/* eslint-disable no-restricted-exports */
'use client'
import * as Sentry from '@sentry/nextjs'
import NextError from 'next/error.js'
import { useEffect } from 'react'
export default function GlobalError({ error }: { error: { digest?: string } & Error }) {
useEffect(() => {
if (process.env.NEXT_PUBLIC_SENTRY_DSN) {
Sentry.captureException(error)
}
}, [error])
return (
<html lang="en-US">
<body>
{/* `NextError` is the default Next.js error page component. Its type
definition requires a `statusCode` prop. However, since the App Router
does not expose status codes for errors, we simply pass 0 to render a
generic error message. */}
{/* @ts-expect-error types repo */}
<NextError statusCode={0} />
</body>
</html>
)
}

View File

@@ -79,7 +79,7 @@ Returns a boolean which allows/denies access to the `create` request.
To add create Access Control to a Collection, use the `create` property in the [Collection Config](../collections/overview):
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const CollectionWithCreateAccess: CollectionConfig = {
// ...

View File

@@ -33,7 +33,7 @@ Access Control is specific to the operation of the request.
To add Access Control to a Field, use the `access` property in the [Field Config](../fields/overview):
```ts
import { CollectionConfig } from 'payload';
import type { CollectionConfig } from 'payload';
export const Posts: CollectionConfig = {
slug: 'posts',

View File

@@ -11,7 +11,7 @@ The behavior of [Collections](../configuration/collections) within the [Admin Pa
To configure Admin Options for Collections, use the `admin` property in your Collection Config:
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const MyCollection: CollectionConfig = {
// ...
@@ -25,23 +25,23 @@ 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. |
| 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). |
| **`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). |
### Components
@@ -89,7 +89,7 @@ It is possible to display a Preview Button within the Edit View of the Admin Pan
To configure the Preview Button, set the `admin.preview` property to a function in your [Collection Config](../configuration/collections):
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const Posts: CollectionConfig = {
// ...
@@ -126,7 +126,7 @@ All Collections receive their own List View which displays a paginated list of d
To configure pagination options, use the `admin.pagination` property in your [Collection Config](../configuration/collections):
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const Posts: CollectionConfig = {
// ...
@@ -155,7 +155,7 @@ In the List View, there is a "search" box that allows you to quickly find a docu
To define which fields should be searched, use the `admin.listSearchableFields` property in your [Collection Config](../configuration/collections):
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const Posts: CollectionConfig = {
// ...

View File

@@ -145,7 +145,7 @@ Instead, we utilize component paths to reference React Components. This method e
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 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](/docs/beta/local-api/outside-nextjs#troubleshooting) section.
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.
### Component paths in external packages

View File

@@ -33,6 +33,19 @@ Here is an example of how you might target the Dashboard View and change the bac
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.
</Banner>
### Specificity rules
All Payload CSS is encapsulated inside CSS layers under `@layer payload-default`. Any custom css will now have the highest possible specificity.
We have also provided a layer `@layer payload` if you want to use layers and ensure that your styles are applied after payload.
To override existing styles in a way that the previous rules of specificity would be respected you can use the default layer like so
```css
@layer payload-default {
// my styles within the payload specificity
}
```
## Re-using Payload SCSS variables and utilities
You can re-use Payload's SCSS variables and utilities in your own stylesheets by importing it from the UI package.

View File

@@ -40,21 +40,21 @@ export const CollectionConfig: CollectionConfig = {
The following options are available:
| Option | Description |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`condition`** | Programmatically show / hide fields based on other fields. [More details](../admin/fields#conditional-logic). |
| **`components`** | All Field Components can be swapped out for [Custom Components](../admin/components) that you define. [More details](../admin/fields). |
| **`description`** | Helper text to display alongside the field to provide more information for the editor. [More details](../admin/fields#description). |
| Option | Description |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`condition`** | Programmatically show / hide fields based on other fields. [More details](../admin/fields#conditional-logic). |
| **`components`** | All Field Components can be swapped out for [Custom Components](../admin/components) that you define. [More details](../admin/fields). |
| **`description`** | Helper text to display alongside the field to provide more information for the editor. [More details](../admin/fields#description). |
| **`position`** | Specify if the field should be rendered in the sidebar by defining `position: 'sidebar'`. |
| **`width`** | Restrict the width of a field. You can pass any string-based value here, be it pixels, percentages, etc. This property is especially useful when fields are nested within a `Row` type where they can be organized horizontally. |
| **`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. |
| **`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). |
| **`disableBulkEdit`** | Set `disableBulkEdit` to `true` to prevent fields from appearing in the select options when making edits for multiple documents. |
| **`disabled`** | If a field is `disabled`, it is completely omitted from the [Admin Panel](../admin/overview). |
| **`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. |
| **`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
@@ -217,6 +217,7 @@ Client Component:
'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} />

View File

@@ -29,7 +29,7 @@ The lockDocuments property exists on both the Collection Config and the Global C
Heres an example configuration for document locking:
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const Posts: CollectionConfig = {
slug: 'posts',

View File

@@ -151,7 +151,7 @@ Collection Metadata is the metadata that is applied to all pages within any give
To customize Collection Metadata, use the `admin.meta` key within your Collection Config:
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const MyCollection: CollectionConfig = {
// ...

View File

@@ -98,6 +98,7 @@ The following options are available:
| **`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). |
<Banner type="success">

View File

@@ -97,7 +97,7 @@ Cookies can cross subdomains without being considered third party cookies, for e
##### 2. Configure cookies
If option 1 isn't possible, then you can get around this limitation by [configuring your cookies](https://payloadcms.com/docs/beta/authentication/overview#config-options) on your authentication collection to achieve the following setup:
If option 1 isn't possible, then you can get around this limitation by [configuring your cookies](./overview#config-options) on your authentication collection to achieve the following setup:
```
SameSite: None // allows the cookie to cross domains
@@ -122,7 +122,7 @@ Configuration example:
},
```
If you're configuring [cors](https://payloadcms.com/docs/beta/production/preventing-abuse#cross-origin-resource-sharing-cors) in your Payload config, you won't be able to use a wildcard anymore, you'll need to specify the list of allowed domains.
If you're configuring [cors](../production/preventing-abuse#cross-origin-resource-sharing-cors) in your Payload config, you won't be able to use a wildcard anymore, you'll need to specify the list of allowed domains.
<Banner type="success">

View File

@@ -38,7 +38,7 @@ At its core a strategy simply takes information from the incoming request and re
Your `authenticate` method should return an object containing a Payload user document and any optional headers that you'd like Payload to set for you when we return a response.
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const Users: CollectionConfig = {
slug: 'users',

View File

@@ -15,7 +15,7 @@ Email Verification forces users to prove they have access to the email address t
To enable Email Verification, use the `auth.verify` property on your [Collection Config](../configuration/collections):
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const Customers: CollectionConfig = {
// ...
@@ -42,7 +42,7 @@ The following options are available:
Function that accepts one argument, containing `{ req, token, user }`, that allows for overriding the HTML within emails that are sent to users indicating how to validate their account. The function should return a string that supports HTML, which can optionally be a full HTML email.
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const Customers: CollectionConfig = {
// ...
@@ -74,7 +74,7 @@ export const Customers: CollectionConfig = {
Similarly to the above `generateEmailHTML`, you can also customize the subject of the email. The function argument are the same but you can only return a string - not HTML.
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const Customers: CollectionConfig = {
// ...
@@ -95,7 +95,7 @@ export const Customers: CollectionConfig = {
You can customize how the Forgot Password workflow operates with the following options on the `auth.forgotPassword` property:
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const Customers: CollectionConfig = {
// ...
@@ -119,7 +119,7 @@ The following options are available:
This function allows for overriding the HTML within emails that are sent to users attempting to reset their password. The function should return a string that supports HTML, which can be a full HTML email.
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const Customers: CollectionConfig = {
// ...
@@ -179,7 +179,7 @@ The following arguments are passed to the `generateEmailHTML` function:
Similarly to the above `generateEmailHTML`, you can also customize the subject of the email. The function argument are the same but you can only return a string - not HTML.
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const Customers: CollectionConfig = {
// ...

View File

@@ -25,7 +25,7 @@ When Authentication is enabled on a [Collection](../configuration/collections),
To enable Authentication on a Collection, use the `auth` property in the [Collection Config](../configuration/collection#auth):
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const Users: CollectionConfig = {
// ...
@@ -48,7 +48,7 @@ Any [Collection](../configuration/collections) can opt-in to supporting Authenti
To enable Authentication on a Collection, use the `auth` property in the [Collection Config](../configuration/collections):
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const Admins: CollectionConfig = {
// ...

View File

@@ -84,7 +84,7 @@ export default buildConfig({
## Email
Powered by [Resend](https://resend.com), Payload Cloud comes with integrated email support out of the box. No configuration is needed, and you can use `payload.sendEmail()` to send email right from your Payload app. To learn more about sending email with Payload, checkout the [Email Configuration](https://payloadcms.com/docs/email/overview) overview.
Powered by [Resend](https://resend.com), Payload Cloud comes with integrated email support out of the box. No configuration is needed, and you can use `payload.sendEmail()` to send email right from your Payload app. To learn more about sending email with Payload, checkout the [Email Configuration](../email/overview) overview.
If you are on the Pro or Enterprise plan, you can add your own custom Email domain name. From the Email page of your projects Settings, add the domain you wish to use for email delivery. This will generate a set of DNS records. Add these records to your DNS provider and click verify to check that your records are resolving properly. Once verified, your emails will now be sent from your custom domain name.

View File

@@ -37,7 +37,7 @@ It's often best practice to write your Collections in separate files and then im
Here is what a simple Collection Config might look like:
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const Posts: CollectionConfig = {
slug: 'posts',

View File

@@ -40,7 +40,6 @@ export default buildConfig({
// highlight-start
i18n: {
fallbackLanguage: 'en', // default
debug: false, // default
}
// highlight-end
})
@@ -51,7 +50,6 @@ The following options are available:
| Option | Description |
| --------------------- | --------------------------------|
| **`fallbackLanguage`** | The language to fall back to if the user's preferred language is not supported. Default is `'en'`. |
| **`debug`** | Whether to log debug information to the console. Default is `false`. |
| **`translations`** | An object containing the translations. The keys are the language codes and the values are the translations. |
| **`supportedLanguages`** | An object containing the supported languages. The keys are the language codes and the values are the translations. |
@@ -119,7 +117,7 @@ While Payload's built-in features come fully translated, you may also want to tr
To do this, provide the translations wherever applicable, keyed to the language code:
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const Articles: CollectionConfig = {
slug: 'articles',
@@ -178,60 +176,80 @@ Anywhere in your Payload app that you have access to the `req` object, you can a
In order to use custom translations in your project, you need to provide the types for the translations.
Here is an example of how you can define the types for the custom translations in a [Custom Component](../admin/components):
Here we create a shareable translations object. We will import this in both our custom components and in our Payload config.
```ts
'use client'
// <rootDir>/custom-translations.ts
import type { Config } from 'payload'
import type { NestedKeysStripped } from '@payloadcms/translations'
import type React from 'react'
import { useTranslation } from '@payloadcms/ui/providers/Translation'
const customTranslations = {
export const customTranslations: Config['i18n']['translations'] = {
en: {
general: {
test: 'Custom Translation',
myCustomKey: 'My custom english translation',
},
fields: {
addLabel: 'Add!',
}
},
}
type CustomTranslationObject = typeof customTranslations.en
type CustomTranslationKeys = NestedKeysStripped<CustomTranslationObject>
export type CustomTranslationsObject = typeof customTranslations.en
export type CustomTranslationsKeys = NestedKeysStripped<CustomTranslationsObject>
```
Import the shared translations object into our Payload config so they are available for use:
```ts
// <rootDir>/payload.config.ts
import { buildConfig } from 'payload'
import { customTranslations } from './custom-translations'
export default buildConfig({
//...
i18n: {
translations: customTranslations,
},
//...
})
```
Import the shared translation types to use in your [Custom Component](../admin/components):
```ts
// <rootDir>/components/MyComponent.tsx
'use client'
import type React from 'react'
import { useTranslation } from '@payloadcms/ui'
import type { CustomTranslationsObject, CustomTranslationsKeys } from '../custom-translations'
export const MyComponent: React.FC = () => {
const { i18n, t } = useTranslation<CustomTranslationObject, CustomTranslationKeys>() // These generics merge your custom translations with the default client translations
const { i18n, t } = useTranslation<CustomTranslationsObject, CustomTranslationsKeys>() // These generics merge your custom translations with the default client translations
return t('general:test')
return t('general:myCustomKey')
}
```
Additionally, Payload exposes the `t` function in various places, for example in labels. Here is how you would type those:
```ts
import type {
DefaultTranslationKeys,
NestedKeysStripped,
TFunction,
} from '@payloadcms/translations'
// <rootDir>/fields/myField.ts
import type { DefaultTranslationKeys, TFunction } from '@payloadcms/translations'
import type { Field } from 'payload'
const customTranslations = {
en: {
general: {
test: 'Custom Translation',
},
},
}
type CustomTranslationObject = typeof customTranslations.en
type CustomTranslationKeys = NestedKeysStripped<CustomTranslationObject>
import { CustomTranslationsKeys } from '../custom-translations'
const field: Field = {
name: 'myField',
type: 'text',
label: (
{ t }: { t: TFunction<CustomTranslationKeys | DefaultTranslationKeys> }, // The generic passed to TFunction does not automatically merge the custom translations with the default translations. We need to merge them ourselves here
{ t }: { t: TFunction<CustomTranslationsKeys | DefaultTranslationKeys> }, // The generic passed to TFunction does not automatically merge the custom translations with the default translations. We need to merge them ourselves here
) => t('fields:addLabel'),
}
```

View File

@@ -35,7 +35,8 @@ import { buildConfig } from 'payload'
export default buildConfig({
// ...
localization: {
locales: ['en', 'es', 'de'] // highlight-line
locales: ['en', 'es', 'de'] // required
defaultLocale: 'en', // required
},
})
```
@@ -63,7 +64,7 @@ export default buildConfig({
rtl: true,
},
],
defaultLocale: 'en',
defaultLocale: 'en', // required
fallback: true,
},
})

View File

@@ -216,7 +216,7 @@ Cross-origin resource sharing (CORS) can be configured with either a whitelist a
Here's an example showing how to allow incoming requests from any domain:
```ts
import { buildConfig } from 'payload/config'
import { buildConfig } from 'payload'
export default buildConfig({
// ...
@@ -227,7 +227,7 @@ export default buildConfig({
Here's an example showing how to append a new header (`x-custom-header`) in `Access-Control-Allow-Headers`:
```ts
import { buildConfig } from 'payload/config'
import { buildConfig } from 'payload'
export default buildConfig({
// ...

View File

@@ -157,7 +157,7 @@ You can disable this setting and solely use migrations to manage your local deve
For this reason, we suggest that you leave `push` as its default setting and treat your local dev database as a sandbox.
For more information about push mode and prototyping in development, [click here](/docs/beta/database/postgres#prototyping-in-dev-mode).
For more information about push mode and prototyping in development, [click here](./postgres#prototyping-in-dev-mode).
The typical workflow in Payload is to build out your Payload configs, install plugins, and make progress in development mode - allowing Drizzle to push your changes to your local database for you. Once you're finished, you can create a migration.

View File

@@ -60,6 +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`. |
| `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'. |
@@ -98,7 +99,7 @@ Alternatively, you can disable `push` and rely solely on migrations to keep your
In Postgres, migrations are a fundamental aspect of working with Payload and you should become familiar with how they work.
For more information about migrations, [click here](/docs/beta/database/migrations#when-to-run-migrations).
For more information about migrations, [click here](./migrations#when-to-run-migrations).
## Drizzle schema hooks

View File

@@ -78,7 +78,7 @@ Alternatively, you can disable `push` and rely solely on migrations to keep your
In SQLite, migrations are a fundamental aspect of working with Payload and you should become familiar with how they work.
For more information about migrations, [click here](/docs/beta/database/migrations#when-to-run-migrations).
For more information about migrations, [click here](./migrations#when-to-run-migrations).
## Drizzle schema hooks

View File

@@ -8,7 +8,7 @@ desc: Database transactions are fully supported within Payload.
Database transactions allow your application to make a series of database changes in an all-or-nothing commit. Consider an HTTP request that creates a new **Order** and has an `afterChange` hook to update the stock count of related **Items**. If an error occurs when updating an **Item** and an HTTP error is returned to the user, you would not want the new **Order** to be persisted or any other items to be changed either. This kind of interaction with the database is handled seamlessly with transactions.
By default, Payload will use transactions for all operations, as long as it is supported by the configured database. Database changes are contained within all Payload operations and any errors thrown will result in all changes being rolled back without being committed. When transactions are not supported by the database, Payload will continue to operate as expected without them.
By default, Payload will use transactions for all data changing operations, as long as it is supported by the configured database. Database changes are contained within all Payload operations and any errors thrown will result in all changes being rolled back without being committed. When transactions are not supported by the database, Payload will continue to operate as expected without them.
<Banner type="info">
<strong>Note:</strong>
@@ -114,3 +114,18 @@ standalonePayloadScript()
## Disabling Transactions
If you wish to disable transactions entirely, you can do so by passing `false` as the `transactionOptions` in your database adapter configuration. All the official Payload database adapters support this option.
In addition to allowing database transactions to be disabled at the adapter level. You can prevent Payload from using a transaction in direct calls to the local API by adding `disableTransaction: true` to the args. For example:
```ts
await payload.update({
collection: 'posts',
data: {
some: 'data',
},
where: {
slug: { equals: 'my-slug' }
},
req: { disableTransaction: true },
})
```

View File

@@ -24,7 +24,7 @@ Arrays are useful for many different types of content from simple to complex, su
To create an Array Field, set the `type` to `array` in your [Field Config](./overview):
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyArrayField: Field = {
// ...
@@ -66,10 +66,10 @@ _\* An asterisk denotes that a property is required._
## Admin Options
The customize the appearance and behavior of the Array Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
To customize the appearance and behavior of the Array Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyArrayField: Field = {
// ...
@@ -81,18 +81,18 @@ export const MyArrayField: Field = {
The Array Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:
| Option | Description |
| ------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| **`initCollapsed`** | Set the initial collapsed state |
| **`components.RowLabel`** | Function or React component to be rendered as the label on the array row. Receives `({ data, index, path })` as args |
| **`isSortable`** | Disable order sorting by setting this value to `false` |
| Option | Description |
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| **`initCollapsed`** | Set the initial collapsed state |
| **`components.RowLabel`** | React component to be rendered as the label on the array row. [Example](#example-of-a-custom-rowlabel-component) |
| **`isSortable`** | Disable order sorting by setting this value to `false` |
## Example
In this example, we have an Array Field called `slider` that contains a set of fields for a simple image slider. Each row in the array has a `title`, `image`, and `caption`. We also customize the row label to display the title if it exists, or a default label if it doesn't.
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',
@@ -127,12 +127,27 @@ export const ExampleCollection: CollectionConfig = {
],
admin: {
components: {
RowLabel: ({ data, index }) => {
return data?.title || `Slide ${String(index).padStart(2, '0')}`
},
RowLabel: '/path/to/ArrayRowLabel#ArrayRowLabel',
},
},
},
],
}
```
### Example of a custom RowLabel component
```tsx
'use client'
import { useRowLabel } from '@payloadcms/ui'
export const ArrayRowLabel = () => {
const { data, rowNumber } = useRowLabel<{ title?: string }>()
const customLabel = `${data.title || 'Slide'} ${String(rowNumber).padStart(2, '0')} `
return <div>Custom Label: {customLabel}</div>
}
```

View File

@@ -24,7 +24,7 @@ Blocks are a great way to create a flexible content model that can be used to bu
To add a Blocks Field, set the `type` to `blocks` in your [Field Config](./overview):
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyBlocksField: Field = {
// ...
@@ -67,7 +67,7 @@ _\* An asterisk denotes that a property is required._
The customize the appearance and behavior of the Blocks Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyBlocksField: Field = {
// ...

View File

@@ -18,7 +18,7 @@ The Checkbox Field saves a boolean in the database.
To add a Checkbox Field, set the `type` to `checkbox` in your [Field Config](./overview):
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyCheckboxField: Field = {
// ...
@@ -53,7 +53,7 @@ _\* An asterisk denotes that a property is required._
Here is an example of a Checkbox Field in a Collection:
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -19,7 +19,7 @@ The Code Field saves a string in the database, but provides the [Admin Panel](..
To add a Code Field, set the `type` to `code` in your [Field Config](./overview):
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyBlocksField: Field = {
// ...
@@ -57,7 +57,7 @@ _\* An asterisk denotes that a property is required._
The customize the appearance and behavior of the Code Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyCodeField: Field = {
// ...
@@ -79,7 +79,7 @@ The Code Field inherits all of the default options from the base [Field Admin Co
`collections/ExampleCollection.ts
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -18,7 +18,7 @@ The Collapsible Field is presentational-only and only affects the Admin Panel. B
To add a Collapsible Field, set the `type` to `collapsible` in your [Field Config](./overview):
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyCollapsibleField: Field = {
// ...
@@ -47,7 +47,7 @@ _\* An asterisk denotes that a property is required._
The customize the appearance and behavior of the Collapsible Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyCollapsibleField: Field = {
// ...
@@ -68,7 +68,7 @@ The Collapsible Field inherits all of the default options from the base [Field A
`collections/ExampleCollection.ts`
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -18,7 +18,7 @@ The Date Field saves a Date in the database and provides the [Admin Panel](../ad
To add a Date Field, set the `type` to `date` in your [Field Config](./overview):
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyDateField: Field = {
// ...
@@ -53,7 +53,7 @@ _\* An asterisk denotes that a property is required._
The customize the appearance and behavior of the Date Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyDateField: Field = {
// ...
@@ -97,7 +97,7 @@ When only `pickerAppearance` is set, an equivalent format will be rendered in th
`collections/ExampleCollection.ts`
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -18,7 +18,7 @@ The Email Field enforces that the value provided is a valid email address.
To create an Email Field, set the `type` to `email` in your [Field Config](./overview):
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyEmailField: Field = {
// ...
@@ -54,7 +54,7 @@ _\* An asterisk denotes that a property is required._
The customize the appearance and behavior of the Email Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyEmailField: Field = {
// ...
@@ -76,7 +76,7 @@ The Email Field inherits all of the default options from the base [Field Admin C
`collections/ExampleCollection.ts`
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -18,7 +18,7 @@ The Group Field allows [Fields](./overview) to be nested under a common property
To add a Group Field, set the `type` to `group` in your [Field Config](./overview):
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyGroupField: Field = {
// ...
@@ -58,7 +58,7 @@ _\* An asterisk denotes that a property is required._
The customize the appearance and behavior of the Group Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyGroupField: Field = {
// ...
@@ -79,7 +79,7 @@ The Group Field inherits all of the default options from the base [Field Admin C
`collections/ExampleCollection.ts`
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -6,8 +6,8 @@ desc: The Join field provides the ability to work on related documents. Learn ho
keywords: join, relationship, junction, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
The Join Field is used to make Relationship fields in the opposite direction. It is used to show the relationship from
the other side. The field itself acts as a virtual field, in that no new data is stored on the collection with a Join
The Join Field is used to make Relationship and Upload fields available in the opposite direction. With a Join you can edit and view collections
having reference to a specific collection document. The field itself acts as a virtual field, in that no new data is stored on the collection with a Join
field. Instead, the Admin UI surfaces the related documents for a better editing experience and is surfaced by Payload's
APIs.
@@ -16,6 +16,7 @@ The Join field is useful in scenarios including:
- To surface `Order`s for a given `Product`
- To view and edit `Posts` belonging to a `Category`
- To work with any bi-directional relationship data
- Displaying where a document or upload is used in other documents
<LightDarkImage
srcLight="https://payloadcms.com/images/docs/fields/join.png"
@@ -24,12 +25,12 @@ The Join field is useful in scenarios including:
caption="Admin Panel screenshot of Join field"
/>
For the Join field to work, you must have an existing [relationship](./relationship) field in the collection you are
joining. This will reference the collection and path of the field of the related documents.
For the Join field to work, you must have an existing [relationship](./relationship) or [upload](./upload) field in the
collection you are joining. This will reference the collection and path of the field of the related documents.
To add a Relationship Field, set the `type` to `join` in your [Field Config](./overview):
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyJoinField: Field = {
// highlight-start
@@ -49,8 +50,7 @@ export const MyRelationshipField: Field = {
```
In this example, the field is defined to show the related `posts` when added to a `category` collection. The `on`
property is used to
specify the relationship field name of the field that relates to the collection document.
property is used to specify the relationship field name of the field that relates to the collection document.
With this example, if you navigate to a Category in the Admin UI or an API response, you'll now see that the Posts which
are related to the Category are populated for you. This is extremely powerful and can be used to define a wide variety
@@ -111,10 +111,9 @@ related docs from a new pseudo-junction collection called `categories_posts`. No
third junction collection, and can be surfaced on both Posts and Categories. But, importantly, you could add
additional "context" fields to this shared junction collection.
For example, on this `categories_posts` collection, in addition to having the `category` and
post` fields, we could add custom "context" fields like `featured` or `
spotlight`, which would allow you to store additional information directly on relationships. The `join` field gives you
complete control over any type of relational architecture in Payload, all wrapped up in a powerful Admin UI.
For example, on this `categories_posts` collection, in addition to having the `category` and `post` fields, we could add custom "context" fields like `featured` or `spotlight`,
which would allow you to store additional information directly on relationships.
The `join` field gives you complete control over any type of relational architecture in Payload, all wrapped up in a powerful Admin UI.
## Config Options
@@ -122,7 +121,7 @@ complete control over any type of relational architecture in Payload, all wrappe
|------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **`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 relationship field name of the field that relates to collection document. Use dot notation for nested paths, like 'myGroup.relationName'. |
| **`on`** \* | The name of the relationship or upload field that relates to the collection document. Use dot notation for nested paths, like 'myGroup.relationName'. |
| **`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). |

View File

@@ -19,7 +19,7 @@ The JSON Field saves actual JSON in the database, which differs from the Code fi
To add a JSON Field, set the `type` to `json` in your [Field Config](./overview):
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyJSONField: Field = {
// ...
@@ -56,7 +56,7 @@ _\* An asterisk denotes that a property is required._
The customize the appearance and behavior of the JSON Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyJSONField: Field = {
// ...
@@ -77,7 +77,7 @@ The JSON Field inherits all of the default options from the base [Field Admin Co
`collections/ExampleCollection.ts`
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',
@@ -102,7 +102,7 @@ If you only provide a URL to a schema, Payload will fetch the desired schema if
`collections/ExampleCollection.ts`
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',
@@ -135,7 +135,7 @@ export const ExampleCollection: CollectionConfig = {
`collections/ExampleCollection.ts`
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -18,7 +18,7 @@ The Number Field stores and validates numeric entry and supports additional nume
To add a Number Field, set the `type` to `number` in your [Field Config](./overview):
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyNumberField: Field = {
// ...
@@ -59,7 +59,7 @@ _\* An asterisk denotes that a property is required._
The customize the appearance and behavior of the Number Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyNumberField: Field = {
// ...
@@ -82,7 +82,7 @@ The Number Field inherits all of the default options from the base [Field Admin
`collections/ExampleCollection.ts`
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -18,7 +18,7 @@ The Point Field saves a pair of coordinates in the database and assigns an index
To add a Point Field, set the `type` to `point` in your [Field Config](./overview):
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyPointField: Field = {
// ...
@@ -59,7 +59,7 @@ _\* An asterisk denotes that a property is required._
`collections/ExampleCollection.ts`
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -18,7 +18,7 @@ The Radio Field allows for the selection of one value from a predefined set of p
To add a Radio Field, set the `type` to `radio` in your [Field Config](./overview):
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyRadioField: Field = {
// ...
@@ -69,7 +69,7 @@ _\* An asterisk denotes that a property is required._
The customize the appearance and behavior of the Radio Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyRadioField: Field = {
// ...
@@ -90,7 +90,7 @@ The Radio Field inherits all of the default options from the base [Field Admin C
`collections/ExampleCollection.ts`
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -24,7 +24,7 @@ The Relationship field is used in a variety of ways, including:
To add a Relationship Field, set the `type` to `relationship` in your [Field Config](./overview):
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyRelationshipField: Field = {
// ...
@@ -74,7 +74,7 @@ _\* An asterisk denotes that a property is required._
The customize the appearance and behavior of the Relationship Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyRelationshipField: Field = {
// ...
@@ -90,6 +90,7 @@ The Relationship Field inherits all of the default options from the base [Field
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------- |
| **`isSortable`** | Set to `true` if you'd like this field to be sortable within the Admin UI using drag and drop (only works when `hasMany` is set to `true`). |
| **`allowCreate`** | Set to `false` if you'd like to disable the ability to create new documents from within the relationship field. |
| **`allowEdit`** | Set to `false` if you'd like to disable the ability to edit documents from within the relationship field. |
| **`sortOptions`** | Define a default sorting order for the options within a Relationship field's dropdown. [More](#sortOptions) |
### Sort Options
@@ -158,7 +159,7 @@ called with an argument object with the following properties:
## Example
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -63,7 +63,7 @@ _\* An asterisk denotes that a property is required._
The customize the appearance and behavior of the Rich Text Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyRichTextField: Field = {
// ...

View File

@@ -18,7 +18,7 @@ The Row Field is presentational-only and only affects the [Admin Panel](../admin
To add a Row Field, set the `type` to `row` in your [Field Config](./overview):
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyRowField: Field = {
// ...
@@ -46,7 +46,7 @@ _\* An asterisk denotes that a property is required._
`collections/ExampleCollection.ts`
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -18,7 +18,7 @@ The Select Field provides a dropdown-style interface for choosing options from a
To add a Select Field, set the `type` to `select` in your [Field Config](./overview):
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MySelectField: Field = {
// ...
@@ -71,7 +71,7 @@ _\* An asterisk denotes that a property is required._
The customize the appearance and behavior of the Select Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MySelectField: Field = {
// ...
@@ -93,7 +93,7 @@ The Select Field inherits all of the default options from the base [Field Admin
`collections/ExampleCollection.ts`
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',
@@ -156,8 +156,7 @@ You can import the existing Select component directly from Payload, then extend
```ts
import * as React from 'react';
import { SelectInput, useField } from 'payload/components/forms';
import { useAuth } from 'payload/components/utilities';
import { SelectInput, useAuth, useField } from '@payloadcms/ui';
type CustomSelectProps = {
path: string;

View File

@@ -18,7 +18,7 @@ The Tabs Field is presentational-only and only affects the [Admin Panel](../admi
To add a Tabs Field, set the `type` to `tabs` in your [Field Config](./overview):
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyTabsField: Field = {
// ...
@@ -59,7 +59,7 @@ _\* An asterisk denotes that a property is required._
`collections/ExampleCollection.ts`
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -18,7 +18,7 @@ The Text Field is one of the most commonly used fields. It saves a string to the
To add a Text Field, set the `type` to `text` in your [Field Config](./overview):
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyTextField: Field = {
// ...
@@ -59,7 +59,7 @@ _\* An asterisk denotes that a property is required._
The customize the appearance and behavior of the Text Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyTextField: Field = {
// ...
@@ -82,7 +82,7 @@ The Text Field inherits all of the default options from the base [Field Admin Co
`collections/ExampleCollection.ts`
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -18,7 +18,7 @@ The Textarea Field is nearly identical to the [Text Field](./text) but it featur
To add a Textarea Field, set the `type` to `textarea` in your [Field Config](./overview):
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyTextareaField: Field = {
// ...
@@ -56,7 +56,7 @@ _\* An asterisk denotes that a property is required._
The customize the appearance and behavior of the Textarea Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyTextareaField: Field = {
// ...
@@ -79,7 +79,7 @@ The Textarea Field inherits all of the default options from the base [Field Admi
`collections/ExampleCollection.ts`
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -18,7 +18,7 @@ With the UI Field, you can:
To add a UI Field, set the `type` to `ui` in your [Field Config](./overview):
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyUIField: Field = {
// ...
@@ -28,14 +28,14 @@ export const MyUIField: Field = {
## Config Options
| Option | Description |
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| **`name`** \* | A unique identifier for this field. |
| **`label`** | Human-readable label for this UI field. |
| Option | Description |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| **`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.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) |
| **`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) |
_\* An asterisk denotes that a property is required._
@@ -44,7 +44,7 @@ _\* An asterisk denotes that a property is required._
`collections/ExampleCollection.ts`
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -6,7 +6,8 @@ desc: Upload fields will allow a file to be uploaded, only from a collection sup
keywords: upload, images media, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
The Upload Field allows for the selection of a Document from a Collection supporting [Uploads](../upload/overview), and formats the selection as a thumbnail in the Admin Panel.
The Upload Field allows for the selection of a Document from a Collection supporting [Uploads](../upload/overview), and
formats the selection as a thumbnail in the Admin Panel.
Upload fields are useful for a variety of use cases, such as:
@@ -15,16 +16,16 @@ Upload fields are useful for a variety of use cases, such as:
- To give a layout building block the ability to feature a background image
<LightDarkImage
srcLight="https://payloadcms.com/images/docs/fields/upload.png"
srcDark="https://payloadcms.com/images/docs/fields/upload-dark.png"
alt="Shows an upload field in the Payload Admin Panel"
caption="Admin Panel screenshot of an Upload field"
srcLight="https://payloadcms.com/images/docs/fields/upload.png"
srcDark="https://payloadcms.com/images/docs/fields/upload-dark.png"
alt="Shows an upload field in the Payload Admin Panel"
caption="Admin Panel screenshot of an Upload field"
/>
To create an Upload Field, set the `type` to `upload` in your [Field Config](./overview):
```ts
import type { Field } from 'payload/types'
import type { Field } from 'payload'
export const MyUploadField: Field = {
// ...
@@ -43,7 +44,7 @@ export const MyUploadField: Field = {
## Config Options
| Option | Description |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
| **`*relationTo`** \* | Provide a single collection `slug` to allow this field to accept a relation to. <strong>Note: the related collection must be configured to support Uploads.</strong> |
| **`filterOptions`** | A query to filter which options appear in the UI and validate against. [More](#filtering-upload-options). |
@@ -72,7 +73,7 @@ _\* An asterisk denotes that a property is required._
`collections/ExampleCollection.ts`
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',
@@ -97,7 +98,7 @@ prevent all, or a `Where` query. When using a function, it will be
called with an argument object with the following properties:
| Property | Description |
| ------------- | ----------------------------------------------------------------------------------------------------- |
|---------------|-------------------------------------------------------------------------------------------------------|
| `relationTo` | The collection `slug` to filter against, limited to this field's `relationTo` property |
| `data` | An object containing the full collection or global document currently being edited |
| `siblingData` | An object containing document data that is scoped to only fields within the same parent of this field |
@@ -127,3 +128,10 @@ You can learn more about writing queries [here](/docs/queries/overview).
unless you call the default upload field validation function imported from{' '}
<strong>payload/shared</strong> in your validate function.
</Banner>
## Bi-directional relationships
The `upload` field on its own is used to reference documents in an upload collection. This can be considered a "one-way"
relationship. If you wish to allow an editor to visit the upload document and see where it is being used, you may use
the `join` field in the upload enabled collection. Read more about bi-directional relationships using
the [Join field](./join)

View File

@@ -178,7 +178,7 @@ Notice how even the toolbars are features? That's how extensible our lexical edi
## Creating your own, custom Feature
You can find more information about creating your own feature in our [building custom feature docs](lexical/building-custom-features).
You can find more information about creating your own feature in our [building custom feature docs](/docs/lexical/building-custom-features).
## TypeScript

View File

@@ -71,26 +71,27 @@ 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](/docs/beta/local-api/outside-nextjs).
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).
## Local options available
You can specify more options within the Local API vs. REST or GraphQL due to the server-only context that they are executed in.
| Local Option | Description |
| ------------------ | ------------ |
| `collection` | Required for Collection operations. Specifies the Collection slug to operate against. |
| `data` | The data to use within the operation. Required for `create`, `update`. |
| `depth` | [Control auto-population](../queries/depth) of nested relationship and upload fields. |
| `locale` | Specify [locale](/docs/configuration/localization) for any returned 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).|
| `user` | If you set `overrideAccess` to `false`, you can pass a user to use against the access control checks. |
| `showHiddenFields` | Opt-in to receiving hidden fields. By default, they are hidden from returned documents in accordance to your config. |
| `pagination` | Set to false to return all documents and avoid querying for document counts. |
| `context` | [Context](/docs/hooks/context), which will then be passed to `context` and `req.context`, which can be read by hooks. Useful if you want to pass additional information to the hooks which shouldn't be necessarily part of the document, for example a `triggerBeforeChange` option which can be read by the BeforeChange hook to determine if it should run or not. |
| `disableErrors` | When set to `true`, errors will not be thrown. Instead, the `findByID` operation will return `null`, and the `find` operation will return an empty documents array. |
| Local Option | Description |
|-----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `collection` | Required for Collection operations. Specifies the Collection slug to operate against. |
| `data` | The data to use within the operation. Required for `create`, `update`. |
| `depth` | [Control auto-population](../queries/depth) of nested relationship and upload fields. |
| `locale` | Specify [locale](/docs/configuration/localization) for any returned 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). |
| `user` | If you set `overrideAccess` to `false`, you can pass a user to use against the access control checks. |
| `showHiddenFields` | Opt-in to receiving hidden fields. By default, they are hidden from returned documents in accordance to your config. |
| `pagination` | Set to false to return all documents and avoid querying for document counts. |
| `context` | [Context](/docs/hooks/context), which will then be passed to `context` and `req.context`, which can be read by hooks. Useful if you want to pass additional information to the hooks which shouldn't be necessarily part of the document, for example a `triggerBeforeChange` option which can be read by the BeforeChange hook to determine if it should run or not. |
| `disableErrors` | When set to `true`, errors will not be thrown. Instead, the `findByID` operation will return `null`, and the `find` operation will return an empty documents array. |
| `disableTransaction` | When set to `true`, a [database transactions](../database/transactions) will not be initialized. |
_There are more options available on an operation by operation basis outlined below._

View File

@@ -138,7 +138,7 @@ const beforeEmail: BeforeEmail<FormSubmission> = (emailsToSend, beforeChangePara
### `defaultToEmail`
Provide a fallback for the email address to send form submissions to. If the email in form configuration does not have a to email set, this email address will be used. If this is not provided then it falls back to the `defaultFromAddress` in your [email configuration](https://payloadcms.com/docs/beta/email/overview).
Provide a fallback for the email address to send form submissions to. If the email in form configuration does not have a to email set, this email address will be used. If this is not provided then it falls back to the `defaultFromAddress` in your [email configuration](../email/overview).
```ts
// payload.config.ts
@@ -412,7 +412,7 @@ formBuilder({
## Email
This plugin relies on the [email configuration](https://payloadcms.com/docs/beta/email/overview) defined in your payload configuration. It will read from your config and attempt to send your emails using the credentials provided.
This plugin relies on the [email configuration](../email/overview) defined in your payload configuration. It will read from your config and attempt to send your emails using the credentials provided.
### Email formatting

View File

@@ -177,7 +177,7 @@ You can also extend the built-in `parent` and `breadcrumbs` fields per collectio
and `createBreadcrumbField` methods. They will merge your customizations overtop the plugin's base field configurations.
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
import { createParentField } from '@payloadcms/plugin-nested-docs/fields'
import { createBreadcrumbsField } from '@payloadcms/plugin-nested-docs/fields'

View File

@@ -31,7 +31,7 @@ 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/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-seo%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/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.
</Banner>
## Installation
@@ -42,6 +42,15 @@ Install the plugin using any JavaScript package manager like [Yarn](https://yarn
pnpm add @payloadcms/plugin-sentry
```
## Sentry for Next.js setup
This plugin requires to complete the [Sentry + Next.js setup](https://docs.sentry.io/platforms/javascript/guides/nextjs/) before.
You can use either the [automatic setup](https://docs.sentry.io/platforms/javascript/guides/nextjs/#install) with the installation wizard:
```sh
npx @sentry/wizard@latest -i nextjs
```
Or the [Manual Setup](https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/)
## Basic Usage
In the `plugins` array of your [Payload Config](https://payloadcms.com/docs/configuration/overview), call the plugin and pass in your Sentry DSN as an option.
@@ -51,11 +60,13 @@ import { buildConfig } from 'payload'
import { sentryPlugin } from '@payloadcms/plugin-sentry'
import { Pages, Media } from './collections'
import * as Sentry from '@sentry/nextjs'
const config = buildConfig({
collections: [Pages, Media],
plugins: [
sentryPlugin({
dsn: 'https://61edebas776889984d323d777@o4505289711681536.ingest.sentry.io/4505357433352176',
Sentry,
}),
],
})
@@ -65,58 +76,55 @@ export default config
## Options
- `dsn` : string | **required**
- `Sentry` : Sentry | **required**
Sentry automatically assigns a DSN when you create a project, the unique DSN informs Sentry where to send events so they are associated with the correct project.
The `Sentry` instance
<Banner type="warning">
You can find your project DSN (Data Source Name) by visiting [sentry.io](sentry.io) and navigating to your [Project] > Settings > Client Keys (DSN).
Make sure to complete the [Sentry for Next.js Setup](#sentry-for-nextjs-setup) before.
</Banner>
- `enabled`: boolean | optional
Set to false to disable the plugin. Defaults to true.
Set to false to disable the plugin. Defaults to `true`.
- `init` : ClientOptions | optional
- `context`: `(args: ContextArgs) => Partial<ScopeContext> | Promise<Partial<ScopeContext>>`
Sentry allows a variety of options to be passed into the Sentry.init() function, see the full list of options [here](https://docs.sentry.io/platforms/node/guides/express/configuration/options).
- `requestHandler` : RequestHandlerOptions | optional
Accepts options that let you decide what data should be included in the event sent to Sentry, checkout the options [here](https://docs.sentry.io/platforms/node/guides/express/configuration/options).
Pass additional [contextual data](https://docs.sentry.io/platforms/javascript/enriching-events/context/#passing-context-directly) to Sentry
- `captureErrors`: number[] | optional
By default, `Sentry.errorHandler` will capture only errors with a status code of 500 or higher. To capture additional error codes, pass the values as numbers in an array.
To see all options available, visit the [Sentry Docs](https://docs.sentry.io/platforms/node/guides/express/configuration/options).
### Example
Configure any of these options by passing them to the plugin:
```ts
import { buildConfig } from 'payload'
import { sentry } from '@payloadcms/plugin-sentry'
import { sentryPlugin } from '@payloadcms/plugin-sentry'
import * as Sentry from '@sentry/nextjs'
import { Pages, Media } from './collections'
const config = buildConfig({
collections: [Pages, Media],
plugins: [
sentry({
dsn: 'https://61edebas777689984d323d777@o4505289711681536.ingest.sentry.io/4505357433352176',
sentryPlugin({
options: {
init: {
debug: true,
environment: 'development',
tracesSampleRate: 1.0,
captureErrors: [400, 403],
context: ({ defaultContext, req }) => {
return {
...defaultContext,
tags: {
locale: req.locale,
},
}
},
requestHandler: {
serverName: false,
user: ['email'],
},
captureErrors: [400, 403, 404],
debug: true,
},
Sentry,
}),
],
})

View File

@@ -276,6 +276,10 @@ OverviewField({
})
```
<Banner type="info">
Tip: You can override the length rules by changing the minLength and maxLength props on the fields. In the case of the OverviewField you can use `titleOverrides` and `descriptionOverrides` to override the length rules.
</Banner>
## TypeScript
All types can be directly imported:

View File

@@ -591,7 +591,7 @@ Each endpoint object needs to have:
Example:
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
// a collection of 'orders' with an additional route for tracking details, reachable at /api/orders/:id/tracking
export const Orders: CollectionConfig = {
@@ -605,7 +605,7 @@ export const Orders: CollectionConfig = {
path: '/:id/tracking',
method: 'get',
handler: async (req) => {
const tracking = await getTrackingInfo(req.params.id)
const tracking = await getTrackingInfo(req.routeParams.id)
if (!tracking) {
return Response.json({ error: 'not found' }, { status: 404})

View File

@@ -34,7 +34,7 @@ export default buildConfig({
And here's an example for how to install the Slate editor on a field-by-field basis, while customizing its options:
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
import { slateEditor } from '@payloadcms/richtext-slate'
export const Pages: CollectionConfig = {

View File

@@ -43,7 +43,7 @@ Every Payload Collection can opt-in to supporting Uploads by specifying the `upl
</Banner>
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const Media: CollectionConfig = {
slug: 'media',
@@ -217,7 +217,7 @@ You can specify how Payload retrieves admin thumbnails for your upload-enabled C
1. `adminThumbnail` as a **string**, equal to one of your provided image size names.
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const Media: CollectionConfig = {
slug: 'media',
@@ -246,7 +246,7 @@ export const Media: CollectionConfig = {
2. `adminThumbnail` as a **function** that takes the document's data and sends back a full URL to load the thumbnail.
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const Media: CollectionConfig = {
slug: 'media',
@@ -267,7 +267,7 @@ Some example values are: `image/*`, `audio/*`, `video/*`, `image/png`, `applicat
**Example mimeTypes usage:**
```ts
import { CollectionConfig } from 'payload'
import type { CollectionConfig } from 'payload'
export const Media: CollectionConfig = {
slug: 'media',

View File

@@ -53,6 +53,7 @@ export const rootEslintConfig = [
'payload/no-relative-monorepo-imports': 'error',
'payload/no-imports-from-exports-dir': 'error',
'payload/no-imports-from-self': 'error',
'payload/proper-payload-logger-usage': 'error',
},
},
{

View File

@@ -1,9 +1,9 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import type { Metadata } from 'next'
import config from '@payload-config'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import { NotFoundPage, generatePageMetadata } from '@payloadcms/next/views'
import { generatePageMetadata, NotFoundPage } from '@payloadcms/next/views'
type Args = {
params: {

View File

@@ -1,9 +1,9 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import type { Metadata } from 'next'
import config from '@payload-config'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import { RootPage, generatePageMetadata } from '@payloadcms/next/views'
import { generatePageMetadata, RootPage } from '@payloadcms/next/views'
type Args = {
params: {

View File

@@ -1,5 +1,5 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import { REST_DELETE, REST_GET, REST_OPTIONS, REST_PATCH, REST_POST } from '@payloadcms/next/routes'

View File

@@ -1,5 +1,5 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import { GRAPHQL_PLAYGROUND_GET } from '@payloadcms/next/routes'

View File

@@ -1,5 +1,5 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import { GRAPHQL_POST } from '@payloadcms/next/routes'

View File

@@ -1,8 +1,8 @@
/* 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 '@payloadcms/next/css'
import { RootLayout } from '@payloadcms/next/layouts'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import React from 'react'
import './custom.scss'

View File

@@ -1,8 +1,8 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import type { Metadata } from 'next'
import config from '@payload-config'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import { generatePageMetadata, NotFoundPage } from '@payloadcms/next/views'
import { importMap } from '../importMap.js'

View File

@@ -1,8 +1,8 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import type { Metadata } from 'next'
import config from '@payload-config'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import { generatePageMetadata, RootPage } from '@payloadcms/next/views'
import { importMap } from '../importMap.js'

View File

@@ -1,5 +1,5 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import { REST_DELETE, REST_GET, REST_OPTIONS, REST_PATCH, REST_POST } from '@payloadcms/next/routes'

View File

@@ -1,5 +1,5 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import { GRAPHQL_PLAYGROUND_GET } from '@payloadcms/next/routes'

View File

@@ -1,5 +1,5 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import { GRAPHQL_POST } from '@payloadcms/next/routes'

View File

@@ -1,8 +1,8 @@
/* 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 '@payloadcms/next/css'
import { RootLayout } from '@payloadcms/next/layouts'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import React from 'react'
import { importMap } from './admin/importMap.js'

View File

@@ -1,9 +1,9 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import type { Metadata } from 'next'
import config from '@payload-config'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import { NotFoundPage, generatePageMetadata } from '@payloadcms/next/views'
import { generatePageMetadata, NotFoundPage } from '@payloadcms/next/views'
type Args = {
params: {

View File

@@ -1,9 +1,9 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import type { Metadata } from 'next'
import config from '@payload-config'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import { RootPage, generatePageMetadata } from '@payloadcms/next/views'
import { generatePageMetadata, RootPage } from '@payloadcms/next/views'
type Args = {
params: {

View File

@@ -1,5 +1,5 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import { REST_DELETE, REST_GET, REST_OPTIONS, REST_PATCH, REST_POST } from '@payloadcms/next/routes'

View File

@@ -1,5 +1,5 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import { GRAPHQL_PLAYGROUND_GET } from '@payloadcms/next/routes'

View File

@@ -1,5 +1,5 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import { GRAPHQL_POST } from '@payloadcms/next/routes'

View File

@@ -1,8 +1,8 @@
/* 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 '@payloadcms/next/css'
import { RootLayout } from '@payloadcms/next/layouts'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import React from 'react'
import './custom.scss'

View File

@@ -1,6 +1,6 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
import config from '@payload-config'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import { RootPage } from '@payloadcms/next/views'
type Args = {

View File

@@ -1,5 +1,5 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import { REST_DELETE, REST_GET, REST_PATCH, REST_POST } from '@payloadcms/next/routes'

View File

@@ -1,5 +1,5 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import { GRAPHQL_PLAYGROUND_GET } from '@payloadcms/next/routes'

View File

@@ -1,5 +1,5 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import { GRAPHQL_POST } from '@payloadcms/next/routes'

View File

@@ -1,9 +1,9 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import type { Metadata } from 'next'
import config from '@payload-config'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import { NotFoundPage, generatePageMetadata } from '@payloadcms/next/views'
import { generatePageMetadata, NotFoundPage } from '@payloadcms/next/views'
type Args = {
params: {

View File

@@ -1,9 +1,9 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import type { Metadata } from 'next'
import config from '@payload-config'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import { RootPage, generatePageMetadata } from '@payloadcms/next/views'
import { generatePageMetadata, RootPage } from '@payloadcms/next/views'
type Args = {
params: {

View File

@@ -1,5 +1,5 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import { REST_DELETE, REST_GET, REST_OPTIONS, REST_PATCH, REST_POST } from '@payloadcms/next/routes'

View File

@@ -1,5 +1,5 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import { GRAPHQL_PLAYGROUND_GET } from '@payloadcms/next/routes'

View File

@@ -1,5 +1,5 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import { GRAPHQL_POST } from '@payloadcms/next/routes'

View File

@@ -1,8 +1,8 @@
/* 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 '@payloadcms/next/css'
import { RootLayout } from '@payloadcms/next/layouts'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import React from 'react'
import './custom.scss'

View File

@@ -1,3 +0,0 @@
DATABASE_URI=mongodb://127.0.0.1/payload-example-multi-tenant-single-domain
PAYLOAD_SECRET=PAYLOAD_MULTI_TENANT_EXAMPLE_SECRET_KEY
PAYLOAD_PUBLIC_SERVER_URL=http://localhost:3000

View File

@@ -1,4 +0,0 @@
module.exports = {
root: true,
extends: ['@payloadcms'],
}

View File

@@ -1,5 +0,0 @@
build
dist
node_modules
package-lock.json
.env

View File

@@ -1,79 +0,0 @@
# Payload Multi-Tenant Example (Single Domain)
This example demonstrates how to achieve a multi-tenancy in [Payload](https://github.com/payloadcms/payload) on a single domain. Tenants are separated by a `Tenants` collection.
## Quick Start
To spin up this example locally, follow these steps:
1. Clone this repo
1. `cd` into this directory and run `pnpm i --ignore-workspace`\*, `yarn`, or `npm install`
> \*If you are running using pnpm within the Payload Monorepo, the `--ignore-workspace` flag is needed so that pnpm generates a lockfile in this example's directory despite the fact that one exists in root.
1. `pnpm dev`, `yarn dev` or `npm run dev` to start the server
- Press `y` when prompted to seed the database
1. `open http://localhost:3000` to access the home page
1. `open http://localhost:3000/admin` to access the admin panel
- Login with email `demo@payloadcms.com` and password `demo`
## How it works
A multi-tenant Payload application is a single server that hosts multiple "tenants". Examples of tenants may be your agency's clients, your business conglomerate's organizations, or your SaaS customers.
Each tenant has its own set of users, pages, and other data that is scoped to that tenant. This means that your application will be shared across tenants but the data will be scoped to each tenant.
### Collections
See the [Collections](https://payloadcms.com/docs/configuration/collections) docs for details on how to extend any of this functionality.
- #### Users
The `users` collection is auth-enabled and encompass both app-wide and tenant-scoped users based on the value of their `roles` and `tenants` fields. Users with the role `super-admin` can manage your entire application, while users with the _tenant role_ of `admin` have limited access to the platform and can manage only the tenant(s) they are assigned to, see [Tenants](#tenants) for more details.
For additional help with authentication, see the official [Auth Example](https://github.com/payloadcms/payload/tree/main/examples/auth/cms#readme) or the [Authentication](https://payloadcms.com/docs/authentication/overview#authentication-overview) docs.
- #### Tenants
A `tenants` collection is used to achieve tenant-based access control. Each user is assigned an array of `tenants` which includes a relationship to a `tenant` and their `roles` within that tenant. You can then scope any document within your application to any of your tenants using a simple [relationship](https://payloadcms.com/docs/fields/relationship) field on the `users` or `pages` collections, or any other collection that your application needs. The value of this field is used to filter documents in the admin panel and API to ensure that users can only access documents that belong to their tenant and are within their role. See [Access Control](#access-control) for more details.
For more details on how to extend this functionality, see the [Payload Access Control](https://payloadcms.com/docs/access-control/overview) docs.
- #### Pages
Each page is assigned a `tenant` which is used to control access and scope API requests. Pages that are created by tenants are automatically assigned that tenant based on that user's `lastLoggedInTenant` field.
## Access control
Basic role-based access control is setup to determine what users can and cannot do based on their roles, which are:
- `super-admin`: They can access the Payload admin panel to manage your multi-tenant application. They can see all tenants and make all operations.
- `user`: They can only access the Payload admin panel if they are a tenant-admin, in which case they have a limited access to operations based on their tenant (see below).
This applies to each collection in the following ways:
- `users`: Only super-admins, tenant-admins, and the user themselves can access their profile. Anyone can create a user, but only these admins can delete users. See [Users](#users) for more details.
- `tenants`: Only super-admins and tenant-admins can read, create, update, or delete tenants. See [Tenants](#tenants) for more details.
- `pages`: Everyone can access pages, but only super-admins and tenant-admins can create, update, or delete them.
When a user logs in, a `lastLoggedInTenant` field is saved to their profile. This is done by reading the value of `req.headers.host`, querying for a tenant with a matching `domain`, and verifying that the user is a member of that tenant. This field is then used to automatically assign the tenant to any documents that the user creates, such as pages. Super-admins can also use this field to browse the admin panel as a specific tenant.
> If you have versions and drafts enabled on your pages, you will need to add additional read access control condition to check the user's tenants that prevents them from accessing draft documents of other tenants.
For more details on how to extend this functionality, see the [Payload Access Control](https://payloadcms.com/docs/access-control/overview#access-control) docs.
## CORS
This multi-tenant setup requires an open CORS policy. Since each tenant contains a dynamic list of domains, there's no way to know specifically which domains to whitelist at runtime without significant performance implications. This also means that the `serverURL` is not set, as this scopes all requests to a single domain.
Alternatively, if you know the domains of your tenants ahead of time and these values won't change often, you could simply remove the `domains` field altogether and instead use static values.
For more details on this, see the [CORS](https://payloadcms.com/docs/production/preventing-abuse#cross-origin-resource-sharing-cors) docs.
## Front-end
The frontend is scaffolded out in this example directory. You can view the code for rendering pages at `/src/app/(app)/[tenant]/[...slug]/page.tsx`. This is a starter template, you may need to adjust the app to better fit your needs.
## Questions
If you have any issues or questions, reach out to us on [Discord](https://discord.com/invite/payload) or start a [GitHub discussion](https://github.com/payloadcms/payload/discussions).

View File

@@ -1,55 +0,0 @@
{
"name": "multi-tenant-single-domain",
"version": "1.0.0",
"description": "An example of a multi tenant application, using a single domain",
"license": "MIT",
"type": "module",
"scripts": {
"_dev": "cross-env NODE_OPTIONS=--no-deprecation next dev",
"build": "cross-env NODE_OPTIONS=--no-deprecation next build",
"dev": "cross-env NODE_OPTIONS=--no-deprecation && pnpm seed && next dev --turbo",
"generate:schema": "payload-graphql generate:schema",
"generate:types": "payload generate:types",
"payload": "cross-env NODE_OPTIONS=--no-deprecation payload",
"seed": "npm run payload migrate:fresh",
"start": "cross-env NODE_OPTIONS=--no-deprecation next start"
},
"dependencies": {
"@payloadcms/db-mongodb": "3.0.0-beta.58",
"@payloadcms/next": "3.0.0-beta.58",
"@payloadcms/richtext-lexical": "3.0.0-beta.58",
"@payloadcms/ui": "3.0.0-beta.58",
"cross-env": "^7.0.3",
"dotenv": "^8.2.0",
"graphql": "^16.9.0",
"next": "15.0.0-rc.0",
"payload": "3.0.0-beta.58",
"qs": "^6.12.1",
"react": "19.0.0-rc-f994737d14-20240522",
"react-dom": "19.0.0-rc-f994737d14-20240522",
"sharp": "0.32.6"
},
"devDependencies": {
"@payloadcms/graphql": "3.0.0-beta.58",
"@swc/core": "^1.6.13",
"@types/react": "npm:types-react@19.0.0-beta.2",
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2",
"eslint": "^8.57.0",
"eslint-config-next": "15.0.0-rc.0",
"tsx": "^4.16.2",
"typescript": "5.5.2"
},
"engines": {
"node": "^18.20.2 || >=20.9.0"
},
"pnpm": {
"overrides": {
"@types/react": "npm:types-react@19.0.0-beta.2",
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2"
}
},
"overrides": {
"@types/react": "npm:types-react@19.0.0-beta.2",
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2"
}
}

View File

@@ -1,44 +0,0 @@
import type { CollectionConfig } from 'payload'
import { tenantField } from '../../fields/TenantField'
import { isPayloadAdminPanel } from '../../utilities/isPayloadAdminPanel'
import { canMutatePage, filterByTenantRead } from './access/byTenant'
import { externalReadAccess } from './access/externalReadAccess'
import { ensureUniqueSlug } from './hooks/ensureUniqueSlug'
export const Pages: CollectionConfig = {
slug: 'pages',
access: {
create: canMutatePage,
delete: canMutatePage,
read: (args) => {
// when viewing pages inside the admin panel
// restrict access to the ones your user has access to
if (isPayloadAdminPanel(args.req)) return filterByTenantRead(args)
// when viewing pages from outside the admin panel
// you should be able to see your tenants and public tenants
return externalReadAccess(args)
},
update: canMutatePage,
},
admin: {
useAsTitle: 'title',
},
fields: [
{
name: 'title',
type: 'text',
},
{
name: 'slug',
type: 'text',
defaultValue: 'home',
hooks: {
beforeValidate: [ensureUniqueSlug],
},
index: true,
},
tenantField,
],
}

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