Compare commits

..

1 Commits

Author SHA1 Message Date
Dan Ribbens
c02e4df4bc chore: wip sort with 2023-08-29 08:31:59 -04:00
4921 changed files with 164560 additions and 341057 deletions

View File

@@ -19,11 +19,3 @@ indent_size = 2
[*.mdx]
indent_style = space
indent_size = 2
[*.json]
indent_style = space
indent_size = 2
[*.md]
indent_style = space
indent_size = 2

View File

@@ -1,10 +0,0 @@
.tmp
**/.git
**/.hg
**/.pnp.*
**/.svn
**/.yarn/**
**/build
**/dist/**
**/node_modules
**/temp

View File

@@ -1,38 +1,104 @@
module.exports = {
extends: ['@payloadcms'],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module',
ecmaFeatures: {
jsx: true,
},
},
plugins: [
'@typescript-eslint',
],
extends: [
'./eslint-config',
],
settings: {
'import/resolver': {
node: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
},
},
overrides: [
{
extends: ['plugin:@typescript-eslint/disable-type-checked'],
files: ['*.js', '*.cjs', '*.json', '*.md', '*.yml', '*.yaml'],
files: ['test/**/int.spec.ts'],
rules: {
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/consistent-type-imports': 'warn',
'jest/prefer-strict-equal': 'off',
}
},
{
files: ['packages/eslint-config-payload/**'],
files: ['test/**/e2e.spec.ts'],
extends: [
'plugin:playwright/playwright-test'
],
rules: {
'perfectionist/sort-objects': 'off',
'jest/consistent-test-it': 'off',
'jest/require-top-level-describe': 'off',
'jest/no-test-callback': 'off',
'jest/prefer-strict-equal': 'off',
'jest/expect-expect': 'off',
'jest-dom/prefer-to-have-attribute': 'off',
}
},
{
files: ['*.ts', '*.tsx'],
parser: '@typescript-eslint/parser',
extends: [
'plugin:@typescript-eslint/recommended',
],
rules: {
'no-shadow': 'off',
'@typescript-eslint/no-shadow': ['error'],
'import/no-unresolved': [
2,
{
ignore: [
'payload-config',
'payload/generated-types',
],
},
],
},
},
{
files: ['package.json', 'tsconfig.json'],
files: ['*.spec.ts'],
rules: {
'perfectionist/sort-array-includes': 'off',
'perfectionist/sort-astro-attributes': 'off',
'perfectionist/sort-classes': 'off',
'perfectionist/sort-enums': 'off',
'perfectionist/sort-exports': 'off',
'perfectionist/sort-imports': 'off',
'perfectionist/sort-interfaces': 'off',
'perfectionist/sort-jsx-props': 'off',
'perfectionist/sort-keys': 'off',
'perfectionist/sort-maps': 'off',
'perfectionist/sort-named-exports': 'off',
'perfectionist/sort-named-imports': 'off',
'perfectionist/sort-object-types': 'off',
'perfectionist/sort-objects': 'off',
'perfectionist/sort-svelte-attributes': 'off',
'perfectionist/sort-union-types': 'off',
'perfectionist/sort-vue-attributes': 'off',
'@typescript-eslint/no-use-before-define': 'off',
},
},
{
files: ['*.e2e.ts'],
rules: {
'@typescript-eslint/no-use-before-define': 'off',
'jest/expect-expect': 'off',
},
},
],
root: true,
}
rules: {
'import/no-extraneous-dependencies': ['error', { packageDir: './' }],
'react/jsx-filename-extension': [2, { extensions: ['.js', '.jsx', '.ts', '.tsx'] }],
'import/prefer-default-export': 'off',
'react/prop-types': 'off',
'react/require-default-props': 'off',
'react/no-unused-prop-types': 'off',
'no-console': 'warn',
'no-sparse-arrays': 'off',
'no-underscore-dangle': 'off',
'no-use-before-define': 'off',
'arrow-body-style': 0,
'@typescript-eslint/no-use-before-define': 'off',
'import/extensions': [
'error',
'ignorePackages',
{
js: 'never',
jsx: 'never',
ts: 'never',
tsx: 'never',
},
],
},
};

View File

@@ -1,18 +0,0 @@
# lint and format
ae7d6f97d205491390f15850e5104c7abded1550
1fbda85cd04a774cb978778b0f813001664c53dd
# prettier all templates
75a428ddc4672903455998eaba7ae9f9d710bf85
# re-run prettier and eslint everywhere again
cdaa0acd61d3001407609915bd573b78565d5571
# prettier write again
dfac7395fed95fc5d8ebca21b786ce70821942bb
# lint and format plugin-cloud
fb7d1be2f3325d076b7c967b1730afcef37922c2
# lint and format create-payload-app
5fd3d430001efe86515262ded5e26f00c1451181

32
.github/CODEOWNERS vendored
View File

@@ -1,32 +0,0 @@
# Order matters. The last matching pattern takes precedence.
### Core ###
### Adapters ###
/packages/richtext-*/ @AlessioGr
### Plugins ###
/packages/plugin-cloud*/ @denolfe
### Templates ###
/templates/ @jacobsfletch @denolfe
### Misc ###
/packages/create-payload-app/ @denolfe
/packages/eslint-*/ @denolfe
### Build Files ###
/**/package.json @denolfe
/tsconfig.json @denolfe
/**/tsconfig*.json @denolfe
/jest.config.js @denolfe
/**/jest.config.js @denolfe
### Root ###
/package.json @denolfe
/scripts/ @denolfe
/.husky/ @denolfe
/.vscode/ @denolfe
/.github/ @denolfe

42
.github/ISSUE_TEMPLATE/1.bug_report.yml vendored Normal file
View File

@@ -0,0 +1,42 @@
name: Bug Report
description: Create a bug report for Payload
labels: ["possible-bug"]
body:
- type: markdown
attributes:
value: |
*Note:* Feature requests should be opened as [discussions](https://github.com/payloadcms/payload/discussions/new?category=feature-requests-ideas).
- type: input
id: reproduction-link
attributes:
label: Link to reproduction
description: Please add a link to a reproduction. See the fork [reproduction-guide](https://github.com/payloadcms/payload/blob/master/.github/reproduction-guide.md) for more information.
validations:
required: true
- type: textarea
attributes:
label: To Reproduce
description: Steps to reproduce the behavior, please provide a clear description of how to reproduce the issue, based on the linked minimal reproduction. Screenshots can be provided in the issue body below. If using code blocks, make sure that [syntax highlighting is correct](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlighting) and double check that the rendered preview is not broken.
validations:
required: true
- type: textarea
attributes:
label: Describe the Bug
validations:
required: true
- type: input
id: version
attributes:
label: Payload Version
description: What version of Payload are you running?
validations:
required: true
- type: markdown
attributes:
value: Before submitting the issue, go through the steps you've written down to make sure the steps provided are detailed and clear.
- type: markdown
attributes:
value: Contributors should be able to follow the steps provided in order to reproduce the bug.
- type: markdown
attributes:
value: These steps are used to add integration tests to ensure the same issue does not happen again. Thanks in advance!

View File

@@ -1,52 +0,0 @@
name: Bug Report v3
description: Create a bug report for Payload v3 (beta)
labels: ['status: needs-triage', 'v3']
body:
- type: input
id: reproduction-link
attributes:
label: Link to reproduction
description: Want us to look into your issue faster? Follow the [reproduction-guide](https://github.com/payloadcms/payload/blob/main/.github/reproduction-guide.md) for more information.
validations:
required: false
- type: textarea
attributes:
label: Environment Info
description: Paste output from `pnpm payload info` (>= beta.92) _or_ Payload, Node.js, and Next.js versions.
render: text
placeholder: |
Payload:
Node.js:
Next.js:
validations:
required: true
- type: textarea
attributes:
label: Describe the Bug
validations:
required: true
- type: textarea
attributes:
label: Reproduction Steps
description: Steps to reproduce the behavior, please provide a clear description of how to reproduce the issue, based on the linked minimal reproduction. Screenshots can be provided in the issue body below. If using code blocks, make sure that [syntax highlighting is correct](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlighting) and double check that the rendered preview is not broken.
validations:
required: true
- type: input
id: adapters-plugins
attributes:
label: Adapters and Plugins
description: What adapters and plugins are you using if relevant? ie. db-mongodb, db-postgres, storage-vercel-blob, etc.
- type: markdown
attributes:
value: Before submitting the issue, go through the steps you've written down to make sure the steps provided are detailed and clear.
- type: markdown
attributes:
value: Contributors should be able to follow the steps provided in order to reproduce the bug.
- type: markdown
attributes:
value: These steps are used to add integration tests to ensure the same issue does not happen again. Thanks in advance!

View File

@@ -1,47 +0,0 @@
name: Bug Report
description: Create a bug report for Payload
labels: ['status: needs-triage', 'v2']
body:
- type: markdown
attributes:
value: |
*Note:* Feature requests should be opened as [discussions](https://github.com/payloadcms/payload/discussions/new?category=feature-requests-ideas).
- type: input
id: reproduction-link
attributes:
label: Link to reproduction
description: Want us to look into your issue faster? Follow the [reproduction-guide](https://github.com/payloadcms/payload/blob/main/.github/reproduction-guide.md) for more information.
validations:
required: false
- type: textarea
attributes:
label: Describe the Bug
validations:
required: true
- type: textarea
attributes:
label: To Reproduce
description: Steps to reproduce the behavior, please provide a clear description of how to reproduce the issue, based on the linked minimal reproduction. Screenshots can be provided in the issue body below. If using code blocks, make sure that [syntax highlighting is correct](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlighting) and double check that the rendered preview is not broken.
validations:
required: true
- type: input
id: version
attributes:
label: Payload Version
description: What version of Payload are you running?
validations:
required: true
- type: input
id: adapters-plugins
attributes:
label: Adapters and Plugins
description: What adapters and plugins are you using? ie. db-mongodb, db-postgres, bundler-webpack, etc.
- type: markdown
attributes:
value: Before submitting the issue, go through the steps you've written down to make sure the steps provided are detailed and clear.
- type: markdown
attributes:
value: Contributors should be able to follow the steps provided in order to reproduce the bug.
- type: markdown
attributes:
value: These steps are used to add integration tests to ensure the same issue does not happen again. Thanks in advance!

View File

@@ -2,7 +2,7 @@
<!-- Please include a summary of the pull request and any related issues it fixes. Please also include relevant motivation and context. -->
- [ ] I have read and understand the [CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md) document in this repository.
- [ ] I have read and understand the [CONTRIBUTING.md](../CONTRIBUTING.md) document in this repository.
## Type of change
@@ -12,8 +12,8 @@
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Change to the [templates](https://github.com/payloadcms/payload/tree/main/templates) directory (does not affect core functionality)
- [ ] Change to the [examples](https://github.com/payloadcms/payload/tree/main/examples) directory (does not affect core functionality)
- [ ] Change to the [templates](../templates/) directory (does not affect core functionality)
- [ ] Change to the [examples](../examples/) directory (does not affect core functionality)
- [ ] This change requires a documentation update
## Checklist:

View File

@@ -1,47 +0,0 @@
# docs: https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: github-actions
directories:
- /
- /.github/workflows
- /.github/actions/* # Not working until resolved: https://github.com/dependabot/dependabot-core/issues/6345
- /.github/actions/setup
target-branch: beta
schedule:
interval: monthly
timezone: America/Detroit
time: '06:00'
groups:
github_actions:
patterns:
- '*'
- package-ecosystem: npm
directory: /
target-branch: beta
schedule:
interval: weekly
day: sunday
timezone: America/Detroit
time: '06:00'
commit-message:
prefix: 'chore(deps)'
labels:
- dependencies
groups:
production:
dependency-type: production
update-types:
- minor
- patch
patterns:
- '*'
dev:
dependency-type: development
update-types:
- minor
- patch
patterns:
- '*'

View File

@@ -1,16 +1,14 @@
# Reproduction Guide
1. [Fork](https://github.com/payloadcms/payload/fork) this repo
2. Optionally, create a new branch for your reproduction
3. Run `pnpm install` to install dependencies
4. Open up the `test/_community` directory
5. Add any necessary `collections/globals/fields` in this directory to recreate the issue you are experiencing
6. Run `pnpm dev _community` to start the admin panel
1. [fork](https://github.com/payloadcms/payload/fork) this repo
2. run `yarn` to install dependencies
3. open up the `test/_community` directory
4. add any necessary `collections/globals/fields` in this directory to recreate the issue you are experiencing
5. run `yarn dev _community` to start the admin panel
**NOTE:** The goal is to isolate the problem by reducing the number of `collections/globals/fields` you add to the `test/_community` folder. This folder is _not_ meant for you to copy your project into, but rather recreate the issue you are experiencing with minimal config.
## Example test directory file tree
```text
.
├── config.ts
@@ -22,43 +20,39 @@
- `config.ts` - This is the _granular_ Payload config for testing. It should be as lightweight as possible. Reference existing configs for an example
- `int.spec.ts` [Optional] - This is the test file run by jest. Any test file must have a `*int.spec.ts` suffix.
- `e2e.spec.ts` [Optional] - This is the end-to-end test file that will load up the admin UI using the above config and run Playwright tests.
- `payload-types.ts` - Generated types from `config.ts`. Generate this file by running `pnpm dev:generate-types _community`.
- `payload-types.ts` - Generated types from `config.ts`. Generate this file by running `yarn dev:generate-types _community`.
The directory split up in this way specifically to reduce friction when creating tests and to add the ability to boot up Payload with that specific config. You should modify the files in `test/_community` to get started.
<br />
## Testing is optional but encouraged
An issue does not need to have failing tests — reproduction steps with your forked repo are enough at this point. Some people like to dive deeper and we want to give you the guidance/tools to do so. Read more below:
### Running integration tests (Payload API tests)
First install [Jest Runner for VSVode](https://marketplace.visualstudio.com/items?itemName=firsttris.vscode-jest-runner).
There are a couple ways run integration tests:
- **Granularly** - you can run individual tests in vscode by installing the Jest Runner plugin and using that to run individual tests. Clicking the `debug` button will run the test in debug mode allowing you to set break points.
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/payload/src/admin/assets/images/github/int-debug.png" />
<img src="https://raw.githubusercontent.com/payloadcms/payload/master/src/admin/assets/images/github/int-debug.png" />
- **Manually** - you can run all int tests in the `/test/_community/int.spec.ts` file by running the following command:
```bash
pnpm test:int _community
yarn test:int _community
```
### Running E2E tests (Admin Panel UI tests)
The easiest way to run E2E tests is to install
- [Playwright Test for VSCode](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright)
- [Playwright Runner](https://marketplace.visualstudio.com/items?itemName=ortoni.ortoni)
Once they are installed you can open the `testing` tab in vscode sidebar and drill down to the test you want to run, i.e. `/test/_community/e2e.spec.ts`
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/payload/src/admin/assets/images/github/e2e-debug.png" />
<img src="https://raw.githubusercontent.com/payloadcms/payload/master/src/admin/assets/images/github/e2e-debug.png" />
#### Notes
The default credentials are `dev@payloadcms.com` as email and `test` as password. They can be found in `test/credentials.ts`. By default, these will be autofilled, so no log-in is required.
- It is recommended to add the test credentials (located in `test/credentials.ts`) to your autofill for `localhost:3000/admin` as this will be required on every nodemon restart. The default credentials are `dev@payloadcms.com` as email and `test` as password.

View File

@@ -1,80 +0,0 @@
name: label-author
on:
pull_request:
types: [opened]
issues:
types: [opened]
permissions:
contents: read
pull-requests: write
issues: write
jobs:
debug-context:
runs-on: ubuntu-latest
steps:
- name: View context attributes
uses: actions/github-script@v7
with:
script: console.log(context)
label-created-by:
name: Label pr/issue on opening
runs-on: ubuntu-latest
steps:
- name: Tag with 'created-by'
uses: actions/github-script@v7
if: github.event.action == 'opened'
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const payloadTeamUsernames = [
'denolfe',
'jmikrut',
'DanRibbens',
'jacobsfletch',
'JarrodMFlesch',
'AlessioGr',
'JessChowdhury',
'kendelljoseph',
'PatrikKozak',
'tylandavis',
'paulpopus',
];
const type = context.payload.pull_request ? 'pull_request' : 'issue';
const isTeamMember = payloadTeamUsernames
.map(n => n.toLowerCase())
.includes(context.payload[type].user.login.toLowerCase());
if (isTeamMember) {
github.rest.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: ['created-by: Payload team'],
});
console.log(`Added 'created-by: Payload team' label`);
return;
}
const association = context.payload[type].author_association;
let label = ''
if (association === 'MEMBER' || association === 'OWNER') {
label = 'created-by: Payload team';
} else if (association === 'CONTRIBUTOR') {
label = 'created-by: Contributor';
}
if (!label) return;
github.rest.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: [label],
});
console.log(`Added '${label}' label.`);

View File

@@ -1,116 +0,0 @@
name: label-on-change
on:
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target
issues:
types:
- assigned
- closed
- labeled
- reopened
# TODO: Handle labeling on comment
jobs:
on-labeled-ensure-one-status:
runs-on: ubuntu-latest
permissions:
issues: write
# Only run on issue labeled and if label starts with 'status:'
if: github.event.action == 'labeled' && startsWith(github.event.label.name, 'status:')
steps:
- name: Ensure only one status label
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
// Get all labels that start with 'status:' and are not the incoming label
const incomingLabelName = context.payload.label.name;
const labelNamesToRemove = context.payload.issue.labels
.filter(label => label.name.startsWith('status:') && label.name !== incomingLabelName)
.map(label => label.name);
if (!labelNamesToRemove.length) {
console.log('No labels to remove');
return;
}
console.log(`Labels to remove: '${labelNamesToRemove}'`);
// If there is more than one status label, remove all but the incoming label
for (const labelName of labelNamesToRemove) {
await github.rest.issues.removeLabel({
issue_number: context.issue.number,
name: labelName,
owner: context.repo.owner,
repo: context.repo.repo,
});
console.log(`Removed '${labelName}' label`);
}
on-issue-close:
runs-on: ubuntu-latest
permissions:
issues: write
if: github.event.action == 'closed'
steps:
- name: Remove all labels on issue close
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
// Get all labels that start with 'status:' and 'stale'
const labelNamesToRemove = context.payload.issue.labels
.filter(label => label.name.startsWith('status:') || label.name === 'stale')
.map(label => label.name);
if (!labelNamesToRemove.length) {
console.log('No labels to remove');
return;
}
console.log(`Labels to remove: '${labelNamesToRemove}'`);
for (const labelName of labelNamesToRemove) {
await github.rest.issues.removeLabel({
issue_number: context.issue.number,
name: labelName,
owner: context.repo.owner,
repo: context.repo.repo,
});
console.log(`Removed '${labelName}' label`);
}
on-issue-reopen:
runs-on: ubuntu-latest
permissions:
issues: write
if: github.event.action == 'reopened'
steps:
- name: Add needs-triage label on issue reopen
uses: actions-ecosystem/action-add-labels@v1
with:
labels: 'status: needs-triage'
on-issue-assigned:
runs-on: ubuntu-latest
permissions:
issues: write
if: >
github.event.action == 'assigned' &&
contains(github.event.issue.labels.*.name, 'status: needs-triage')
steps:
- name: Remove needs-triage label on issue assign
uses: actions-ecosystem/action-remove-labels@v1
with:
labels: 'status: needs-triage'
# on-pr-merge:
# runs-on: ubuntu-latest
# if: github.event.pull_request.merged == true
# steps:
# on-pr-close:
# runs-on: ubuntu-latest
# if: github.event_name == 'pull_request_target' && github.event.pull_request.merged == false
# steps:

View File

@@ -1,380 +0,0 @@
name: build
on:
pull_request:
types: [ opened, reopened, synchronize ]
push:
branches: [ 'main' ]
jobs:
changes:
runs-on: ubuntu-latest
permissions:
pull-requests: read
outputs:
needs_build: ${{ steps.filter.outputs.needs_build }}
templates: ${{ steps.filter.outputs.templates }}
steps:
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- uses: actions/checkout@v4
with:
fetch-depth: 25
- uses: dorny/paths-filter@v2
id: filter
with:
filters: |
needs_build:
- '.github/workflows/**'
- 'packages/**'
- 'test/**'
- 'pnpm-lock.yaml'
- 'package.json'
templates:
- 'templates/**'
- name: Log all filter results
run: |
echo "needs_build: ${{ steps.filter.outputs.needs_build }}"
echo "templates: ${{ steps.filter.outputs.templates }}"
core-build:
needs: changes
if: ${{ needs.changes.outputs.needs_build == 'true' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 25
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- name: Use Node.js 20
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install pnpm
uses: pnpm/action-setup@v3
with:
version: 9.7.0
run_install: false
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v4
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- run: pnpm install
- run: pnpm run build
- name: Cache build
uses: actions/cache@v4
with:
path: ./*
key: ${{ github.sha }}-${{ github.run_number }}
tests:
runs-on: ubuntu-latest
needs: core-build
strategy:
fail-fast: false
matrix:
database: [mongoose, postgres, postgres-custom-schema, postgres-uuid, supabase]
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: payloadtests
AWS_ENDPOINT_URL: http://127.0.0.1:4566
AWS_ACCESS_KEY_ID: localstack
AWS_SECRET_ACCESS_KEY: localstack
AWS_REGION: us-east-1
steps:
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- name: Use Node.js 20
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install pnpm
uses: pnpm/action-setup@v3
with:
version: 9.7.0
run_install: false
- name: Restore build
uses: actions/cache@v4
with:
path: ./*
key: ${{ github.sha }}-${{ github.run_number }}
- name: Start LocalStack
run: pnpm docker:start
- name: Start PostgreSQL
uses: CasperWA/postgresql-action@v1.2
with:
postgresql version: '14' # See https://hub.docker.com/_/postgres for available versions
postgresql db: ${{ env.POSTGRES_DB }}
postgresql user: ${{ env.POSTGRES_USER }}
postgresql password: ${{ env.POSTGRES_PASSWORD }}
if: startsWith(matrix.database, 'postgres')
- name: Install Supabase CLI
uses: supabase/setup-cli@v1
with:
version: latest
if: matrix.database == 'supabase'
- name: Initialize Supabase
run: |
supabase init
supabase start
if: matrix.database == 'supabase'
- name: Wait for PostgreSQL
run: sleep 30
if: startsWith(matrix.database, 'postgres')
- name: Configure PostgreSQL
run: |
psql "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" -c "CREATE ROLE runner SUPERUSER LOGIN;"
psql "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" -c "SELECT version();"
echo "POSTGRES_URL=postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" >> $GITHUB_ENV
if: startsWith(matrix.database, 'postgres')
- name: Configure PostgreSQL with custom schema
run: |
psql "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" -c "CREATE SCHEMA custom;"
if: matrix.database == 'postgres-custom-schema'
- name: Configure Supabase
run: |
echo "POSTGRES_URL=postgresql://postgres:postgres@127.0.0.1:54322/postgres" >> $GITHUB_ENV
if: matrix.database == 'supabase'
- name: Component Tests
run: pnpm test:components
- name: Integration Tests
run: pnpm test:int
env:
NODE_OPTIONS: --max-old-space-size=8096
PAYLOAD_DATABASE: ${{ matrix.database }}
POSTGRES_URL: ${{ env.POSTGRES_URL }}
tests-e2e:
runs-on: ubuntu-latest
needs: core-build
strategy:
fail-fast: false
matrix:
part: [ 1/8, 2/8, 3/8, 4/8, 5/8, 6/8, 7/8, 8/8 ]
steps:
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- name: Use Node.js 20
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install pnpm
uses: pnpm/action-setup@v3
with:
version: 9.7.0
run_install: false
- name: Restore build
uses: actions/cache@v4
with:
path: ./*
key: ${{ github.sha }}-${{ github.run_number }}
- name: E2E Tests
uses: nick-fields/retry@v2
with:
retry_on: error
max_attempts: 2
timeout_minutes: 15
command: pnpm test:e2e --part ${{ matrix.part }} --bail
- uses: actions/upload-artifact@v3
if: always()
with:
name: test-results
path: test-results/
retention-days: 1
tests-type-generation:
runs-on: ubuntu-latest
needs: core-build
steps:
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- name: Use Node.js 20
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install pnpm
uses: pnpm/action-setup@v3
with:
version: 9.7.0
run_install: false
- name: Restore build
uses: actions/cache@v4
with:
path: ./*
key: ${{ github.sha }}-${{ github.run_number }}
- name: Generate Payload Types
run: pnpm dev:generate-types fields
- name: Generate GraphQL schema file
run: pnpm dev:generate-graphql-schema graphql-schema-gen
build-packages:
runs-on: ubuntu-latest
needs: core-build
strategy:
fail-fast: false
matrix:
pkg:
- db-mongodb
- db-postgres
- bundler-webpack
- bundler-vite
- richtext-slate
- richtext-lexical
- live-preview
- live-preview-react
steps:
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- name: Use Node.js 20
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install pnpm
uses: pnpm/action-setup@v3
with:
version: 9.7.0
run_install: false
- name: Restore build
uses: actions/cache@v4
with:
path: ./*
key: ${{ github.sha }}-${{ github.run_number }}
- name: Build ${{ matrix.pkg }}
run: pnpm turbo run build --filter=${{ matrix.pkg }}
plugins:
runs-on: ubuntu-latest
needs: core-build
strategy:
fail-fast: false
matrix:
pkg:
- create-payload-app
- plugin-cloud
- plugin-cloud-storage
- plugin-form-builder
- plugin-nested-docs
- plugin-search
- plugin-sentry
- plugin-seo
steps:
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- name: Use Node.js 20
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install pnpm
uses: pnpm/action-setup@v3
with:
version: 9.7.0
run_install: false
- name: Restore build
uses: actions/cache@v4
with:
path: ./*
key: ${{ github.sha }}-${{ github.run_number }}
- name: Build ${{ matrix.pkg }}
run: pnpm turbo run build --filter=${{ matrix.pkg }}
- name: Test ${{ matrix.pkg }}
run: pnpm --filter ${{ matrix.pkg }} run test
if: matrix.pkg != 'create-payload-app' # degit doesn't work within GitHub Actions
templates:
needs: changes
if: ${{ needs.changes.outputs.templates == 'true' }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
template: [ blank, website, ecommerce ]
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 25
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- name: Use Node.js 20
uses: actions/setup-node@v4
with:
node-version: 20
- name: Start MongoDB
uses: supercharge/mongodb-github-action@1.10.0
with:
mongodb-version: 6.0
- name: Build Template
run: |
cd templates/${{ matrix.template }}
cp .env.example .env
yarn install
yarn build
yarn generate:types

View File

@@ -1,11 +0,0 @@
name: release-canary
on:
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Echo
run: echo "Register release-canary workflow"

View File

@@ -1,42 +0,0 @@
name: stale
on:
workflow_dispatch:
jobs:
stale:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v9
id: stale
with:
debug-only: true
days-before-stale: 90
days-before-close: 7
ascending: true
operations-per-run: 300
# Ignore all assigned
exempt-all-assignees: false
# Issues
stale-issue-label: 'stale'
exempt-issue-labels: 'blocked,must,should,keep,created-by: Payload team,created-by: Contributor'
stale-issue-message: >
This issue has been marked as stale due to lack of activity. To keep the ticket open, please indicate that it is still relevant in a comment below.
close-issue-message: >
This issue was automatically closed due to lack of activity.
# Pull Requests
stale-pr-label: 'stale'
exempt-pr-labels: 'blocked,must,should,keep,created-by: Payload team,created-by: Contributor'
stale-pr-message: >
This PR is stale due to lack of activity. To keep the PR open, please indicate that it is still relevant in a comment below.
close-pr-message: >
This pull request was automatically closed due to lack of activity.
- name: Print outputs
run: echo ${{ format('{0},{1}', toJSON(steps.stale.outputs.staled-issues-prs), toJSON(steps.stale.outputs.closed-issues-prs)) }}

88
.github/workflows/tests.yml vendored Normal file
View File

@@ -0,0 +1,88 @@
name: build
on:
pull_request:
types: [opened, reopened, synchronize]
push:
branches: ["master"]
jobs:
build_yarn:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x, 18.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org
scope: "@payloadcms"
always-auth: true
- name: Cache node modules
uses: actions/cache@v1
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- run: yarn
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- run: yarn build
- name: Component Tests
run: yarn test:components
- name: Integration Tests
run: yarn test:int
- name: Generate Payload Types
run: yarn dev:generate-types fields
- name: Generate GraphQL schema file
run: yarn dev:generate-graphql-schema graphql-schema-gen
- name: Install Playwright Browsers
run: npx playwright install --with-deps
- name: E2E Tests
run: yarn test:e2e --bail
- uses: actions/upload-artifact@v3
if: always()
with:
name: test-results
path: test-results/
retention-days: 30
install_npm:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org
scope: "@payloadcms"
always-auth: true
- name: Cache node modules
uses: actions/cache@v1
with:
path: ~/.npm
key: ${{ runner.os }}-node-npm-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-${{ env.cache-name }}-
${{ runner.os }}-npm-
${{ runner.os }}-
- run: npm install
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

215
.gitignore vendored
View File

@@ -1,21 +1,13 @@
coverage
package-lock.json
dist
/.idea/*
!/.idea/runConfigurations
.idea
test-results
.devcontainer
.localstack
/migrations
.localstack
# Created by https://www.toptal.com/developers/gitignore/api/node,macos,windows,webstorm,sublimetext,visualstudiocode
# Edit at https://www.toptal.com/developers/gitignore?templates=node,macos,windows,webstorm,sublimetext,visualstudiocode
# Created by https://www.gitignore.io/api/node,macos,windows,webstorm,sublimetext,visualstudiocode
### macOS ###
# General
.DS_Store
*.DS_Store
.AppleDouble
.LSOverride
@@ -38,10 +30,6 @@ Network Trash Folder
Temporary Items
.apdisk
### macOS Patch ###
# iCloud generated files
*.icloud
### Node ###
# Logs
logs
@@ -49,11 +37,6 @@ logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
@@ -66,12 +49,11 @@ lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
@@ -80,18 +62,15 @@ bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Typescript v1 declaration files
typings/
# Optional npm cache directory
.npm
@@ -99,15 +78,6 @@ web_modules/
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
@@ -117,87 +87,34 @@ web_modules/
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
# Yarn Berry
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
.pnp.*
### Node Patch ###
# Serverless Webpack directories
.webpack/
# dotenv environment variables file
.env
# Optional stylelint cache
# SvelteKit build / generate output
.svelte-kit
### SublimeText ###
# Cache files for Sublime Text
# cache files for sublime text
*.tmlanguage.cache
*.tmPreferences.cache
*.stTheme.cache
# Workspace files are user-specific
# workspace files are user-specific
*.sublime-workspace
# Project files should be checked into the repository, unless a significant
# proportion of contributors will probably not be using Sublime Text
# project files should be checked into the repository, unless a significant
# proportion of contributors will probably not be using SublimeText
# *.sublime-project
# SFTP configuration file
# sftp configuration file
sftp-config.json
sftp-config-alt*.json
# Package control specific files
Package Control.last-run
@@ -217,53 +134,84 @@ GitHub.sublime-settings
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide
### WebStorm ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff:
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/dictionaries
# Sensitive or high-churn files:
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.xml
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
# Gradle:
.idea/**/gradle.xml
.idea/**/libraries
# CMake
cmake-build-*/
cmake-build-debug/
# File-based project format
# MongoDB Explorer plugin:
.idea/**/mongoSettings.xml
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
out/
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Ruby plugin and RubyMine
/.rakeTasks
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
### WebStorm Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr
# Sonarlint plugin
.idea/sonarlint
### Windows ###
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
@@ -271,13 +219,26 @@ $RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
# End of https://www.toptal.com/developers/gitignore/api/node,macos,windows,webstorm,sublimetext,visualstudiocode
# End of https://www.gitignore.io/api/node,macos,windows,webstorm,sublimetext,visualstudiocode
/build
# Ignore all uploads
demo/upload
demo/media
demo/files
# Ignore build folder
build
# Ignore built components
components/index.js
components/styles.css
# Ignore generated
demo/generated-types.ts
demo/generated-schema.graphql

View File

@@ -1,4 +0,0 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
pnpm run lint-staged --quiet

View File

@@ -1,5 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run Dev Fields" type="NodeJSConfigurationType" application-parameters="fields" path-to-js-file="node_modules/.pnpm/nodemon@3.0.3/node_modules/nodemon/bin/nodemon.js" working-dir="$PROJECT_DIR$">
<method v="2" />
</configuration>
</component>

View File

@@ -1,5 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run Dev _community" type="NodeJSConfigurationType" application-parameters="_community" path-to-js-file="node_modules/.pnpm/nodemon@3.0.3/node_modules/nodemon/bin/nodemon.js" working-dir="$PROJECT_DIR$">
<method v="2" />
</configuration>
</component>

View File

@@ -0,0 +1,935 @@
{
"id": "4b6f2243-b055-45d8-9afa-53cd2b729a12",
"prevId": "00000000-0000-0000-0000-000000000000",
"version": "5",
"dialect": "pg",
"tables": {
"posts_my_array_my_sub_array": {
"name": "posts_my_array_my_sub_array",
"schema": "",
"columns": {
"_order": {
"name": "_order",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"_parent_id": {
"name": "_parent_id",
"type": "varchar",
"primaryKey": false,
"notNull": true
},
"id": {
"name": "id",
"type": "varchar",
"primaryKey": true,
"notNull": true
},
"sub_sub_field": {
"name": "sub_sub_field",
"type": "varchar",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"posts_my_array_my_sub_array__parent_id_posts_my_array_id_fk": {
"name": "posts_my_array_my_sub_array__parent_id_posts_my_array_id_fk",
"tableFrom": "posts_my_array_my_sub_array",
"tableTo": "posts_my_array",
"columnsFrom": [
"_parent_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"posts_my_array": {
"name": "posts_my_array",
"schema": "",
"columns": {
"_order": {
"name": "_order",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"_parent_id": {
"name": "_parent_id",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"id": {
"name": "id",
"type": "varchar",
"primaryKey": true,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"posts_my_array__parent_id_posts_id_fk": {
"name": "posts_my_array__parent_id_posts_id_fk",
"tableFrom": "posts_my_array",
"tableTo": "posts",
"columnsFrom": [
"_parent_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"posts_my_array_locales": {
"name": "posts_my_array_locales",
"schema": "",
"columns": {
"sub_field": {
"name": "sub_field",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"_locale": {
"name": "_locale",
"type": "_locales",
"primaryKey": false,
"notNull": true
},
"_parent_id": {
"name": "_parent_id",
"type": "varchar",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"posts_my_array_locales__parent_id_posts_my_array_id_fk": {
"name": "posts_my_array_locales__parent_id_posts_my_array_id_fk",
"tableFrom": "posts_my_array_locales",
"tableTo": "posts_my_array",
"columnsFrom": [
"_parent_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"posts_block1": {
"name": "posts_block1",
"schema": "",
"columns": {
"_order": {
"name": "_order",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"_path": {
"name": "_path",
"type": "text",
"primaryKey": false,
"notNull": true
},
"_parent_id": {
"name": "_parent_id",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"id": {
"name": "id",
"type": "varchar",
"primaryKey": true,
"notNull": true
},
"non_localized_text": {
"name": "non_localized_text",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"block_name": {
"name": "block_name",
"type": "varchar",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"posts_block1__parent_id_posts_id_fk": {
"name": "posts_block1__parent_id_posts_id_fk",
"tableFrom": "posts_block1",
"tableTo": "posts",
"columnsFrom": [
"_parent_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"posts_block1_locales": {
"name": "posts_block1_locales",
"schema": "",
"columns": {
"localized_text": {
"name": "localized_text",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"_locale": {
"name": "_locale",
"type": "_locales",
"primaryKey": false,
"notNull": true
},
"_parent_id": {
"name": "_parent_id",
"type": "varchar",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"posts_block1_locales__parent_id_posts_block1_id_fk": {
"name": "posts_block1_locales__parent_id_posts_block1_id_fk",
"tableFrom": "posts_block1_locales",
"tableTo": "posts_block1",
"columnsFrom": [
"_parent_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"posts_block2_block_array": {
"name": "posts_block2_block_array",
"schema": "",
"columns": {
"_order": {
"name": "_order",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"_parent_id": {
"name": "_parent_id",
"type": "varchar",
"primaryKey": false,
"notNull": true
},
"id": {
"name": "id",
"type": "varchar",
"primaryKey": true,
"notNull": true
},
"sub_block_array": {
"name": "sub_block_array",
"type": "varchar",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"posts_block2_block_array__parent_id_posts_block2_id_fk": {
"name": "posts_block2_block_array__parent_id_posts_block2_id_fk",
"tableFrom": "posts_block2_block_array",
"tableTo": "posts_block2",
"columnsFrom": [
"_parent_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"posts_block2": {
"name": "posts_block2",
"schema": "",
"columns": {
"_order": {
"name": "_order",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"_path": {
"name": "_path",
"type": "text",
"primaryKey": false,
"notNull": true
},
"_parent_id": {
"name": "_parent_id",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"id": {
"name": "id",
"type": "varchar",
"primaryKey": true,
"notNull": true
},
"number": {
"name": "number",
"type": "numeric",
"primaryKey": false,
"notNull": false
},
"block_name": {
"name": "block_name",
"type": "varchar",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"posts_block2__parent_id_posts_id_fk": {
"name": "posts_block2__parent_id_posts_id_fk",
"tableFrom": "posts_block2",
"tableTo": "posts",
"columnsFrom": [
"_parent_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"posts_my_group_group_array": {
"name": "posts_my_group_group_array",
"schema": "",
"columns": {
"_order": {
"name": "_order",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"_parent_id": {
"name": "_parent_id",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"id": {
"name": "id",
"type": "varchar",
"primaryKey": true,
"notNull": true
},
"group_array_text": {
"name": "group_array_text",
"type": "varchar",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"posts_my_group_group_array__parent_id_posts_id_fk": {
"name": "posts_my_group_group_array__parent_id_posts_id_fk",
"tableFrom": "posts_my_group_group_array",
"tableTo": "posts",
"columnsFrom": [
"_parent_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"posts": {
"name": "posts",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"my_group_sub_field": {
"name": "my_group_sub_field",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"my_group_sub_group_sub_sub_field": {
"name": "my_group_sub_group_sub_sub_field",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"posts_locales": {
"name": "posts_locales",
"schema": "",
"columns": {
"title": {
"name": "title",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"number": {
"name": "number",
"type": "numeric",
"primaryKey": false,
"notNull": false
},
"my_group_sub_field_localized": {
"name": "my_group_sub_field_localized",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"my_group_sub_group_sub_sub_field_localized": {
"name": "my_group_sub_group_sub_sub_field_localized",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"_locale": {
"name": "_locale",
"type": "_locales",
"primaryKey": false,
"notNull": true
},
"_parent_id": {
"name": "_parent_id",
"type": "integer",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"posts_locales__parent_id_posts_id_fk": {
"name": "posts_locales__parent_id_posts_id_fk",
"tableFrom": "posts_locales",
"tableTo": "posts",
"columnsFrom": [
"_parent_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"posts_relationships": {
"name": "posts_relationships",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"parent_id": {
"name": "parent_id",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"path": {
"name": "path",
"type": "varchar",
"primaryKey": false,
"notNull": true
},
"order": {
"name": "order",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"pages_id": {
"name": "pages_id",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"people_id": {
"name": "people_id",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"posts_id": {
"name": "posts_id",
"type": "integer",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"posts_relationships_parent_id_posts_id_fk": {
"name": "posts_relationships_parent_id_posts_id_fk",
"tableFrom": "posts_relationships",
"tableTo": "posts",
"columnsFrom": [
"parent_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
},
"posts_relationships_pages_id_pages_id_fk": {
"name": "posts_relationships_pages_id_pages_id_fk",
"tableFrom": "posts_relationships",
"tableTo": "pages",
"columnsFrom": [
"pages_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
},
"posts_relationships_people_id_people_id_fk": {
"name": "posts_relationships_people_id_people_id_fk",
"tableFrom": "posts_relationships",
"tableTo": "people",
"columnsFrom": [
"people_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
},
"posts_relationships_posts_id_posts_id_fk": {
"name": "posts_relationships_posts_id_posts_id_fk",
"tableFrom": "posts_relationships",
"tableTo": "posts",
"columnsFrom": [
"posts_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"pages": {
"name": "pages",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"slug": {
"name": "slug",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"people": {
"name": "people",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"full_name": {
"name": "full_name",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"users": {
"name": "users",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"email": {
"name": "email",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"reset_password_token": {
"name": "reset_password_token",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"salt": {
"name": "salt",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"hash": {
"name": "hash",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"login_attempts": {
"name": "login_attempts",
"type": "numeric",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {
"email_idx": {
"name": "email_idx",
"columns": [
"email"
],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"payload_preferences": {
"name": "payload_preferences",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"key": {
"name": "key",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"value": {
"name": "value",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"payload_preferences_relationships": {
"name": "payload_preferences_relationships",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"parent_id": {
"name": "parent_id",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"path": {
"name": "path",
"type": "varchar",
"primaryKey": false,
"notNull": true
},
"order": {
"name": "order",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"users_id": {
"name": "users_id",
"type": "integer",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"payload_preferences_relationships_parent_id_payload_preferences_id_fk": {
"name": "payload_preferences_relationships_parent_id_payload_preferences_id_fk",
"tableFrom": "payload_preferences_relationships",
"tableTo": "payload_preferences",
"columnsFrom": [
"parent_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
},
"payload_preferences_relationships_users_id_users_id_fk": {
"name": "payload_preferences_relationships_users_id_users_id_fk",
"tableFrom": "payload_preferences_relationships",
"tableTo": "users",
"columnsFrom": [
"users_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"payload_migrations": {
"name": "payload_migrations",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"name": {
"name": "name",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"batch": {
"name": "batch",
"type": "numeric",
"primaryKey": false,
"notNull": false
},
"schema": {
"name": "schema",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {
"_locales": {
"name": "_locales",
"values": {
"en": "en",
"es": "es"
}
}
},
"schemas": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
}
}

View File

@@ -1 +1 @@
v18.17.1
v16.14.2

2
.npmrc
View File

@@ -1,2 +0,0 @@
symlink=true
node-linker=isolated # due to a typescript bug, isolated mode requires @types/express-serve-static-core, terser and monaco-editor to be installed https://github.com/microsoft/TypeScript/issues/47663#issuecomment-1519138189 along with two other changes in the code which I've marked with (tsbugisolatedmode) in the code

2
.nvmrc
View File

@@ -1 +1 @@
v18.17.1
v16.14.2

View File

@@ -1,11 +0,0 @@
.tmp
**/.git
**/.hg
**/.pnp.*
**/.svn
**/.yarn/**
**/build
**/dist/**
**/node_modules
**/temp
**/docs/**

View File

@@ -1,6 +0,0 @@
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100,
"semi": false
}

22
.release-it.json Normal file
View File

@@ -0,0 +1,22 @@
{
"verbose": true,
"git": {
"commitMessage": "chore(release): v${version}",
"requireCleanWorkingDir": true
},
"github": {
"release": true
},
"npm": {
"skipChecks": true
},
"hooks": {
"before:init": ["yarn", "yarn clean", "yarn test"]
},
"plugins": {
"@release-it/conventional-changelog": {
"preset": "angular",
"infile": "CHANGELOG.md"
}
}
}

View File

@@ -1,16 +0,0 @@
module.exports = {
verbose: true,
git: {
requireCleanWorkingDir: false,
commit: false,
push: false,
tag: false,
},
npm: {
skipChecks: true,
tag: 'beta',
},
hooks: {
'before:init': ['pnpm install', 'pnpm clean', 'pnpm build'],
},
}

25
.release-it.pre.json Normal file
View File

@@ -0,0 +1,25 @@
{
"verbose": true,
"git": {
"requireCleanWorkingDir": false,
"commit": false,
"push": false,
"tag": false
},
"github": {
"release": true
},
"npm": {
"skipChecks": true,
"tag": "canary"
},
"hooks": {
"before:init": ["yarn", "yarn clean", "yarn test"]
},
"plugins": {
"@release-it/conventional-changelog": {
"preset": "angular",
"infile": "CHANGELOG.md"
}
}
}

View File

@@ -1,3 +0,0 @@
{
"recommendations": ["esbenp.prettier-vscode", "dbaeumer.vscode-eslint"]
}

210
.vscode/launch.json vendored
View File

@@ -1,198 +1,112 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"command": "pnpm run dev _community",
"cwd": "${workspaceFolder}",
"command": "yarn run dev _community",
"name": "Run Dev Community",
"request": "launch",
"type": "node-terminal"
},
{
"command": "pnpm run dev plugin-cloud-storage",
"cwd": "${workspaceFolder}",
"name": "Run Dev - plugin-cloud-storage",
"request": "launch",
"type": "node-terminal",
"env": {
"PAYLOAD_PUBLIC_CLOUD_STORAGE_ADAPTER": "s3"
}
"cwd": "${workspaceFolder}"
},
{
"command": "pnpm run dev collections-graphql",
"cwd": "${workspaceFolder}",
"name": "Run Dev GraphQL",
"request": "launch",
"type": "node-terminal"
},
{
"command": "pnpm run dev fields",
"cwd": "${workspaceFolder}",
"command": "yarn run dev fields",
"name": "Run Dev Fields",
"request": "launch",
"type": "node-terminal"
"type": "node-terminal",
"cwd": "${workspaceFolder}"
},
{
"command": "pnpm run dev:postgres fields",
"cwd": "${workspaceFolder}",
"command": "yarn run dev:postgres postgres -- -I", // Allow input
"name": "Run Dev Postgres",
"request": "launch",
"type": "node-terminal"
"type": "node-terminal",
"cwd": "${workspaceFolder}"
},
{
"command": "pnpm run dev versions",
"cwd": "${workspaceFolder}",
"command": "yarn run dev versions",
"name": "Run Dev Versions",
"request": "launch",
"type": "node-terminal"
},
{
"command": "pnpm run dev localization",
"cwd": "${workspaceFolder}",
"name": "Run Dev Localization",
"request": "launch",
"type": "node-terminal"
},
{
"command": "pnpm run dev field-error-states",
"cwd": "${workspaceFolder}",
"name": "Run Dev Field Error States",
"request": "launch",
"type": "node-terminal"
},
{
"command": "pnpm run dev uploads",
"cwd": "${workspaceFolder}",
"name": "Run Dev Uploads",
"request": "launch",
"type": "node-terminal"
},
{
"command": "PAYLOAD_BUNDLER=vite pnpm run dev fields",
"cwd": "${workspaceFolder}",
"name": "Run Dev Fields (Vite)",
"request": "launch",
"type": "node-terminal",
"env": {
"NODE_ENV": "production"
}
"cwd": "${workspaceFolder}"
},
{
"command": "pnpm run test:int live-preview",
"cwd": "${workspaceFolder}",
"name": "Live Preview Int Tests",
"type": "node-terminal",
"command": "ts-node src/bin/migrate.ts migrate",
"request": "launch",
"type": "node-terminal"
},
{
"command": "pnpm run test:int plugin-search",
"cwd": "${workspaceFolder}",
"name": "Search Plugin Int Tests",
"request": "launch",
"type": "node-terminal"
},
{
"command": "ts-node ./packages/payload/src/bin/index.ts build",
"env": {
"PAYLOAD_CONFIG_PATH": "test/fields/config.ts",
"PAYLOAD_BUNDLER": "vite",
"DISABLE_SWC": "true" // SWC messes up debugging the bin scripts
},
"name": "Build CLI - Vite",
"outputCapture": "std",
"request": "launch",
"type": "node-terminal"
},
{
"command": "ts-node ./packages/payload/src/bin/index.ts build",
"env": {
"PAYLOAD_CONFIG_PATH": "test/fields/config.ts",
"PAYLOAD_ANALYZE_BUNDLE": "true",
"DISABLE_SWC": "true" // SWC messes up debugging the bin scripts
},
"name": "Build CLI - Webpack",
"outputCapture": "std",
"request": "launch",
"type": "node-terminal"
},
{
"command": "ts-node ./packages/payload/src/bin/index.ts generate:types",
"env": {
"PAYLOAD_CONFIG_PATH": "test/_community/config.ts",
"DISABLE_SWC": "true" // SWC messes up debugging the bin scripts
},
"name": "Generate Types CLI",
"outputCapture": "std",
"request": "launch",
"type": "node-terminal"
},
{
"command": "ts-node ./packages/payload/src/bin/index.ts migrate:status",
"name": "Migrate CLI - migrate",
"env": {
"PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
"PAYLOAD_DATABASE": "postgres",
"DISABLE_SWC": "true" // SWC messes up debugging the bin scripts
// "PAYLOAD_DROP_DATABASE": "true",
},
"outputCapture": "std",
},
{
"type": "node-terminal",
"command": "ts-node src/bin/migrate.ts migrate:status",
"request": "launch",
"name": "Migrate CLI - status",
"outputCapture": "std",
"request": "launch",
"type": "node-terminal"
},
{
"command": "ts-node ./packages/payload/src/bin/index.ts migrate:create yass",
"env": {
"PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
"PAYLOAD_DATABASE": "postgres",
"DISABLE_SWC": "true" // SWC messes up debugging the bin scripts
// "PAYLOAD_DROP_DATABASE": "true",
},
"outputCapture": "std",
},
{
"type": "node-terminal",
"command": "ts-node src/bin/migrate.ts migrate:create yass",
"request": "launch",
"name": "Migrate CLI - create",
"outputCapture": "std",
"request": "launch",
"type": "node-terminal"
},
{
"command": "ts-node ./packages/payload/src/bin/index.ts migrate:down",
"env": {
"PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
"PAYLOAD_DATABASE": "mongoose",
"DISABLE_SWC": "true" // SWC messes up debugging the bin scripts
// "PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
"PAYLOAD_CONFIG_PATH": "test/postgres/config.ts",
"PAYLOAD_DATABASE": "postgres",
// "PAYLOAD_DROP_DATABASE": "true",
},
"outputCapture": "std",
},
{
"type": "node-terminal",
"command": "ts-node src/bin/migrate.ts migrate:down",
"request": "launch",
"name": "Migrate CLI - down",
"outputCapture": "std",
"request": "launch",
"type": "node-terminal"
},
{
"command": "ts-node ./packages/payload/src/bin/index.ts migrate:reset",
"env": {
"PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
"PAYLOAD_DATABASE": "mongoose",
"DISABLE_SWC": "true" // SWC messes up debugging the bin scripts
// "PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
"PAYLOAD_CONFIG_PATH": "test/postgres/config.ts",
"PAYLOAD_DATABASE": "postgres",
// "PAYLOAD_DROP_DATABASE": "true",
},
"outputCapture": "std",
},
{
"type": "node-terminal",
"command": "ts-node src/bin/migrate.ts migrate:reset",
"request": "launch",
"name": "Migrate CLI - reset",
"outputCapture": "std",
"request": "launch",
"type": "node-terminal"
},
{
"command": "ts-node ./packages/payload/src/bin/index.ts migrate:refresh",
"env": {
"PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
"PAYLOAD_DATABASE": "mongoose",
"DISABLE_SWC": "true" // SWC messes up debugging the bin scripts
// "PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
"PAYLOAD_CONFIG_PATH": "test/postgres/config.ts",
"PAYLOAD_DATABASE": "postgres",
// "PAYLOAD_DROP_DATABASE": "true",
},
"name": "Migrate CLI - refresh",
"outputCapture": "std",
},
{
"type": "node-terminal",
"command": "ts-node src/bin/migrate.ts migrate:refresh",
"request": "launch",
"type": "node-terminal"
}
],
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0"
"name": "Migrate CLI - refresh",
"env": {
// "PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
"PAYLOAD_CONFIG_PATH": "test/postgres/config.ts",
"PAYLOAD_DATABASE": "postgres",
// "PAYLOAD_DROP_DATABASE": "true",
},
"outputCapture": "std",
},
]
}

43
.vscode/settings.json vendored
View File

@@ -1,43 +0,0 @@
{
"npm.packageManager": "pnpm",
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"editor.formatOnSaveMode": "file",
// All ESLint rules to 'warn' to differentate from TypeScript's 'error' level
"eslint.rules.customizations": [{ "rule": "*", "severity": "warn" }],
"typescript.tsdk": "node_modules/typescript/lib",
// Load .git-blame-ignore-revs file
"gitlens.advanced.blame.customArguments": ["--ignore-revs-file", ".git-blame-ignore-revs"],
"jestrunner.jestCommand": "pnpm exec cross-env NODE_OPTIONS=\"--experimental-vm-modules --no-deprecation\" node 'node_modules/jest/bin/jest.js'",
"jestrunner.debugOptions": {
"runtimeArgs": ["--experimental-vm-modules", "--no-deprecation"]
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -14,7 +14,7 @@ If you find a vulnerability within the core Payload repository, and we determine
## Documentation edits
Payload documentation can be found directly within its codebase, and you can feel free to make changes / improvements to any of it through opening a PR. We utilize these files directly in our website and will periodically deploy documentation updates as necessary.
Payload documentation can be found directly within its codebase and you can feel free to make changes / improvements to any of it through opening a PR. We utilize these files directly in our website and will periodically deploy documentation updates as necessary.
## Building additional features
@@ -30,17 +30,9 @@ Our design review ensures that proposed changes fit seamlessly with other compon
To help us work on new features, you can create a new feature request post in [GitHub Discussion](https://github.com/payloadcms/payload/discussions) or discuss it in our [Discord](https://discord.com/invite/payload). New functionality often has large implications across the entire Payload repo, so it is best to discuss the architecture and approach before starting work on a pull request.
### Installation & Requirements
Payload is structured as a Monorepo, encompassing not only the core Payload platform but also various plugins and packages. To install all required dependencies, you have to run `pnpm install` once in the root directory. **PNPM IS REQUIRED!** Yarn or npm will not work - you will have to use pnpm to develop in the core repository. In most systems, the easiest way to install pnpm is to run `corepack enable` in your terminal.
If you're coming from a very outdated version of payload, it is recommended to nuke the node_modules folder before running pnpm install. On UNIX systems, you can easily do that using the `pnpm clean:unix` command, which will delete all node_modules folders and build artefacts.
It is also recommended to use at least Node v18 or higher. You can check your current node version by typing `node --version` in your terminal. The easiest way to switch between different node versions is to use [nvm](https://github.com/nvm-sh/nvm#intro).
### Code
Most new functionality should keep testing in mind. All top-level directories within the `test/` directory are for testing a specific category: `fields`, `collections`, etc.
Most new functionality should keep testing in mind. With 1.0, testability of new features has been vastly improved. All top-level directories within the `test/` directory are for testing a specific category: `fields`, `collections`, etc.
If it makes sense to add your feature to an existing test directory, please do so.
@@ -57,35 +49,21 @@ A typical directory with `test/` will be structured like this:
- `config.ts` - This is the _granular_ Payload config for testing. It should be as lightweight as possible. Reference existing configs for an example
- `int.spec.ts` - This is the test file run by jest. Any test file must have a `*int.spec.ts` suffix.
- `e2e.spec.ts` - This is the end-to-end test file that will load up the admin UI using the above config and run Playwright tests. These tests are typically only needed if a large change is being made to the Admin UI.
- `payload-types.ts` - Generated types from `config.ts`. Generate this file by running `pnpm dev:generate-types my-test-dir`. Replace `my-test-dir` with the name of your testing directory.
- `payload-types.ts` - Generated types from `config.ts`. Generate this file by running `yarn dev:generate-types my-test-dir`.
Each test directory is split up in this way specifically to reduce friction when creating tests and to add the ability to boot up Payload with that specific config.
The directory split up in this way specifically to reduce friction when creating tests and to add the ability to boot up Payload with that specific config.
The following command will start Payload with your config: `pnpm dev my-test-dir`. Example: `pnpm dev fields` for the test/`fields` test suite. This command will start up Payload using your config and refresh a test database on every restart. If you're using VS Code, the most common run configs are automatically added to your editor - you should be able to find them in your VS Code launch tab.
The following command will start Payload with your config: `yarn dev my-test-dir`. This command will start up Payload using your config and refresh a test database on every restart.
By default, payload will [automatically log you in](https://payloadcms.com/docs/authentication/config#admin-autologin) with the default credentials. To disable that, you can either pass in the --no-auto-login flag (example: `pnpm dev my-test-dir --no-auto-login`) or set the `PAYLOAD_PUBLIC_DISABLE_AUTO_LOGIN` environment variable to `false`.
By default, it will automatically log you in with the default credentials. To disable that, you can either pass in the --no-auto-login flag (example: `yarn dev my-test-dir --no-auto-login`) or set the `PAYLOAD_PUBLIC_DISABLE_AUTO_LOGIN` environment variable to `false`.
The default credentials are `dev@payloadcms.com` as E-Mail and `test` as password. These are used in the auto-login.
### Testing with your own MongoDB database
If you wish to use your own MongoDB database for the `test` directory instead of using the in memory database, all you need to do is add the following env vars to the `test/dev.ts` file:
If you wish to use to your own Mongo database for the `test` directory instead of using the in memory database, all you need to do is add the following env vars to the `test/dev.ts` file:
- `process.env.NODE_ENV`
- `process.env.PAYLOAD_TEST_MONGO_URL`
- Simply set `process.env.NODE_ENV` to `test` and set `process.env.PAYLOAD_TEST_MONGO_URL` to your MongoDB URL e.g. `mongodb://127.0.0.1/your-test-db`.
- Simply set `process.env.NODE_ENV` to `test` and set `process.env.PAYLOAD_TEST_MONGO_URL` to your mongo url e.g. `mongodb://127.0.0.1/your-test-db`.
### Using Postgres
If you have postgres installed on your system, you can also run the test suites using postgres. By default, mongodb is used.
To do that, simply set the `PAYLOAD_DATABASE` environment variable to `postgres`.
### Running the e2e and int tests
You can run the entire test suite using `pnpm test`. If you wish to only run e2e tests, you can use `pnpm test:e2e`. If you wish to only run int tests, you can use `pnpm test:int`.
By default, `pnpm test:int` will only run int test against MongoDB. To run int tests against postgres, you can use `pnpm test:int:postgres`. You will have to have postgres installed on your system for this to work.
NOTE: It is recommended to add the test credentials (located in `test/credentials.ts`) to your autofill for `localhost:3000/admin` as this will be required on every nodemon restart. The default credentials are `dev@payloadcms.com` as E-Mail and `test` as password.
### Commits
@@ -111,14 +89,3 @@ If you are committing to [templates](./templates) or [examples](./examples), use
## Pull Requests
For all Pull Requests, you should be extremely descriptive about both your problem and proposed solution. If there are any affected open or closed issues, please leave the issue number in your PR message.
## Previewing docs
This is how you can preview changes you made locally to the docs:
1. Clone our [website repository](https://github.com/payloadcms/website)
2. Run `yarn install`
3. Duplicate the `.env.example` file and rename it to `.env`
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/)

View File

@@ -6,10 +6,9 @@ To report an issue, please follow the steps below:
2. Add necessary collections/globals/fields to the `test/_community` directory to recreate the issue you are experiencing
3. Create an issue and add a link to your forked repo
**The goal is to isolate the problem by reducing the number of fields/collections you add to the test/\_community folder. This folder is not meant for you to copy your project into, but to recreate the issue you are experiencing with minimal config.**
**The goal is to isolate the problem by reducing the number of fields/collections you add to the test/_community folder. This folder is not meant for you to copy your project into, but to recreate the issue you are experiencing with minimal config.**
## Test directory file tree explanation
```text
.
├── config.ts
@@ -21,49 +20,45 @@ To report an issue, please follow the steps below:
- `config.ts` - This is the _granular_ Payload config for testing. It should be as lightweight as possible. Reference existing configs for an example
- `int.spec.ts` [Optional] - This is the test file run by jest. Any test file must have a `*int.spec.ts` suffix.
- `e2e.spec.ts` [Optional] - This is the end-to-end test file that will load up the admin UI using the above config and run Playwright tests.
- `payload-types.ts` - Generated types from `config.ts`. Generate this file by running `pnpm dev:generate-types _community`.
- `payload-types.ts` - Generated types from `config.ts`. Generate this file by running `yarn dev:generate-types _community`.
The directory split up in this way specifically to reduce friction when creating tests and to add the ability to boot up Payload with that specific config. You should modify the files in `test/_community` to get started.
## How to start test collection admin UI
To start the admin panel so you can manually recreate your issue, you can run the following command:
```bash
# This command will start up Payload using your config
# NOTE: it will wipe the test database on restart
pnpm dev _community
```
```bash
# This command will start up Payload using your config
# NOTE: it will wipe the test database on restart
yarn dev _community
```
## Testing is optional but encouraged
An issue does not need to have failing tests — reproduction steps with your forked repo are enough at this point. Some people like to dive deeper and we want to give you the guidance/tools to do so. Read more below.
### How to run integration tests (Payload API tests)
There are a couple ways to do this:
- **Granularly** - you can run individual tests in vscode by installing the Jest Runner plugin and using that to run individual tests. Clicking the `debug` button will run the test in debug mode allowing you to set break points.
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/src/admin/assets/images/github/int-debug.png" />
<img src="https://raw.githubusercontent.com/payloadcms/payload/master/src/admin/assets/images/github/int-debug.png" />
- **Manually** - you can run all int tests in the `/test/_community/int.spec.ts` file by running the following command:
```bash
pnpm test:int _community
yarn test:int _community
```
### How to run E2E tests (Admin Panel UI tests)
The easiest way to run E2E tests is to install
- [Playwright Test for VSCode](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright)
- [Playwright Runner](https://marketplace.visualstudio.com/items?itemName=ortoni.ortoni)
Once they are installed you can open the `testing` tab in vscode sidebar and drill down to the test you want to run, i.e. `/test/_community/e2e.spec.ts`
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/src/admin/assets/images/github/e2e-debug.png" />
<img src="https://raw.githubusercontent.com/payloadcms/payload/master/src/admin/assets/images/github/e2e-debug.png" />
#### Notes
- It is recommended to add the test credentials (located in `test/credentials.ts`) to your autofill for `localhost:3000/admin` as this will be required on every nodemon restart. The default credentials are `dev@payloadcms.com` as email and `test` as password.

View File

@@ -1,24 +1,30 @@
<a href="https://payloadcms.com"><img width="100%" src="https://github.com/payloadcms/payload/blob/main/packages/payload/src/admin/assets/images/github-banner-alt.jpg?raw=true" alt="Payload headless CMS Admin panel built with React" /></a>
<a href="https://payloadcms.com">
<img width="100%" src="src/admin/assets/images/github-banner-alt.jpg" alt="Payload headless CMS Admin panel built with React" />
</a>
<br />
<br />
<p align="left">
<a href="https://github.com/payloadcms/payload/actions"><img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/payloadcms/payload/main.yml?style=flat-square"></a>
<a href="https://github.com/payloadcms/payload/actions">
<img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/payloadcms/payload/tests.yml?style=flat-square">
</a>
&nbsp;
<a href="https://discord.gg/payload"><img alt="Discord" src="https://img.shields.io/discord/967097582721572934?label=Discord&color=7289da&style=flat-square" /></a>
<a href="https://discord.gg/payload">
<img alt="Discord" src="https://img.shields.io/discord/967097582721572934?label=Discord&color=7289da&style=flat-square" />
</a>
&nbsp;
<a href="https://www.npmjs.com/package/payload"><img alt="npm" src="https://img.shields.io/npm/v/payload?style=flat-square" /></a>
<a href="https://www.npmjs.com/package/payload">
<img alt="npm" src="https://img.shields.io/npm/v/payload?style=flat-square" />
</a>
&nbsp;
<a href="https://twitter.com/payloadcms"><img src="https://img.shields.io/badge/follow-payloadcms-1DA1F2?logo=twitter&style=flat-square" alt="Payload Twitter" /></a>
<a href="https://twitter.com/payloadcms">
<img src="https://img.shields.io/badge/follow-payloadcms-1DA1F2?logo=twitter&style=flat-square" alt="Payload Twitter" />
</a>
</p>
<hr/>
<h4>
<a target="_blank" href="https://payloadcms.com/docs/getting-started/what-is-payload" rel="dofollow"><strong>Explore the Docs</strong></a>&nbsp;·&nbsp;<a target="_blank" href="https://payloadcms.com/community-help" rel="dofollow"><strong>Community Help</strong></a>&nbsp;·&nbsp;<a target="_blank" href="https://demo.payloadcms.com/" rel="dofollow"><strong>Try Live Demo</strong></a>&nbsp;·&nbsp;<a target="_blank" href="https://github.com/payloadcms/payload/discussions/1539" rel="dofollow"><strong>Roadmap</strong></a>&nbsp;·&nbsp;<a target="_blank" href="https://www.g2.com/products/payload-cms/reviews#reviews" rel="dofollow"><strong>View G2 Reviews</strong></a>
</h4>
<hr/>
> [!IMPORTANT]
> 🎉 <strong>Payload 3.0 beta released!</strong> You can now deploy Payload fully in any Next.js app folder. Read more in the <a target="_blank" href="https://payloadcms.com/blog/30-beta-install-payload-into-any-nextjs-app-with-one-line" rel="dofollow"><strong>announcement post</strong></a>.
<h3>Benefits over a regular CMS</h3>
<ul>
<li>Dont hit some third-party SaaS API, hit your own API</li>
@@ -33,7 +39,6 @@
</ul>
## ☁️ Deploy instantly with Payload Cloud.
Create a cloud account, connect your GitHub, and [deploy in minutes](https://payloadcms.com/new).
## 🚀 Get started by self-hosting completely free, forever.
@@ -41,27 +46,18 @@ Create a cloud account, connect your GitHub, and [deploy in minutes](https://pay
Before beginning to work with Payload, make sure you have all of the [required software](https://payloadcms.com/docs/getting-started/installation).
```text
npx create-payload-app@latest
npx create-payload-app
```
Alternatively, it only takes about five minutes to [create an app from scratch](https://payloadcms.com/docs/getting-started/installation#from-scratch).
## 🖱️ One-click templates
### 🛒 [E-Commerce](https://github.com/payloadcms/payload/tree/master/templates/ecommerce)
Eliminate the need to combine Shopify and a CMS, and instead do it all with Payload + Stripe. Best of all, you can extend it as much as you need.
Jumpstart your next project by starting with a pre-made template. These are production-ready, end-to-end solutions designed to get you to market as fast as possible.
[All Official Templates](https://github.com/orgs/payloadcms/repositories?q=topic%3Apayload-template)&nbsp;·&nbsp;[Community Templates](https://github.com/topics/payload-template)
### [🛒 E-Commerce](https://github.com/payloadcms/payload/tree/main/templates/ecommerce)
Eliminate the need to combine Shopify and a CMS, and instead do it all with Payload + Stripe. Comes with a beautiful, fully functional front-end complete with shopping cart, checkout, orders, and much more.
### [🌐 Website](https://github.com/payloadcms/payload/tree/main/templates/website)
Build any kind of website, blog, or portfolio from small to enterprise. Comes with a beautiful, fully functional front-end complete with posts, projects, comments, and much more.
We're constantly adding more templates to our [Templates Directory](https://github.com/payloadcms/payload/tree/main/templates). If you maintain your own template, consider adding the `payload-template` topic to your GitHub repository for others to find.
- [Official Templates](https://github.com/payloadcms/payload/tree/main/templates)
- [Community Templates](https://github.com/topics/payload-template)
**If you maintain your own template, consider adding the `payload-template` topic to your GitHub repository for others to find.**
## ✨ Features
@@ -89,19 +85,15 @@ We're constantly adding more templates to our [Templates Directory](https://gith
Check out the [Payload website](https://payloadcms.com/docs/getting-started/what-is-payload) to find in-depth documentation for everything that Payload offers.
Migrating from v1 to v2? Check out the [2.0 Release Notes](https://github.com/payloadcms/payload/releases/tag/v2.0.0) on how to do it.
## 🙋 Contributing
If you want to add contributions to this repository, please follow the instructions in [contributing.md](./CONTRIBUTING.md).
If you want to add contributions to this repository, please follow the instructions in [contributing.md](./contributing.md).
## 📚 Examples
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.
The examples directory is a great resource for learning how to setup Payload in a variety of different ways.
- [Examples Directory](./examples)
- [Payload Blog](https://payloadcms.com/blog)
- [Payload YouTube](https://www.youtube.com/@payloadcms)
[Examples Directory](./examples)
## 🔌 Plugins

1
auth.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
export * from './dist/auth';

1
auth.js Normal file
View File

@@ -0,0 +1 @@
module.exports = require('./dist/auth');

2
bin.js Executable file
View File

@@ -0,0 +1,2 @@
#!/usr/bin/env node
require('./dist/bin');

View File

@@ -1,39 +0,0 @@
module.exports = {
// gitRawCommitsOpts: {
// from: 'v2.0.9',
// path: 'packages/payload',
// },
// infile: 'CHANGELOG.md',
options: {
preset: {
name: 'conventionalcommits',
types: [
{ section: 'Features', type: 'feat' },
{ section: 'Features', type: 'feature' },
{ section: 'Bug Fixes', type: 'fix' },
{ section: 'Documentation', type: 'docs' },
],
},
},
// outfile: 'NEW.md',
writerOpts: {
commitGroupsSort: (a, b) => {
const groupOrder = ['Features', 'Bug Fixes', 'Documentation']
return groupOrder.indexOf(a.title) - groupOrder.indexOf(b.title)
},
// Scoped commits at the end, alphabetical sort
commitsSort: (a, b) => {
if (a.scope || b.scope) {
if (!a.scope) return -1
if (!b.scope) return 1
return a.scope === b.scope
? a.subject.localeCompare(b.subject)
: a.scope.localeCompare(b.scope)
}
// Alphabetical sort
return a.subject.localeCompare(b.subject)
},
},
}

5
components/elements.js Normal file
View File

@@ -0,0 +1,5 @@
exports.Button = require('../dist/admin/components/elements/Button').default;
exports.Card = require('../dist/admin/components/elements/Card').default;
exports.Eyebrow = require('../dist/admin/components/elements/Eyebrow').default;
exports.Nav = require('../dist/admin/components/elements/Nav').default;
exports.Gutter = require('../dist/admin/components/elements/Gutter').Gutter;

5
components/elements.ts Normal file
View File

@@ -0,0 +1,5 @@
export { default as Button } from '../dist/admin/components/elements/Button';
export { default as Card } from '../dist/admin/components/elements/Card';
export { default as Eyebrow } from '../dist/admin/components/elements/Eyebrow';
export { default as Nav } from '../dist/admin/components/elements/Nav';
export { Gutter } from '../dist/admin/components/elements/Gutter';

View File

@@ -0,0 +1 @@
export type { Props } from '../../dist/admin/components/forms/field-types/Array/types';

View File

@@ -0,0 +1 @@
export type { Props } from '../../dist/admin/components/forms/field-types/Blocks/types';

View File

@@ -0,0 +1 @@
export type { Props } from '../../dist/admin/components/views/collections/List/Cell/types';

View File

@@ -0,0 +1 @@
export type { Props } from '../../dist/admin/components/forms/field-types/Checkbox/types';

View File

@@ -0,0 +1 @@
export type { Props } from '../../dist/admin/components/forms/field-types/Code/types';

View File

@@ -0,0 +1 @@
export type { Props } from '../../dist/admin/components/forms/field-types/DateTime/types';

View File

@@ -0,0 +1 @@
export type { Props } from '../../dist/admin/components/forms/field-types/Email/types';

View File

@@ -0,0 +1 @@
export type { Props } from '../../dist/admin/components/forms/field-types/Group/types';

View File

@@ -0,0 +1 @@
export type { Props } from '../../dist/admin/components/forms/field-types/JSON/types';

View File

@@ -0,0 +1 @@
export type { Props } from '../../dist/admin/components/forms/field-types/Number/types';

View File

@@ -0,0 +1 @@
export type { Props } from '../../dist/admin/components/forms/field-types/Password/types';

View File

@@ -0,0 +1 @@
export type { Props } from '../../../dist/admin/components/forms/field-types/RadioGroup/RadioInput/types';

View File

@@ -0,0 +1 @@
export type { Props } from '../../../dist/admin/components/forms/field-types/RadioGroup/types';

View File

@@ -0,0 +1 @@
export type { Props, Option, ValueWithRelation } from '../../dist/admin/components/forms/field-types/Relationship/types';

View File

@@ -0,0 +1 @@
export type { Props } from '../../dist/admin/components/forms/field-types/RichText/types';

1
components/fields/Row.ts Normal file
View File

@@ -0,0 +1 @@
export type { Props } from '../../dist/admin/components/forms/field-types/Row/types';

View File

@@ -0,0 +1 @@
export type { Props } from '../../dist/admin/components/forms/field-types/Select/types';

View File

@@ -0,0 +1 @@
export type { Props } from '../../dist/admin/components/forms/field-types/Text/types';

View File

@@ -0,0 +1 @@
export type { Props } from '../../dist/admin/components/forms/field-types/Textarea/types';

View File

@@ -0,0 +1 @@
export type { Props } from '../../dist/admin/components/forms/field-types/Upload/types';

42
components/forms.js Normal file
View File

@@ -0,0 +1,42 @@
exports.useForm = require('../dist/admin/components/forms/Form/context').useForm;
/**
* @deprecated useWatchForm is no longer preferred. If you need all form fields, prefer `useAllFormFields`.
*/
exports.useWatchForm = require('../dist/admin/components/forms/Form/context').useWatchForm;
exports.useFormFields = require('../dist/admin/components/forms/Form/context').useFormFields;
exports.useAllFormFields = require('../dist/admin/components/forms/Form/context').useAllFormFields;
exports.useFormSubmitted = require('../dist/admin/components/forms/Form/context').useFormSubmitted;
exports.useFormProcessing = require('../dist/admin/components/forms/Form/context').useFormProcessing;
exports.useFormModified = require('../dist/admin/components/forms/Form/context').useFormModified;
exports.useField = require('../dist/admin/components/forms/useField').default;
/**
* @deprecated This method is now called useField. The useFieldType alias will be removed in an upcoming version.
*/
exports.useFieldType = require('../dist/admin/components/forms/useField').default;
exports.Form = require('../dist/admin/components/forms/Form').default;
exports.Text = require('../dist/admin/components/forms/field-types/Text').default;
exports.TextInput = require('../dist/admin/components/forms/field-types/Text/Input').default;
exports.Group = require('../dist/admin/components/forms/field-types/Group').default;
exports.Select = require('../dist/admin/components/forms/field-types/Select').default;
exports.SelectInput = require('../dist/admin/components/forms/field-types/Select/Input').default;
exports.Checkbox = require('../dist/admin/components/forms/field-types/Checkbox').default;
exports.Submit = require('../dist/admin/components/forms/Submit').default;
exports.Label = require('../dist/admin/components/forms/Label').default;
exports.reduceFieldsToValues = require('../dist/admin/components/forms/Form/reduceFieldsToValues').default;
exports.getSiblingData = require('../dist/admin/components/forms/Form/getSiblingData').default;
exports.withCondition = require('../dist/admin/components/forms/withCondition').default;

38
components/forms.ts Normal file
View File

@@ -0,0 +1,38 @@
export {
useForm,
/**
* @deprecated useWatchForm is no longer preferred. If you need all form fields, prefer `useAllFormFields`.
*/
useWatchForm,
useFormFields,
useAllFormFields,
useFormSubmitted,
useFormProcessing,
useFormModified,
} from '../dist/admin/components/forms/Form/context';
export { default as useField } from '../dist/admin/components/forms/useField';
/**
* @deprecated This method is now called useField. The useFieldType alias will be removed in an upcoming version.
*/
export { default as useFieldType } from '../dist/admin/components/forms/useField';
export { default as Form } from '../dist/admin/components/forms/Form';
export { default as Text } from '../dist/admin/components/forms/field-types/Text';
export { default as TextInput } from '../dist/admin/components/forms/field-types/Text/Input';
export { default as Group } from '../dist/admin/components/forms/field-types/Group';
export { default as Select } from '../dist/admin/components/forms/field-types/Select';
export { default as SelectInput } from '../dist/admin/components/forms/field-types/Select/Input';
export { default as Checkbox } from '../dist/admin/components/forms/field-types/Checkbox';
export { default as Submit } from '../dist/admin/components/forms/Submit';
export { default as Label } from '../dist/admin/components/forms/Label';
export { default as reduceFieldsToValues } from '../dist/admin/components/forms/Form/reduceFieldsToValues';
export { default as getSiblingData } from '../dist/admin/components/forms/Form/getSiblingData';
export { default as withCondition } from '../dist/admin/components/forms/withCondition';

1
components/hooks.js Normal file
View File

@@ -0,0 +1 @@
exports.useStepNav = require('../dist/admin/components/elements/StepNav').useStepNav;

1
components/hooks.ts Normal file
View File

@@ -0,0 +1 @@
export { useStepNav } from '../dist/admin/components/elements/StepNav';

2
components/icons.js Normal file
View File

@@ -0,0 +1,2 @@
exports.Chevron = require('../dist/admin/components/icons/Chevron').default;
exports.X = require('../dist/admin/components/icons/X').default;

2
components/icons.ts Normal file
View File

@@ -0,0 +1,2 @@
export { default as Chevron } from '../dist/admin/components/icons/Chevron';
export { default as X } from '../dist/admin/components/icons/X';

1
components/index.ts Normal file
View File

@@ -0,0 +1 @@
export * from '../dist/admin/components';

View File

@@ -0,0 +1 @@
exports.usePreferences = require('../dist/admin/components/utilities/Preferences').usePreferences;

View File

@@ -0,0 +1 @@
export { usePreferences } from '../dist/admin/components/utilities/Preferences';

3
components/rich-text.js Normal file
View File

@@ -0,0 +1,3 @@
exports.LeafButton = require('../dist/admin/components/forms/field-types/RichText/leaves/Button').default;
exports.ElementButton = require('../dist/admin/components/forms/field-types/RichText/elements/Button').default;
exports.toggleElement = require('../dist/admin/components/forms/field-types/RichText/elements/toggle').default;

3
components/rich-text.ts Normal file
View File

@@ -0,0 +1,3 @@
export { default as LeafButton } from '../dist/admin/components/forms/field-types/RichText/leaves/Button';
export { default as ElementButton } from '../dist/admin/components/forms/field-types/RichText/elements/Button';
export { default as toggleElement } from '../dist/admin/components/forms/field-types/RichText/elements/toggle';

2
components/templates.js Normal file
View File

@@ -0,0 +1,2 @@
exports.DefaultTemplate = require('../dist/admin/components/templates/Default').default;
exports.MinimalTemplate = require('../dist/admin/components/templates/Minimal').default;

2
components/templates.ts Normal file
View File

@@ -0,0 +1,2 @@
export { default as DefaultTemplate } from '../dist/admin/components/templates/Default';
export { default as MinimalTemplate } from '../dist/admin/components/templates/Minimal';

7
components/utilities.js Normal file
View File

@@ -0,0 +1,7 @@
exports.Meta = require('../dist/admin/components/utilities/Meta').default;
exports.useLocale = require('../dist/admin/components/utilities/Locale').useLocale;
exports.useDocumentInfo = require('../dist/admin/components/utilities/DocumentInfo').useDocumentInfo;
exports.useConfig = require('../dist/admin/components/utilities/Config').useConfig;
exports.useAuth = require('../dist/admin/components/utilities/Auth').useAuth;
exports.useEditDepth = require('../dist/admin/components/utilities/EditDepth').useEditDepth;
exports.useTheme = require('../dist/admin/components/utilities/Theme').useTheme;

6
components/utilities.ts Normal file
View File

@@ -0,0 +1,6 @@
export { default as Meta } from '../dist/admin/components/utilities/Meta';
export { useLocale } from '../dist/admin/components/utilities/Locale';
export { useDocumentInfo } from '../dist/admin/components/utilities/DocumentInfo';
export { useConfig } from '../dist/admin/components/utilities/Config';
export { useAuth } from '../dist/admin/components/utilities/Auth';
export { useEditDepth } from '../dist/admin/components/utilities/EditDepth';

1
components/views/Cell.js Normal file
View File

@@ -0,0 +1 @@
exports.Cell = require('../../dist/admin/components/views/collections/List/Cell').default;

2
components/views/Cell.ts Normal file
View File

@@ -0,0 +1,2 @@
export { default as Cell } from '../../dist/admin/components/views/collections/List/Cell';
export type { Props } from '../../dist/admin/components/views/collections/List/Cell/types';

View File

@@ -0,0 +1 @@
exports.Dashboard = required('../../dist/admin/components/views/Dashboard/Default').default;

View File

@@ -0,0 +1,3 @@
export { default as Dashboard } from '../../dist/admin/components/views/Dashboard/Default';
export type { Props } from '../../dist/admin/components/views/Dashboard/types';

1
components/views/Edit.js Normal file
View File

@@ -0,0 +1 @@
exports.Edit = require('../../dist/admin/components/views/collections/Edit/Default').default;

2
components/views/Edit.ts Normal file
View File

@@ -0,0 +1,2 @@
export { default as Edit } from '../../dist/admin/components/views/collections/Edit/Default';
export type { Props } from '../../dist/admin/components/views/collections/Edit/types';

1
components/views/List.js Normal file
View File

@@ -0,0 +1 @@
exports.List = require('../../dist/admin/components/views/collections/List/Default').default;

2
components/views/List.ts Normal file
View File

@@ -0,0 +1,2 @@
export { default as List } from '../../dist/admin/components/views/collections/List/Default';
export type { Props } from '../../dist/admin/components/views/collections/List/types';

2
config.d.ts vendored Normal file
View File

@@ -0,0 +1,2 @@
export { buildConfig } from './dist/config/build';
export * from './dist/config/types';

1
config.js Normal file
View File

@@ -0,0 +1 @@
exports.buildConfig = require('./dist/config/build').buildConfig;

1
database/index.ts Normal file
View File

@@ -0,0 +1 @@
export { baseDatabaseAdapter } from '../dist/database/baseDatabaseAdapter';

View File

@@ -10,24 +10,23 @@ You can define Collection-level Access Control within each Collection's `access`
## Available Controls
| Function | Allows/Denies Access |
| ----------------------- | -------------------------------------------- |
| **[`create`](#create)** | Used in the `create` operation |
| **[`read`](#read)** | Used in the `find` and `findByID` operations |
| **[`update`](#update)** | Used in the `update` operation |
| **[`delete`](#delete)** | Used in the `delete` operation |
| Function | Allows/Denies Access |
| ------------------------ | -------------------- |
| **[`create`](#create)** | Used in the `create` operation |
| **[`read`](#read)** | Used in the `find` and `findByID` operations |
| **[`update`](#update)** | Used in the `update` operation |
| **[`delete`](#delete)** | Used in the `delete` operation |
#### Auth-enabled Controls
If a Collection supports [`Authentication`](/docs/authentication/overview), the following Access Controls become available:
| Function | Allows/Denies Access |
| ----------------------- | -------------------------------------------------------------- |
| **[`admin`](#admin)** | Used to restrict access to the Payload Admin panel |
| Function | Allows/Denies Access |
| ----------------------- | -------------------- |
| **[`admin`](#admin)** | Used to restrict access to the Payload Admin panel |
| **[`unlock`](#unlock)** | Used to restrict which users can access the `unlock` operation |
**Example Collection config:**
```ts
import { CollectionConfig } from 'payload/types';
@@ -51,10 +50,10 @@ Returns a boolean which allows/denies access to the `create` request.
**Available argument properties:**
| Option | Description |
| ---------- | -------------------------------------------------------------------------- |
| Option | Description |
| ---------- | ----------- |
| **`req`** | The Express `request` object containing the currently authenticated `user` |
| **`data`** | The data passed to create the document with. |
| **`data`** | The data passed to create the document with. |
**Example:**
@@ -78,20 +77,20 @@ Read access functions can return a boolean result or optionally return a [query
**Available argument properties:**
| Option | Description |
| --------- | -------------------------------------------------------------------------- |
| Option | Description |
| --------- | ----------- |
| **`req`** | The Express `request` object containing the currently authenticated `user` |
| **`id`** | `id` of document requested, if within `findByID` |
| **`id`** | `id` of document requested, if within `findByID` |
**Example:**
```ts
import { Access } from 'payload/config'
import { Access } from 'payload/config';
const canReadPage: Access = ({ req: { user } }) => {
// allow authenticated users
if (user) {
return true
return true;
}
// using a query constraint, guest users can access when a field named 'isPublic' is set to true
return {
@@ -100,7 +99,7 @@ const canReadPage: Access = ({ req: { user } }) => {
equals: true,
},
}
}
};
```
### Update
@@ -109,25 +108,25 @@ Update access functions can return a boolean result or optionally return a [quer
**Available argument properties:**
| Option | Description |
| ---------- | -------------------------------------------------------------------------- |
| Option | Description |
| ---------- | ----------- |
| **`req`** | The Express `request` object containing the currently authenticated `user` |
| **`id`** | `id` of document requested to update |
| **`data`** | The data passed to update the document with |
| **`id`** | `id` of document requested to update |
| **`data`** | The data passed to update the document with |
**Example:**
```ts
import { Access } from 'payload/config'
import { Access } from 'payload/config';
const canUpdateUser: Access = ({ req: { user }, id }) => {
// allow users with a role of 'admin'
if (user.roles && user.roles.some((role) => role === 'admin')) {
return true
if (user.roles && user.roles.some(role => role === 'admin')) {
return true;
}
// allow any other users to update only oneself
return user.id === id
}
return user.id === id;
};
```
### Delete
@@ -136,10 +135,10 @@ Similarly to the Update function, returns a boolean or a [query constraint](/doc
**Available argument properties:**
| Option | Description |
| --------- | --------------------------------------------------------------------------------------------------- |
| Option | Description |
| --------- | ----------- |
| **`req`** | The Express `request` object with additional `user` property, which is the currently logged in user |
| **`id`** | `id` of document requested to delete |
| **`id`** | `id` of document requested to delete |
**Example:**
@@ -149,7 +148,7 @@ import { Access } from 'payload/config'
const canDeleteCustomer: Access = async ({ req, id }) => {
if (!id) {
// allow the admin UI to show controls to delete since it is indeterminate without the id
return true
return true;
}
// query another collection using the id
const result = await req.payload.find({
@@ -159,10 +158,10 @@ const canDeleteCustomer: Access = async ({ req, id }) => {
where: {
customer: { equals: id },
},
})
});
return result.totalDocs === 0
}
return result.totalDocs === 0;
};
```
### Admin
@@ -171,8 +170,8 @@ If the Collection is [used to access the Payload Admin panel](/docs/admin/overvi
**Available argument properties:**
| Option | Description |
| --------- | -------------------------------------------------------------------------- |
| Option | Description |
| --------- | ----------- |
| **`req`** | The Express `request` object containing the currently authenticated `user` |
### Unlock
@@ -181,6 +180,6 @@ Determines which users can [unlock](/docs/authentication/operations#unlock) othe
**Available argument properties:**
| Option | Description |
| --------- | -------------------------------------------------------------------------- |
| Option | Description |
| --------- | ----------- |
| **`req`** | The Express `request` object containing the currently authenticated `user` |

View File

@@ -10,14 +10,13 @@ Field Access Control is specified with functions inside a field's config. All fi
## Available Controls
| Function | Purpose |
| ----------------------- | -------------------------------------------------------------------------------- |
| **[`create`](#create)** | Allows or denies the ability to set a field's value when creating a new document |
| **[`read`](#read)** | Allows or denies the ability to read a field's value |
| **[`update`](#update)** | Allows or denies the ability to update a field's value |
| Function | Purpose |
| ------------------------ | ------- |
| **[`create`](#create)** | Allows or denies the ability to set a field's value when creating a new document |
| **[`read`](#read)** | Allows or denies the ability to read a field's value |
| **[`update`](#update)** | Allows or denies the ability to update a field's value |
**Example Collection config:**
```ts
import { CollectionConfig } from 'payload/types';
@@ -45,11 +44,11 @@ Returns a boolean which allows or denies the ability to set a field's value when
**Available argument properties:**
| Option | Description |
| ----------------- | -------------------------------------------------------------------------- |
| Option | Description |
| ----------------- | ----------- |
| **`req`** | The Express `request` object containing the currently authenticated `user` |
| **`data`** | The full data passed to create the document. |
| **`siblingData`** | Immediately adjacent field data passed to create the document. |
| **`data`** | The full data passed to create the document. |
| **`siblingData`** | Immediately adjacent field data passed to create the document. |
### Read
@@ -57,12 +56,12 @@ Returns a boolean which allows or denies the ability to read a field's value. If
**Available argument properties:**
| Option | Description |
| ----------------- | -------------------------------------------------------------------------- |
| Option | Description |
| ----------------- | ----------- |
| **`req`** | The Express `request` object containing the currently authenticated `user` |
| **`id`** | `id` of the document being read |
| **`doc`** | The full document data. |
| **`siblingData`** | Immediately adjacent field data of the document being read. |
| **`id`** | `id` of the document being read |
| **`doc`** | The full document data. |
| **`siblingData`** | Immediately adjacent field data of the document being read. |
### Update
@@ -72,10 +71,10 @@ If `false` is returned and you attempt to update the field's value, the operatio
**Available argument properties:**
| Option | Description |
| ----------------- | -------------------------------------------------------------------------- |
| Option | Description |
| ----------------- | ----------- |
| **`req`** | The Express `request` object containing the currently authenticated `user` |
| **`id`** | `id` of the document being updated |
| **`data`** | The full data passed to update the document. |
| **`siblingData`** | Immediately adjacent field data passed to update the document with. |
| **`doc`** | The full document data, before the update is applied. |
| **`id`** | `id` of the document being updated |
| **`data`** | The full data passed to update the document. |
| **`siblingData`** | Immediately adjacent field data passed to update the document with. |
| **`doc`** | The full document data, before the update is applied. |

View File

@@ -8,35 +8,30 @@ keywords: globals, access control, permissions, documentation, Content Managemen
You can define Global-level Access Control within each Global's `access` property. All Access Control functions accept one `args` argument.
\*\*Available argument properties:
**Available argument properties:
## Available Controls
| Function | Allows/Denies Access |
| ----------------------- | -------------------------------------- |
| **[`read`](#read)** | Used in the `findOne` Global operation |
| **[`update`](#update)** | Used in the `update` Global operation |
| Function | Allows/Denies Access |
| ------------------------ | -------------------- |
| **[`read`](#read)** | Used in the `findOne` Global operation |
| **[`update`](#update)** | Used in the `update` Global operation |
**Example Global config:**
```ts
import { GlobalConfig } from 'payload/types'
import { GlobalConfig } from 'payload/types';
const Header: GlobalConfig = {
slug: 'header',
slug: "header",
// highlight-start
access: {
read: ({ req: { user } }) => {
/* */
},
update: ({ req: { user } }) => {
/* */
},
read: ({ req: { user } }) => { /* */ },
update: ({ req: { user } }) => { /* */ },
},
// highlight-end
}
};
export default Header
export default Header;
```
### Read
@@ -45,8 +40,8 @@ Returns a boolean result or optionally a [query constraint](/docs/queries/overvi
**Available argument properties:**
| Option | Description |
| --------- | -------------------------------------------------------------------------- |
| Option | Description |
| --------- | ----------- |
| **`req`** | The Express `request` object containing the currently authenticated `user` |
### Update
@@ -55,7 +50,7 @@ Returns a boolean result or optionally a [query constraint](/docs/queries/overvi
**Available argument properties:**
| Option | Description |
| ---------- | -------------------------------------------------------------------------- |
| Option | Description |
| ---------- | ----------- |
| **`req`** | The Express `request` object containing the currently authenticated `user` |
| **`data`** | The data passed to update the global with. |
| **`data`** | The data passed to update the global with. |

View File

@@ -8,7 +8,10 @@ keywords: overview, access control, permissions, documentation, Content Manageme
Access control within Payload is extremely powerful while remaining easy and intuitive to manage. Declaring who should have access to what documents is no more complex than writing a simple JavaScript function that either returns a `boolean` or a [`query`](/docs/queries/overview) constraint to restrict which documents users can interact with.
<YouTube id="DoPLyXG26Dg" title="Overview of Payload Access Control" />
<YouTube
id="DoPLyXG26Dg"
title="Overview of Payload Access Control"
/>
**Example use cases:**
@@ -29,21 +32,13 @@ Access control within Payload is extremely powerful while remaining easy and int
const defaultPayloadAccess = ({ req: { user } }) => {
// Return `true` if a user is found
// and `false` if it is undefined or null
return Boolean(user)
return Boolean(user);
}
```
<Banner type="success">
<strong>Note:</strong>
<br />
In the Local API, all Access Control functions are skipped by default, allowing your server to do
whatever it needs. But, you can opt back in by setting the option
{' '}
<strong>
overrideAccess
</strong>
{' '}
to <strong>false</strong>.
<strong>Note:</strong><br/>
In the Local API, all Access Control functions are skipped by default, allowing your server to do whatever it needs. But, you can opt back in by setting the option <strong>overrideAccess</strong> to <strong>false</strong>.
</Banner>
### Access Control Types
@@ -54,13 +49,12 @@ You can manage access within Payload on three different levels:
- [Fields](/docs/access-control/fields)
- [Globals](/docs/access-control/globals)
### When Access Control is Executed
<Banner type="success">
<strong>Note:</strong>
<br />
Access control functions are utilized in two places. It's important to understand how and when
your access control is executed.
<strong>Note:</strong><br/>
Access control functions are utilized in two places. It's important to understand how and when your access control is executed.
</Banner>
#### As you execute operations
@@ -76,11 +70,8 @@ To accomplish this, Payload ships with an `Access` operation, which is executed
### Argument Availability
<Banner type="warning">
<strong>Important:</strong>
<br />
When your access control functions are executed via the <strong>access</strong> operation, the{' '}
<strong>id</strong> and <strong>data</strong> arguments will be <strong>undefined</strong>,
because Payload is executing your functions without referencing a specific document.
<strong>Important:</strong><br/>
When your access control functions are executed via the <strong>access</strong> operation, the <strong>id</strong> and <strong>data</strong> arguments will be <strong>undefined</strong>, because Payload is executing your functions without referencing a specific document.
</Banner>
If you use `id` or `data` within your access control functions, make sure to check that they are defined first. If they are not, then you can assume that your access control is being executed via the `access` operation, to determine solely what the user can do within the Admin UI.

View File

@@ -1,54 +0,0 @@
---
title: Bundlers
label: Bundlers
order: 60
desc: Bundlers are used to bundle the code that serves Payload's Admin Panel.
---
Payload has two official bundlers, the [Webpack Bundler](/docs/admin/webpack) and the [Vite Bundler](/docs/admin/vite). You must install a bundler to use the admin panel.
##### Install a bundler
Webpack (recommended):
```text
yarn add @payloadcms/bundler-webpack
```
Vite (beta):
```text
yarn add @payloadcms/bundler-vite
```
##### Configure the bundler
```ts
// payload.config.ts
import { buildConfig } from 'payload/config'
import { webpackBundler } from '@payloadcms/bundler-webpack'
// import { viteBundler } from '@payloadcms/bundler-vite'
export default buildConfig({
// highlight-start
admin: {
bundler: webpackBundler() // or viteBundler()
},
// highlight-end
})
```
### What are bundlers?
At their core, a bundler's main goal is to take a bunch of files and turn them into a few optimized files that you ship to the browser. The admin UI has a root `index.html` entry point, and from there the bundler traverses the dependency tree, bundling all of the files that are required from that point on.
Since the bundled file is sent to the browser, it can't include any server-only code. You will need to remove any server-only code from your admin UI before bundling it. You can learn more about [excluding server code](/docs/admin/excluding-server-code) section.
<Banner type="warning">
<strong>Using environment variables in the admin UI</strong>
<br />
Bundles should not contain sensitive information. By default, Payload
excludes env variables from the bundle. If you need to use env variables in your payload config,
you need to prefix them with `PAYLOAD_PUBLIC_` to make them available to the client-side code.
</Banner>

View File

@@ -6,15 +6,13 @@ desc: Fully customize your Admin Panel by swapping in your own React components.
keywords: admin, components, custom, documentation, Content Management System, cms, headless, javascript, node, react, express
---
While designing the Payload Admin panel, we determined it should be as minimal and straightforward as possible to allow easy customization and control. There are many times where you may want to completely control how a whole view or a field works. You might even want to add in new views entirely. In order for Payload to support this level of customization without introducing versioning / future-proofing issues, Payload provides for a pattern to supply your own React components via your Payload config.
While designing the Payload Admin panel, we determined it should be as minimal and straightforward as possible to allow easy customization and control. There are many times where you may want to completely control how a whole view or a field works. You might even want to add in your own routes entirely. In order for Payload to support that level of customization without introducing versioning / future-proofing issues, Payload provides for a pattern to supply your own React components via your Payload config.
To swap in your own React component, first, consult the list of available component overrides below. Determine the scope that corresponds to what you are trying to accomplish, and then author your React component accordingly.
<Banner type="success">
<strong>Tip:</strong>
<br />
Custom components will automatically be provided with all props that the default component normally
accepts.
<strong>Tip:</strong><br />
Custom components will automatically be provided with all props that the default component would accept.
</Banner>
### Base Component Overrides
@@ -23,27 +21,27 @@ You can override a set of admin panel-wide components by providing a component t
| Path | Description |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`Nav`** | Contains the sidebar / mobile menu in its entirety. |
| **`BeforeNavLinks`** | Array of components to inject into the built-in Nav, _before_ the links themselves. |
| **`AfterNavLinks`** | Array of components to inject into the built-in Nav, _after_ the links. |
| **`Nav`** | Contains the sidebar and mobile Nav in its entirety. |
| **`logout.Button`** | A custom React component. |
| **`BeforeDashboard`** | Array of components to inject into the built-in Dashboard, _before_ the default dashboard contents. |
| **`AfterDashboard`** | Array of components to inject into the built-in Dashboard, _after_ the default dashboard contents. [Demo](https://github.com/payloadcms/payload/tree/main/test/admin/components/AfterDashboard/index.tsx) |
| **`AfterDashboard`** | Array of components to inject into the built-in Dashboard, _after_ the default dashboard contents. [Demo](https://github.com/payloadcms/payload/tree/master/test/admin/components/AfterDashboard/index.tsx) |
| **`BeforeLogin`** | Array of components to inject into the built-in Login, _before_ the default login form. |
| **`AfterLogin`** | Array of components to inject into the built-in Login, _after_ the default login form. |
| **`logout.Button`** | A custom React component. |
| **`BeforeNavLinks`** | Array of components to inject into the built-in Nav, _before_ the links themselves. |
| **`AfterNavLinks`** | Array of components to inject into the built-in Nav, _after_ the links. |
| **`views.Account`** | The Account view is used to show the currently logged in user's Account page. |
| **`views.Dashboard`** | The main landing page of the Admin panel. |
| **`graphics.Icon`** | Used as a graphic within the `Nav` component. Often represents a condensed version of a full logo. |
| **`graphics.Logo`** | The full logo to be used in contexts like the `Login` view. |
| **`routes`** | Define your own routes to add to the Payload Admin UI. [More](#custom-routes) |
| **`providers`** | Define your own provider components that will wrap the Payload Admin UI. [More](#custom-providers) |
| **`actions`** | Array of custom components to be rendered in the Payload Admin UI header, providing additional interactivity and functionality. |
| **`views`** | Override or create new views within the Payload Admin UI. [More](#views) |
Here is a full example showing how to swap some of these components for your own.
#### Full example:
`payload.config.js`
```ts
import { buildConfig } from 'payload/config'
import { buildConfig } from "payload/config";
import {
MyCustomNav,
MyCustomLogo,
@@ -51,8 +49,7 @@ import {
MyCustomAccount,
MyCustomDashboard,
MyProvider,
MyCustomAdminAction,
} from './customComponents'
} from "./customComponents";
export default buildConfig({
admin: {
@@ -62,7 +59,6 @@ export default buildConfig({
Icon: MyCustomIcon,
Logo: MyCustomLogo,
},
actions: [MyCustomAdminAction],
views: {
Account: MyCustomAccount,
Dashboard: MyCustomDashboard,
@@ -70,111 +66,47 @@ export default buildConfig({
providers: [MyProvider],
},
},
})
});
```
#### Views
You can easily swap entire views with your own by using the `admin.components.views` property. At the root level, Payload renders the following views by default, all of which can be overridden:
| Property | Description |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
| **`Account`** | The Account view is used to show the currently logged in user's Account page. |
| **`Dashboard`** | The main landing page of the Admin panel. |
To swap out any of these views, simply pass in your custom component to the `admin.components.views` property of your Payload config. For example:
```ts
// payload.config.ts
{
// ...
admin: {
components: {
views: {
Account: MyCustomAccountView,
Dashboard: MyCustomDashboardView,
},
},
},
}
```
For more granular control, pass a configuration object instead. Each view corresponds to its own `<Route />` component in [React Router v5](https://v5.reactrouter.com). Payload exposes all of the properties of React Router:
| Property | Description |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
| **`Component`** \* | Pass in the component that should be rendered when a user navigates to this route. |
| **`path`** \* | React Router `path`. [See the React Router docs](https://v5.reactrouter.com/web/api/Route/path-string-string) for more info. |
| **`exact`** | React Router `exact` property. [More](https://v5.reactrouter.com/web/api/Route/exact-bool) |
| **`strict`** | React Router `strict` property. [More](https://v5.reactrouter.com/web/api/Route/strict-bool) |
| **`sensitive`** | React Router `sensitive` property. [More](https://v5.reactrouter.com/web/api/Route/sensitive-bool) |
_\* An asterisk denotes that a property is required._
#### Adding new views
To add a _new_ view to the Admin Panel, simply add another key to the `views` object with at least a `path` and `Component` property. For example:
```ts
// payload.config.ts
{
// ...
admin: {
components: {
views: {
MyCustomView: {
Component: MyCustomView,
path: '/my-custom-view',
},
},
},
},
}
```
<Banner type="warning">
<strong>Note:</strong>
<br />
Routes are cascading. This means that unless explicitly given the `exact` property, they will match on URLs that simply _start_ with the route's path. This is helpful when creating catch-all routes in your application. Alternatively, you could define your nested route _before_ your parent route.
</Banner>
_For more examples regarding how to customize components, look at the following [examples](https://github.com/payloadcms/payload/tree/main/test/admin/components)._
For help on how to build your own custom view components, see [building a custom view component](#building-a-custom-view-component).
_For more examples regarding how to customize components, look at the following [examples](https://github.com/payloadcms/payload/tree/master/test/admin/components)._
### Collections
You can override components on a collection-by-collection basis via the `admin.components` property.
You can override components on a Collection-by-Collection basis via each Collection's `admin` property.
| Path | Description |
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| **`BeforeList`** | Array of components to inject _before_ the built-in List view |
| **`BeforeListTable`** | Array of components to inject _before_ the built-in List view's table |
| **`AfterList`** | Array of components to inject _after_ the built-in List view |
| **`AfterListTable`** | Array of components to inject _after_ the built-in List view's table |
| **`views.Edit`** | Used while a document within this Collection is being edited. |
| **`views.List`** | The `List` view is used to render a paginated, filterable table of Documents in this Collection. |
| **`edit.SaveButton`** | Replace the default `Save` button with a custom component. Drafts must be disabled |
| **`edit.SaveDraftButton`** | Replace the default `Save Draft` button with a custom component. Drafts must be enabled and autosave must be disabled. |
| **`edit.PublishButton`** | Replace the default `Publish` button with a custom component. Drafts must be enabled. |
| **`edit.PreviewButton`** | Replace the default `Preview` button with a custom component. |
| **`views`** | Override or create new views within the Payload Admin UI. [More](#collection-views) |
| **`BeforeList`** | Array of components to inject _before_ the built-in List view |
| **`BeforeListTable`** | Array of components to inject _before_ the built-in List view's table |
| **`AfterListTable`** | Array of components to inject _after_ the built-in List view's table |
| **`AfterList`** | Array of components to inject _after_ the built-in List view |
Here is a full example showing how to swap some of these components for your own:
`Collection.ts`
#### Examples
```tsx
import * as React from 'react'
// Custom Buttons
import * as React from "react";
import {
CustomSaveButtonProps,
CustomSaveDraftButtonProps,
CustomPublishButtonType,
CustomPublishButtonProps,
CustomPreviewButtonProps,
} from 'payload/types'
} from "payload/types";
export const CustomSaveButton: CustomSaveButtonProps = ({ DefaultButton, label, save }) => {
return <DefaultButton label={label} save={save} />
}
export const CustomSaveButton: CustomSaveButtonProps = ({
DefaultButton,
label,
}) => {
return <DefaultButton label={label} />;
};
export const CustomSaveDraftButton: CustomSaveDraftButtonProps = ({
DefaultButton,
@@ -182,17 +114,19 @@ export const CustomSaveDraftButton: CustomSaveDraftButtonProps = ({
label,
saveDraft,
}) => {
return <DefaultButton label={label} disabled={disabled} saveDraft={saveDraft} />
}
return (
<DefaultButton label={label} disabled={disabled} saveDraft={saveDraft} />
);
};
export const CustomPublishButton: CustomPublishButtonType = ({
export const CustomPublishButton: CustomPublishButtonProps = ({
DefaultButton,
disabled,
label,
publish,
}) => {
return <DefaultButton label={label} disabled={disabled} publish={publish} />
}
return <DefaultButton label={label} disabled={disabled} publish={publish} />;
};
export const CustomPreviewButton: CustomPreviewButtonProps = ({
DefaultButton,
@@ -200,271 +134,58 @@ export const CustomPreviewButton: CustomPreviewButtonProps = ({
label,
preview,
}) => {
return <DefaultButton label={label} disabled={disabled} preview={preview} />
}
export const MyCollection: SanitizedCollectionConfig = {
slug: 'my-collection',
admin: {
components: {
edit: {
SaveButton: CustomSaveButton,
SaveDraftButton: CustomSaveDraftButton,
PublishButton: CustomPublishButton,
PreviewButton: CustomPreviewButton,
},
},
}
}
return <DefaultButton label={label} disabled={disabled} preview={preview} />;
};
```
#### Collection views
##### Custom Collection List View Example
To swap out entire views on collections, you can use the `admin.components.views` property on the collection's config. Payload renders the following views by default, all of which can be overridden:
Collection.ts
| Property | Description |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
| **`Edit`** | The Edit view is used to edit a single document for a given collection. |
| **`List`** | The List view is used to show a list of documents for a given collection. |
To swap out any of these views, simply pass in your custom component to the `admin.components.views` property of your Payload config. This will replace the entire view, including the page breadcrumbs, title, tabs, etc, _as well as all nested routes_.
```ts
// Collection.ts
{
// ...
```tsx
import { MyListComponent } from './MyListComponent';
export const MyCollection: CollectionConfig = {
slug: 'mycollection',
admin: {
components: {
views: {
Edit: MyCustomEditView,
List: MyCustomListView,
List: MyListComponent,
},
},
},
}
fields: [
...
],
};
```
_For help on how to build your own custom view components, see [building a custom view component](#building-a-custom-view-component)._
MyListComponent.tsx
**Customizing Nested Views within 'Edit' in Collections**
The `Edit` view in collections consists of several nested views, each serving a unique purpose. You can customize these nested views using the `admin.components.views.Edit` property in the collection's configuration. This approach allows you to replace specific nested views while keeping the overall structure of the `Edit` view intact, including the page breadcrumbs, title, tabs, etc.
Here's an example of how you can customize nested views within the `Edit` view in collections, including the use of the `actions` property:
```ts
// Collection.ts
{
// ...
admin: {
components: {
views: {
Edit: {
Default: {
Component: MyCustomDefaultTab,
actions: [CollectionEditButton], // Custom actions for the default edit view
},
API: {
Component: MyCustomAPIView,
actions: [CollectionAPIButton], // Custom actions for API view
},
LivePreview: {
Component: MyCustomLivePreviewView,
actions: [CollectionLivePreviewButton], // Custom actions for Live Preview
},
Version: {
Component: MyCustomVersionView,
actions: [CollectionVersionButton], // Custom actions for Version view
},
Versions: {
Component: MyCustomVersionsView,
actions: [CollectionVersionsButton], // Custom actions for Versions view
},
},
List: {
actions: [CollectionListButton],
},
},
},
},
}
```tsx
import React from "react";
import { List, type Props } from "payload/components/views/List"; // Payload's default List view component and its props
export const MyListComponent: React.FC<Props> = (props) => (
<div>
<p>
Some text before the default list view component. If you just want to do
that, you can also use the admin.components.list.BeforeList hook
</p>
<List {...props} />
</div>
);
```
**Adding New Tabs to 'Edit' View**
You can also add _new_ tabs to the `Edit` view by adding another key to the `components.views.Edit[key]` object with a `path` and `Component` property. See [Custom Tabs](#custom-tabs) for more information.
### Globals
As with Collections, you can override components on a global-by-global basis via the `admin.components` property.
As with Collections, You can override components on a global-by-global basis via their `admin` property.
| Path | Description |
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------- |
| **`elements.SaveButton`** | Replace the default `Save` button with a custom component. Drafts must be disabled |
| **`elements.SaveDraftButton`** | Replace the default `Save Draft` button with a custom component. Drafts must be enabled and autosave must be disabled. |
| **`elements.PublishButton`** | Replace the default `Publish` button with a custom component. Drafts must be enabled. |
| **`elements.PreviewButton`** | Replace the default `Preview` button with a custom component. |
| **`views`** | Override or create new views within the Payload Admin UI. [More](#global-views) |
#### Global views
To swap out views for globals, you can use the `admin.components.views` property on the global's config. Payload renders the following views by default, all of which can be overridden:
| Property | Description |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
| **`Edit`** | The Edit view is used to edit a single document for a given Global. |
To swap out any of these views, simply pass in your custom component to the `admin.components.views` property of your Payload config. This will replace the entire view, including the page breadcrumbs, title, and tabs, _as well as all nested views_.
```ts
// Global.ts
{
// ...
admin: {
components: {
views: {
Edit: MyCustomEditView,
},
},
},
}
```
_For help on how to build your own custom view components, see [building a custom view component](#building-a-custom-view-component)._
**Customizing Nested Views within 'Edit' in Globals**
Similar to collections, Globals allow for detailed customization within the `Edit` view. This includes the ability to swap specific nested views while maintaining the overall structure of the `Edit` view. You can use the `admin.components.views.Edit` property in the Globals configuration to achieve this, and this will only replace the nested view, leaving the page breadcrumbs, title, and tabs intact.
Here's how you can customize nested views within the `Edit` view in Globals, including the use of the `actions` property:
```ts
// Global.ts
{
// ...
admin: {
components: {
views: {
Edit: {
Default: {
Component: MyCustomGlobalDefaultTab,
actions: [GlobalEditButton], // Custom actions for the default edit view
},
API: {
Component: MyCustomGlobalAPIView,
actions: [GlobalAPIButton], // Custom actions for API view
},
LivePreview: {
Component: MyCustomGlobalLivePreviewView,
actions: [GlobalLivePreviewButton], // Custom actions for Live Preview
},
Version: {
Component: MyCustomGlobalVersionView,
actions: [GlobalVersionButton], // Custom actions for Version view
},
Versions: {
Component: MyCustomGlobalVersionsView,
actions: [GlobalVersionsButton], // Custom actions for Versions view
},
},
},
},
},
}
```
You can also add _new_ tabs to the `Edit` view by adding another key to the `components.views.Edit[key]` object with a `path` and `Component` property. See [Custom Tabs](#custom-tabs) for more information.
### Custom Tabs
You can easily swap individual collection or global edit views. To do this, pass an _object_ to the `admin.components.views.Edit` property of the config. Payload renders the following views by default, all of which can be overridden:
| Property | Description |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
| **`Default`** | The Default view is the primary view in which your document is edited. |
| **`Versions`** | The Versions view is used to view the version history of a single document. [More details](../versions) |
| **`Version`** | The Version view is used to view a single version of a single document for a given collection. [More details](../versions). |
| **`API`** | The API view is used to display the REST API JSON response for a given document. |
| **`LivePreview`** | The LivePreview view is used to display the Live Preview interface. [More details](../live-preview) |
Here is an example:
```ts
// Collection.ts or Global.ts
export const MyCollection: SanitizedCollectionConfig = {
slug: 'my-collection',
admin: {
components: {
views: {
Edit: { // You can also define `components.views.Edit` as a component, this will override _all_ nested views
Default: MyCustomDefaultTab,
Versions: MyCustomVersionsTab,
Version: MyCustomVersionTab,
API: MyCustomAPITab,
LivePreview: MyCustomLivePreviewTab,
},
},
},
},
}
```
To add a _new_ tab to the `Edit` view, simply add another key to `components.views.Edit[key]` with at least a `path` and `Component` property. For example:
```ts
// `Collection.ts` or `Global.ts`
export const MyCollection: SanitizedCollectionConfig = {
slug: 'my-collection',
admin: {
components: {
views: {
Edit: {
MyCustomTab: {
Component: MyCustomTab,
path: '/my-custom-tab',
// You an swap the entire tab component out for your own
Tab: MyCustomTab
},
AnotherCustomView: {
Component: AnotherCustomView,
path: '/another-custom-view',
// Or you can use the default tab component and just pass in your own label and href
Tab: {
label: 'Another Custom View',
href: '/another-custom-view',
}
},
},
},
},
},
}
```
### Building a custom view component
Your custom view components will be given all the props that a React Router `<Route />` typically would receive, as well as two props from Payload:
| Prop | Description |
| ----------------------- | ---------------------------------------------------------------------------- |
| **`user`** | The currently logged in user. Will be `null` if no user is logged in. |
| **`canAccessAdmin`** \* | If the currently logged in user is allowed to access the admin panel or not. |
<Banner type="warning">
<strong>Note:</strong>
<br />
It's up to you to secure your custom views. If your view requires a user to be logged in or to
have certain access rights, you should handle that within your view component yourself.
</Banner>
#### Example
You can find examples of custom views in the [Payload source code `/test/admin/components/views` folder](https://github.com/payloadcms/payload/tree/main/test/admin/components/views). There, you'll find two custom views:
1. A custom view that uses the `DefaultTemplate`, which is the built-in Payload template that displays the sidebar and "eyebrow nav"
1. A custom view that uses the `MinimalTemplate` - which is just a centered template used for things like logging in or out
To see how to pass in your custom views to create custom views of your own, take a look at the `admin.components.views` property of the [Payload test admin config](https://github.com/payloadcms/payload/blob/main/test/admin/config.ts).
| Path | Description |
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| **`views.Edit`** | Used while this Global is being edited. |
| **`edit.SaveButton`** | Replace the default `Save` button with a custom component. Drafts must be disabled |
| **`edit.SaveDraftButton`** | Replace the default `Save Draft` button with a custom component. Drafts must be enabled and autosave must be disabled. |
| **`edit.PublishButton`** | Replace the default `Publish` button with a custom component. Drafts must be enabled. |
| **`edit.PreviewButton`** | Replace the default `Preview` button with a custom component. |
### Fields
@@ -473,9 +194,10 @@ All Payload fields support the ability to swap in your own React components. So,
<Banner type="success">
<strong>Tip:</strong>
<br />
Don't see a built-in field type that you need? Build it! Using a combination of custom validation
and custom components, you can override the entirety of how a component functions within the admin
panel and effectively create your own field type.
Don't see a built-in field type that you need? Build it! Using a combination
of custom validation and custom components, you can override the entirety of
how a component functions within the admin panel and effectively create your
own field type.
</Banner>
**Fields support the following custom components:**
@@ -486,15 +208,6 @@ All Payload fields support the ability to swap in your own React components. So,
| **`Cell`** | Used in the `List` view's table to represent a table-based preview of the data stored in the field. [More](#cell-component) |
| **`Field`** | Swap out the field itself within all `Edit` views. [More](#field-component) |
As an alternative to replacing the entire Field component, you may want to keep the majority of the default Field component and only swap components within. This allows you to replace the **`Label`** or **`Error`** within a field component or add additional components inside the field with **`beforeInput`** or **`afterInput`**. **`beforeInput`** and **`afterInput`** are allowed in any fields that don't contain other fields, except [UI](/docs/fields/ui) and [Rich Text](/docs/fields/rich-text).
| Component | Description |
| ----------------- | --------------------------------------------------------------------------------------------------------------- |
| **`Label`** | Override the default Label in the Field Component. [More](#label-component) |
| **`Error`** | Override the default Error in the Field Component. [More](#error-component) |
| **`beforeInput`** | An array of elements that will be added before `input`/`textarea` elements. [More](#afterinput-and-beforeinput) |
| **`afterInput`** | An array of elements that will be added after `input`/`textarea` elements. [More](#afterinput-and-beforeinput) |
## Cell Component
These are the props that will be passed to your custom Cell to use in your own components.
@@ -510,17 +223,15 @@ These are the props that will be passed to your custom Cell to use in your own c
#### Example
```tsx
import React from 'react'
import type { Props } from 'payload/components/views/Cell'
import './index.scss'
const baseClass = 'custom-cell'
import React from "react";
import "./index.scss";
const baseClass = "custom-cell";
const CustomCell: React.FC<Props> = (props) => {
const { field, colIndex, collection, cellData, rowData } = props
const { field, colIndex, collection, cellData, rowData } = props;
return <span className={baseClass}>{cellData}</span>
}
return <span className={baseClass}>{cellData}</span>;
};
```
## Field Component
@@ -532,132 +243,81 @@ When writing your own custom components you can make use of a number of hooks to
When swapping out the `Field` component, you'll be responsible for sending and receiving the field's `value` from the form itself. To do so, import the `useField` hook as follows:
```tsx
import { useField } from 'payload/components/forms'
import { useField } from "payload/components/forms";
const CustomTextField: React.FC<{ path: string }> = ({ path }) => {
type Props = { path: string };
const CustomTextField: React.FC<Props> = ({ path }) => {
// highlight-start
const { value, setValue } = useField<string>({ path })
const { value, setValue } = useField<Props>({ path });
// highlight-end
return <input onChange={(e) => setValue(e.target.value)} value={value} />
}
return (
<input onChange={(e) => setValue(e.target.value)} value={value.path} />
);
};
```
<Banner type="success">
For more information regarding the hooks that are available to you while you build custom
components, including the <strong>useField</strong> hook, [click here](/docs/admin/hooks).
For more information regarding the hooks that are available to you while you
build custom components, including the <strong>useField</strong> hook, [click
here](/docs/admin/hooks).
</Banner>
## Label Component
## Custom routes
These are the props that will be passed to your custom Label.
You can easily add your own custom routes to the Payload Admin panel using the `admin.components.routes` property. Payload currently uses the extremely powerful React Router v5.x and custom routes support all the properties of the React Router `<Route />` component.
| Property | Description |
| ---------------- | ---------------------------------------------------------------- |
| **`htmlFor`** | Property used to set `for` attribute for label. |
| **`label`** | Label value provided in field, it can be used with i18n. |
| **`required`** | A boolean value that represents if the field is required or not. |
**Custom routes support the following properties:**
| Property | Description |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
| **`Component`** \* | Pass in the component that should be rendered when a user navigates to this route. |
| **`path`** \* | React Router `path`. [See the React Router docs](https://v5.reactrouter.com/web/api/Route/path-string-string) for more info. |
| **`exact`** | React Router `exact` property. [More](https://v5.reactrouter.com/web/api/Route/exact-bool) |
| **`strict`** | React Router `strict` property. [More](https://v5.reactrouter.com/web/api/Route/strict-bool) |
| **`sensitive`** | React Router `sensitive` property. [More](https://v5.reactrouter.com/web/api/Route/sensitive-bool) |
_\* An asterisk denotes that a property is required._
#### Custom route components
Your custom route components will be given all the props that a React Router `<Route />` typically would receive, as well as two props from Payload:
| Prop | Description |
| ----------------------- | ---------------------------------------------------------------------------- |
| **`user`** | The currently logged in user. Will be `null` if no user is logged in. |
| **`canAccessAdmin`** \* | If the currently logged in user is allowed to access the admin panel or not. |
<Banner type="warning">
<strong>Note:</strong>
<br />
It's up to you to secure your custom routes. If your route requires a user to
be logged in or to have certain access rights, you should handle that within
your route component yourself.
</Banner>
#### Example
```tsx
import React from 'react'
import { useTranslation } from 'react-i18next'
You can find examples of custom route views in the [Payload source code `/test/admin/components/views` folder](https://github.com/payloadcms/payload/tree/master/test/admin/components/views). There, you'll find two custom routes:
import { getTranslation } from 'payload/utilities/getTranslation'
1. A custom view that uses the `DefaultTemplate`, which is the built-in Payload template that displays the sidebar and "eyebrow nav"
1. A custom view that uses the `MinimalTemplate` - which is just a centered template used for things like logging in or out
type Props = {
htmlFor?: string
label?: Record<string, string> | false | string
required?: boolean
}
const CustomLabel: React.FC<Props> = (props) => {
const { htmlFor, label, required = false } = props
const { i18n } = useTranslation()
if (label) {
return (<span>
{getTranslation(label, i18n)}
{required && <span className="required">*</span>}
</span>);
}
return null
}
```
## Error Component
These are the props that will be passed to your custom Error.
| Property | Description |
| ---------------- | ------------------------------------------------------------- |
| **`message`** | The error message. |
| **`showError`** | A boolean value that represents if the error should be shown. |
#### Example
```tsx
import React from 'react'
type Props = {
message: string
showError?: boolean
}
const CustomError: React.FC<Props> = (props) => {
const { message, showError } = props
if (showError) {
return <p style={{color: 'red'}}>{message}</p>
} else return null;
}
```
## afterInput and beforeInput
With these properties you can add multiple components before and after the input element. For example, you can add an absolutely positioned button to clear the current field value.
#### Example
```tsx
import React from 'react'
import { Field } from 'payload/types'
import './style.scss'
const ClearButton: React.FC = () => {
return <button onClick={() => {/* ... */}}>X</button>
}
const titleField: Field = {
name: 'title',
type: 'text',
admin: {
components: {
afterInput: [ClearButton]
}
}
}
export default titleField;
```
To see how to pass in your custom views to create custom routes of your own, take a look at the `admin.components.routes` property of the [Payload test admin config](https://github.com/payloadcms/payload/blob/master/test/admin/config.ts).
## Custom providers
As your admin customizations gets more complex you may want to share state between fields or other components. You can add custom providers to add your own context to any Payload app for use in other custom components within the admin panel. Within your config add `admin.components.providers`, these can be used to share context or provide other custom functionality. Read the [React context](https://reactjs.org/docs/context.html) docs to learn more.
As your admin customizations gets more complex you may want to share state between fields or other components. You can add custom providers to do add your own context to any Payload app for use in other custom components within the admin panel. Within your config add `admin.components.providers`, these can be used to share context or provide other custom functionality. Read the [React context](https://reactjs.org/docs/context.html) docs to learn more.
<Banner type="warning">
<strong>Reminder:</strong> Don't forget to pass the **children** prop through the provider
component for the admin UI to show
<strong>Reminder:</strong> Don't forget to pass the **children** prop through
the provider component for the admin UI to show
</Banner>
### Styling Custom Components
Payload exports its SCSS variables and mixins for reuse in your own custom components. This is helpful in cases where you might want to style a custom input similarly to Payload's built-in styling, so it blends more thoroughly into the existing admin UI.
Payload exports its SCSS variables and mixins for reuse in your own custom components. This is helpful in cases where you might want to style a custom input similarly to Payload's built-ini styling, so it blends more thoroughly into the existing admin UI.
To make use of Payload SCSS variables / mixins to use directly in your own components, you can import them as follows:
@@ -672,21 +332,21 @@ When developing custom components you can support multiple languages to be consi
For example:
```tsx
import { useTranslation } from 'react-i18next'
import { useTranslation } from "react-i18next";
const CustomComponent: React.FC = () => {
// highlight-start
const { t, i18n } = useTranslation('namespace1')
const { t, i18n } = useTranslation("namespace1");
// highlight-end
return (
<ul>
<li>{t('key', { variable: 'value' })}</li>
<li>{t('namespace2:key', { variable: 'value' })}</li>
<li>{t("key", { variable: "value" })}</li>
<li>{t("namespace2:key", { variable: "value" })}</li>
<li>{i18n.language}</li>
</ul>
)
}
);
};
```
### Getting the current locale
@@ -694,18 +354,18 @@ const CustomComponent: React.FC = () => {
In any custom component you can get the selected locale with `useLocale` hook. `useLocale` returns the full locale object, consisting of a `label`, `rtl`(right-to-left) property, and then `code`. Here is a simple example:
```tsx
import { useLocale } from 'payload/components/utilities'
import { useLocale } from "payload/components/utilities";
const Greeting: React.FC = () => {
// highlight-start
const locale = useLocale()
const locale = useLocale();
// highlight-end
const trans = {
en: 'Hello',
es: 'Hola',
}
en: "Hello",
es: "Hola",
};
return <span> {trans[locale.code]} </span>
}
};
```

View File

@@ -13,16 +13,15 @@ You can add your own CSS by providing your base Payload config with a path to yo
To do so, provide your base Payload config with a path to your own stylesheet. It can be either a CSS or SCSS file.
**Example in payload.config.js:**
```ts
import { buildConfig } from 'payload/config'
import path from 'path'
import { buildConfig } from 'payload/config';
import path from 'path';
const config = buildConfig({
admin: {
css: path.resolve(__dirname, 'relative/path/to/stylesheet.scss'),
},
})
admin: {
css: path.resolve(__dirname, 'relative/path/to/stylesheet.scss'),
},
});
```
### Overriding built-in styles
@@ -31,7 +30,7 @@ To make it as easy as possible for you to override our styles, Payload uses [BEM
In addition to adding your own style definitions, you can also override Payload's built-in CSS variables. We use as much as possible behind the scenes, and you can override any of them that you'd like to.
You can find the built-in Payload CSS variables within [`./src/admin/scss/app.scss`](https://github.com/payloadcms/payload/blob/main/packages/payload/src/admin/scss/app.scss) and [`./src/admin/scss/colors.scss`](https://github.com/payloadcms/payload/blob/main/packages/payload/src/admin/scss/colors.scss). The following variables are defined and can be overridden:
You can find the built-in Payload CSS variables within [`./src/admin/scss/app.scss`](https://github.com/payloadcms/payload/blob/master/src/admin/scss/app.scss) and [`./src/admin/scss/colors.scss`](https://github.com/payloadcms/payload/blob/master/src/admin/scss/colors.scss). The following variables are defined and can be overridden:
- Breakpoints
- Base color shades (white to black by default)
@@ -44,8 +43,7 @@ You can find the built-in Payload CSS variables within [`./src/admin/scss/app.sc
#### Dark mode
<Banner type="warning">
If you're overriding colors or theme elevations, make sure to consider how your changes will
affect dark mode.
If you're overriding colors or theme elevations, make sure to consider how your changes will affect dark mode.
</Banner>
By default, Payload automatically overrides all `--theme-elevation`s and inverts all success / warning / error shades to suit dark mode. We also update some base theme variables like `--theme-bg`, `--theme-text`, etc.

View File

@@ -1,27 +0,0 @@
---
title: Environment Variables in Admin UI
label: Environment Variables
order: 100
desc: NEEDS TO BE WRITTEN
---
## Admin environment vars
<Banner type="warning">
<strong>Important:</strong>
<br />
Be careful about what variables you provide to your client-side code. Analyze every single one to
make sure that you're not accidentally leaking anything that an attacker could exploit. Only keys
that are safe for anyone to read in plain text should be provided to your Admin panel.
</Banner>
By default, `env` variables are **not** provided to the Admin panel for security and safety reasons.
But, Payload provides you with a way to still provide `env` vars to your frontend code.
**Payload will automatically supply any present `env` variables that are prefixed with `PAYLOAD_PUBLIC_` directly to the Admin panel.**
For example, if you've got the following environment variable:
`PAYLOAD_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_XXXXXXXXXXXXXXXXXX`
This key will automatically be made available to the Payload bundle and can be referenced in your Admin component code as `process.env.PAYLOAD_PUBLIC_STRIPE_PUBLISHABLE_KEY`.

View File

@@ -1,206 +0,0 @@
---
title: Excluding server-only code from admin UI
label: Excluding server code
order: 70
desc: Learn how to exclude server-only code from the Payload Admin UI bundle
---
Because the Admin Panel browser bundle includes your Payload Config file, files using server-only modules need to be excluded.
It's common for your config to rely on server only modules to perform logic in access control functions, hooks, and other contexts.
Any file that imports a server-only module such as `fs`, `stripe`, `authorizenet`, `nodemailer`, etc. **cannot** be included in the browser bundle.
#### Example Scenario
Say we have a collection called `Subscriptions` that has a `beforeChange` hook that creates a Stripe subscription whenever a Subscription document is created in Payload.
**Collection config**:
```ts
// collections/Subscriptions/index.ts
import { CollectionConfig } from 'payload/types'
import createStripeSubscription from './hooks/createStripeSubscription'
export const Subscription: CollectionConfig = {
slug: 'subscriptions',
hooks: {
beforeChange: [createStripeSubscription],
},
fields: [
{
name: 'stripeSubscriptionID',
type: 'text',
required: true,
},
],
}
```
**Collection hook**:
```ts
// collections/Subscriptions/hooks/createStripeSubscription.ts
// highlight-start
import Stripe from 'stripe' // <-- server-only module
// highlight-end
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY)
export const createStripeSubscription = async ({ data, operation }) => {
if (operation === 'create') {
const dataWithStripeID = { ...data }
// use Stripe to create a Stripe subscription
const subscription = await stripe.subscriptions.create({
// Configure the subscription accordingly
})
// Automatically add the Stripe subscription ID
// to the data that will be saved to this Subscription doc
dataWithStripeID.stripeSubscriptionID = subscription.id
return dataWithStripeID
}
return data
}
```
<Banner type="error">
<strong>Warning:</strong>
<br />
The above code is NOT production-ready and should not be referenced to create Stripe
subscriptions. Although creating a beforeChange hook is a completely valid spot to do things like
create subscriptions, the code above is incomplete and insecure, meant for explanation purposes
only.
</Banner>
**As-is, this collection will prevent your Admin panel from bundling or loading correctly, because Stripe relies on some Node-only packages.**
#### How to fix this
You need to make sure that you use `alias`es to tell your bundler to import "safe" files vs. attempting to import any server-side code that you need to get rid of. Depending on your bundler (Webpack, Vite, etc.) the steps involved may be slightly different.
The basic idea is to create a file that exports an empty object, and then alias import paths of any files that import server-only modules to that empty object file.
This way when your bundler goes to import a file that contains server-only modules, it will instead import the empty object file, which will not break the browser bundle.
### Aliasing server-only modules
To remove files that contain server-only modules from your bundle, you can use an `alias`.
In the Subscriptions config file above, we are importing the hook like so:
```ts
// collections/Subscriptions/index.ts
import createStripeSubscription from './hooks/createStripeSubscription'
```
By default the browser bundle will now include all the code from that file and any files down the tree. We know that the file imports `stripe`.
To fix this, we need to alias the `createStripeSubscription` file to a different file that can safely be included in the browser bundle.
First, we will create a mock file to replace the server-only file when bundling:
```js
// mocks/modules.js
export default {}
/**
* NOTE: if you are destructuring an import
* the mock file will need to export matching
* variables as the destructured object.
*
* export const namedExport = {}
*/
```
Aliasing with [Webpack](/docs/admin/webpack) can be done by:
```ts
// payload.config.ts
import { buildConfig } from 'payload/config'
import { webpackBundler } from '@payloadcms/bundler-webpack'
import { Subscriptions } from './collections/Subscriptions'
const mockModulePath = path.resolve(__dirname, 'mocks/emptyObject.js')
const fullFilePath = path.resolve(
__dirname,
'collections/Subscriptions/hooks/createStripeSubscription'
)
export default buildConfig({
collections: [Subscriptions],
admin: {
bundler: webpackBundler(),
webpack: (config) => {
return {
...config,
resolve: {
...config.resolve,
// highlight-start
alias: {
...config.resolve.alias,
[fullFilePath]: mockModulePath,
},
// highlight-end
},
}
},
},
})
```
Aliasing with [Vite](/docs/admin/vite) can be done by:
```ts
// payload.config.ts
import { buildConfig } from 'payload/config'
import { viteBundler } from '@payloadcms/bundler-vite'
import { Subscriptions } from './collections/Subscriptions'
const mockModulePath = path.resolve(__dirname, 'mocks/emptyObject.js')
export default buildConfig({
collections: [Subscriptions],
admin: {
bundler: viteBundler(),
vite: (incomingViteConfig) => {
const existingAliases = incomingViteConfig?.resolve?.alias || {};
let aliasArray: { find: string | RegExp; replacement: string; }[] = [];
// Pass the existing Vite aliases
if (Array.isArray(existingAliases)) {
aliasArray = existingAliases;
} else {
aliasArray = Object.values(existingAliases);
}
// highlight-start
// Add your own aliases using the find and replacement keys
// remember, vite aliases are exact-match only
aliasArray.push({
find: '../server-only-module',
replacement: path.resolve(__dirname, './path/to/browser-safe-module.js')
});
// highlight-end
return {
...incomingViteConfig,
resolve: {
...(incomingViteConfig?.resolve || {}),
alias: aliasArray,
}
};
},
},
})
```

View File

@@ -24,7 +24,7 @@ const CustomTextField: React.FC<Props> = ({ path }) => {
const { value, setValue } = useField<string>({ path })
// highlight-end
return <input onChange={(e) => setValue(e.target.value)} value={value.path} />
return <input onChange={e => setValue(e.target.value)} value={value.path} />
}
```
@@ -57,8 +57,7 @@ const {
There are times when a custom field component needs to have access to data from other fields, and you have a few options to do so. The `useFormFields` hook is a powerful and highly performant way to retrieve a form's field state, as well as to retrieve the `dispatchFields` method, which can be helpful for setting other fields' form states from anywhere within a form.
<Banner type="success">
<strong>This hook is great for retrieving only certain fields from form state</strong> because it
ensures that it will only cause a rerender when the items that you ask for change.
<strong>This hook is great for retrieving only certain fields from form state</strong> because it ensures that it will only cause a rerender when the items that you ask for change.
</Banner>
Thanks to the awesome package [`use-context-selector`](https://github.com/dai-shi/use-context-selector), you can retrieve a specific field's state easily. This is ideal because you can ensure you have an up-to-date field state, and your component will only re-render when _that field's state_ changes.
@@ -66,19 +65,21 @@ Thanks to the awesome package [`use-context-selector`](https://github.com/dai-sh
You can pass a Redux-like selector into the hook, which will ensure that you retrieve only the field that you want. The selector takes an argument with type of `[fields: Fields, dispatch: React.Dispatch<Action>]]`.
```tsx
import { useFormFields } from 'payload/components/forms'
import { useFormFields } from 'payload/components/forms';
const MyComponent: React.FC = () => {
// Get only the `amount` field state, and only cause a rerender when that field changes
const amount = useFormFields(([fields, dispatch]) => fields.amount)
const amount = useFormFields(([fields, dispatch]) => fields.amount);
// Do the same thing as above, but to the `feePercentage` field
const feePercentage = useFormFields(([fields, dispatch]) => fields.feePercentage)
const feePercentage = useFormFields(([fields, dispatch]) => fields.feePercentage);
if (typeof amount?.value !== 'undefined' && typeof feePercentage?.value !== 'undefined') {
return <span>The fee is ${(amount.value * feePercentage.value) / 100}</span>
return (
<span>The fee is ${(amount.value * feePercentage.value) / 100}</span>
);
}
}
};
```
### useAllFormFields
@@ -116,7 +117,7 @@ If you are building a custom component, then you should use `setValue` which is
You can send the following actions to the `dispatchFields` function.
| Action | Description |
| ---------------------- | -------------------------------------------------------------------------- |
|------------------------|----------------------------------------------------------------------------|
| **`ADD_ROW`** | Adds a row of data (useful in array / block field data) |
| **`DUPLICATE_ROW`** | Duplicates a row of data (useful in array / block field data) |
| **`MODIFY_CONDITION`** | Updates a field's conditional logic result (true / false) |
@@ -126,19 +127,15 @@ You can send the following actions to the `dispatchFields` function.
| **`REPLACE_STATE`** | Completely replaces form state |
| **`UPDATE`** | Update any property of a specific field's state |
To see types for each action supported within the `dispatchFields` hook, check out the Form types [here](https://github.com/payloadcms/payload/blob/main/packages/payload/src/admin/components/forms/Form/types.ts).
To see types for each action supported within the `dispatchFields` hook, check out the Form types [here](https://github.com/payloadcms/payload/blob/master/src/admin/components/forms/Form/types.ts).
### useForm
The `useForm` hook can be used to interact with the form itself, and sends back many methods that can be used to reactively fetch form state without causing rerenders within your components each time a field is changed. This is useful if you have action-based callbacks that your components fire, and need to interact with form state _based on a user action_.
<Banner type="warning">
<strong>Warning:</strong>
<br />
This hook is optimized to avoid causing rerenders when fields change, and as such, its `fields`
property will be out of date. You should only leverage this hook if you need to perform actions
against the form in response to your users' actions. Do not rely on its returned "fields" as being
up-to-date. They will be removed from this hook's response in an upcoming version.
<strong>Warning:</strong><br/>
This hook is optimized to avoid causing rerenders when fields change, and as such, its `fields` property will be out of date. You should only leverage this hook if you need to perform actions against the form in response to your users' actions. Do not rely on its returned "fields" as being up-to-date. They will be removed from this hook's response in an upcoming version.
</Banner>
The `useForm` hook returns an object with the following properties: |
@@ -324,7 +321,7 @@ The `useForm` hook returns an object with the following properties: |
},
{
drawerTitle: 'addFieldRow',
drawerDescription: 'A useful method to programmatically add a row to an array or block field.',
drawerDescription: 'A useful method to programtically add a row to an array or block field.',
drawerSlug: 'addFieldRow',
drawerContent: (
<>
@@ -347,7 +344,7 @@ The `useForm` hook returns an object with the following properties: |
value: <strong><code>rowIndex</code></strong>,
},
{
value: "The index of the row to add. If omitted, the row will be added to the end of the array.",
value: "The index of the row to add",
},
],
[
@@ -361,14 +358,10 @@ The `useForm` hook returns an object with the following properties: |
]}
/>
{' '}
<br />
<br />
{' '}
<pre>
{`import { useForm } from "payload/components/forms";
<pre>
{`import { useForm } from "payload/components/forms";
export const CustomArrayManager = () => {
const { addFieldRow } = useForm()
@@ -392,7 +385,7 @@ export const CustomArrayManager = () => {
</button>
)
}`}
</pre>
</pre>
<p>An example config to go along with the custom component</p>
<pre>
@@ -434,7 +427,7 @@ export const CustomArrayManager = () => {
},
{
drawerTitle: 'removeFieldRow',
drawerDescription: 'A useful method to programmatically remove a row from an array or block field.',
drawerDescription: 'A useful method to programtically remove a row from an array or block field.',
drawerSlug: 'removeFieldRow',
drawerContent: (
<>
@@ -463,14 +456,10 @@ export const CustomArrayManager = () => {
]}
/>
{' '}
<br />
<br />
{' '}
<pre>
{`import { useForm } from "payload/components/forms";
<pre>
{`import { useForm } from "payload/components/forms";
export const CustomArrayManager = () => {
const { removeFieldRow } = useForm()
@@ -489,7 +478,7 @@ export const CustomArrayManager = () => {
</button>
)
}`}
</pre>
</pre>
<p>An example config to go along with the custom component</p>
<pre>
@@ -531,7 +520,7 @@ export const CustomArrayManager = () => {
},
{
drawerTitle: 'replaceFieldRow',
drawerDescription: 'A useful method to programmatically replace a row from an array or block field.',
drawerDescription: 'A useful method to programtically replace a row from an array or block field.',
drawerSlug: 'replaceFieldRow',
drawerContent: (
<>
@@ -568,14 +557,10 @@ export const CustomArrayManager = () => {
]}
/>
{' '}
<br />
<br />
{' '}
<pre>
{`import { useForm } from "payload/components/forms";
<pre>
{`import { useForm } from "payload/components/forms";
export const CustomArrayManager = () => {
const { replaceFieldRow } = useForm()
@@ -599,7 +584,7 @@ export const CustomArrayManager = () => {
</button>
)
}`}
</pre>
</pre>
<p>An example config to go along with the custom component</p>
<pre>
@@ -635,43 +620,12 @@ export const CustomArrayManager = () => {
]}
/>
### useCollapsible
The `useCollapsible` hook allows you to control parent collapsibles:
| Property | Description |
|---------------------------|--------------------------------------------------------------------------------------------------------------------|
| **`collapsed`** | State of the collapsible. `true` if open, `false` if collapsed |
| **`isVisible`** | If nested, determine if the nearest collapsible is visible. `true` if no parent is closed, `false` otherwise |
| **`toggle`** | Toggles the state of the nearest collapsible |
| **`withinCollapsible`** | Determine when you are within another collaspible | |
**Example:**
```tsx
import React from 'react'
import { useCollapsible } from 'payload/components/utilities'
const CustomComponent: React.FC = () => {
const { collapsed, toggle } = useCollapsible()
return (
<div>
<p className="field-type">I am {collapsed ? 'closed' : 'open'}</p>
<button onClick={toggle} type="button">
Toggle
</button>
</div>
)
}
```
### useDocumentInfo
The `useDocumentInfo` hook provides lots of information about the document currently being edited, including the following:
| Property | Description |
|---------------------------|--------------------------------------------------------------------------------------------------------------------|
|---------------------------|--------------------------------------------------------------------------------------------------------------------| |
| **`collection`** | If the doc is a collection, its collection config will be returned |
| **`global`** | If the doc is a global, its global config will be returned |
| **`id`** | If the doc is a collection, its ID will be returned |
@@ -686,24 +640,24 @@ The `useDocumentInfo` hook provides lots of information about the document curre
**Example:**
```tsx
import { useDocumentInfo } from 'payload/components/utilities'
import { useDocumentInfo } from 'payload/components/utilities';
const LinkFromCategoryToPosts: React.FC = () => {
// highlight-start
const { id } = useDocumentInfo()
const { id } = useDocumentInfo();
// highlight-end
// id will be undefined on the create form
if (!id) {
return null
return null;
}
return (
<a href={`/admin/collections/posts?where[or][0][and][0][category][in][0]=[${id}]`}>
<a href={`/admin/collections/posts?where[or][0][and][0][category][in][0]=[${id}]`} >
View posts
</a>
)
}
};
```
### useLocale
@@ -711,20 +665,22 @@ const LinkFromCategoryToPosts: React.FC = () => {
In any custom component you can get the selected locale object with the `useLocale` hook. `useLocale`gives you the full locale object, consisting of a `label`, `rtl`(right-to-left) property, and then `code`. Here is a simple example:
```tsx
import { useLocale } from 'payload/components/utilities'
import { useLocale } from 'payload/components/utilities';
const Greeting: React.FC = () => {
// highlight-start
const locale = useLocale()
const locale = useLocale();
// highlight-end
const trans = {
en: 'Hello',
es: 'Hola',
}
};
return <span> {trans[locale.code]} </span>
}
return (
<span> { trans[locale.code] } </span>
);
};
```
### useAuth
@@ -732,7 +688,7 @@ const Greeting: React.FC = () => {
Useful to retrieve info about the currently logged in user as well as methods for interacting with it. It sends back an object with the following properties:
| Property | Description |
| ------------------------ | --------------------------------------------------------------------------------------- |
|--------------------------|-----------------------------------------------------------------------------------------|
| **`user`** | The currently logged in user |
| **`logOut`** | A method to log out the currently logged in user |
| **`refreshCookie`** | A method to trigger the silent refreshing of a user's auth token |
@@ -742,16 +698,18 @@ Useful to retrieve info about the currently logged in user as well as methods fo
| **`permissions`** | The permissions of the current user |
```tsx
import { useAuth } from 'payload/components/utilities'
import { User } from '../payload-types.ts'
import { useAuth } from 'payload/components/utilities';
import { User } from '../payload-types.ts';
const Greeting: React.FC = () => {
// highlight-start
const { user } = useAuth<User>()
const { user } = useAuth<User>();
// highlight-end
return <span>Hi, {user.email}!</span>
}
return (
<span>Hi, {user.email}!</span>
);
};
```
### useConfig
@@ -759,15 +717,17 @@ const Greeting: React.FC = () => {
Used to easily fetch the full Payload config.
```tsx
import { useConfig } from 'payload/components/utilities'
import { useConfig } from 'payload/components/utilities';
const MyComponent: React.FC = () => {
// highlight-start
const config = useConfig()
const config = useConfig();
// highlight-end
return <span>{config.serverURL}</span>
}
return (
<span>{config.serverURL}</span>
);
};
```
### useEditDepth
@@ -775,99 +735,19 @@ const MyComponent: React.FC = () => {
Sends back how many editing levels "deep" the current component is. Edit depth is relevant while adding new documents / editing documents in modal windows and other cases.
```tsx
import { useEditDepth } from 'payload/components/utilities'
import { useEditDepth } from 'payload/components/utilities';
const MyComponent: React.FC = () => {
// highlight-start
const editDepth = useEditDepth()
const editDepth = useEditDepth();
// highlight-end
return <span>My component is {editDepth} levels deep</span>
return (
<span>My component is {editDepth} levels deep</span>
)
}
```
### usePreferences
Returns methods to set and get user preferences. More info can be found [here](https://payloadcms.com/docs/admin/preferences).
### useTheme
Returns the currently selected theme (`light`, `dark` or `auto`), a set function to update it and a boolean `autoMode`, used to determine if the theme value should be set automatically based on the user's device preferences.
```tsx
import { useTheme } from 'payload/components/utilities'
const MyComponent: React.FC = () => {
// highlight-start
const { autoMode, setTheme, theme } = useTheme()
// highlight-end
return (
<>
<span>The current theme is {theme} and autoMode is {autoMode}</span>
<button
type="button"
onClick={() => setTheme(prev => prev === "light" ? "dark" : "light")}
>
Toggle theme
</button>
</>
)
}
```
### useTableColumns
Returns methods to manipulate table columns
```tsx
import { useTableColumns } from 'payload/components/hooks'
const MyComponent: React.FC = () => {
// highlight-start
const { setActiveColumns } = useTableColumns()
const resetColumns = () => {
setActiveColumns(['id', 'createdAt', 'updatedAt'])
}
// highlight-end
return (
<button
type="button"
onClick={resetColumns}
>
Reset columns
</button>
)
}
```
### useDocumentEvents
The `useDocumentEvents` hook provides a way of subscribing to cross-document events, such as updates made to nested documents within a drawer. This hook will report document events that are outside the scope of the document currently being edited. This hook provides the following:
| Property | Description |
|---------------------------|-------------------------------------------------------------------------------------------------------------------------------------------|
| **`mostRecentUpdate`** | An object containing the most recently updated document. It contains the `entitySlug`, `id` (if collection), and `updatedAt` properties |
| **`reportUpdate`** | A method used to report updates to documents. It accepts the same arguments as the `mostRecentUpdate` property. |
**Example:**
```tsx
import { useDocumentEvents } from 'payload/components/hooks'
const ListenForUpdates: React.FC = () => {
const { mostRecentUpdate } = useDocumentEvents()
return (
<span>
{JSON.stringify(mostRecentUpdate)}
</span>
)
}
```
<Banner type="info">
Right now the `useDocumentEvents` hook only tracks recently updated documents, but in the future it will track more document-related events as needed, such as document creation, deletion, etc.
</Banner>

View File

@@ -8,45 +8,38 @@ keywords: admin, components, custom, customize, documentation, Content Managemen
Payload dynamically generates a beautiful, fully functional React admin panel to manage your data. It's extremely powerful and can be customized / extended upon easily by swapping in your own React components. You can add additional views, modify how built-in views look / work, swap out Payload branding for your client's, build your own field types and much more.
The Payload Admin panel can be bundled with our officially supported [Vite](/docs/admin/vite) and [webpack](/docs/admin/webpack) bundlers or you can integrate another bundler following our adapter pattern approach.
When bundled, it is code-split, highly performant (even with 100+ fields), and written fully in TypeScript.
The Payload Admin panel is built with Webpack, code-split, highly performant (even with 100+ fields), and written fully in TypeScript.
<Banner type="success">
The Admin panel is meant to be simple enough to give you a starting point but not bring too much
complexity, so that you can easily customize it to suit the needs of your application and your
editors.
The Admin panel is meant to be simple enough to give you a starting point but
not bring too much complexity, so that you can easily customize it to suit the
needs of your application and your editors.
</Banner>
<LightDarkImage
srcLight="https://payloadcms.com/images/docs/admin.jpg"
srcDark="https://payloadcms.com/images/docs/admin-dark.jpg"
alt="Admin panel with collapsible sidebar"
caption="Redesigned admin panel with a collapsible sidebar that's open by default, providing greater extensibility and enhanced horizontal real estate."
/>
![Payload's Admin panel built in React](https://payloadcms.com/images/docs/admin.jpg)
_Screenshot of the Admin panel while editing a document from an example `AllFields` collection_
## Admin Options
All options for the Admin panel are defined in your base Payload config file.
| Option | Description |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `bundler` | The bundler that you would like to use to bundle the admin panel. Officially supported bundlers: [Webpack](/docs/admin/webpack) and [Vite](/docs/admin/vite). |
| `user` | The `slug` of a Collection that you want be used to log in to the Admin dashboard. [More](/docs/admin/overview#the-admin-user-collection) |
| `buildPath` | Specify an absolute path for where to store the built Admin panel bundle used in production. Defaults to `path.resolve(process.cwd(), 'build')`. |
| `meta` | Base meta data to use for the Admin panel. Included properties are `titleSuffix`, `ogImage`, and `favicon`. |
| `disable` | If set to `true`, the entire Admin panel will be disabled. |
| `indexHTML` | Optionally replace the entirety of the `index.html` file used by the Admin panel. Reference the [base index.html file](https://github.com/payloadcms/payload/blob/main/packages/payload/src/admin/index.html) to ensure your replacement has the appropriate HTML elements. |
| `css` | Absolute path to a stylesheet that you can use to override / customize the Admin panel styling. [More](/docs/admin/customizing-css). |
| `scss` | Absolute path to a Sass variables / mixins stylesheet meant to override Payload styles to make for an easy re-skinning of the Admin panel. [More](/docs/admin/customizing-css#overriding-scss-variables). |
| `dateFormat` | Global date format that will be used for all dates in the Admin panel. Any valid [date-fns](https://date-fns.org/) format pattern can be used. |
| `avatar` | Set account profile picture. Options: `gravatar`, `default` or a custom React component. |
| `autoLogin` | Used to automate admin log-in for dev and demonstration convenience. [More](/docs/authentication/config). |
| `livePreview` | Enable real-time editing for instant visual feedback of your front-end application. [More](/docs/live-preview/overview). |
| `components` | Component overrides that affect the entirety of the Admin panel. [More](/docs/admin/components) |
| `webpack` | Customize the Webpack config that's used to generate the Admin panel. [More](/docs/admin/webpack) |
| `vite` | Customize the Vite config that's used to generate the Admin panel. [More](/docs/admin/vite) |
| `logoutRoute` | The route for the `logout` page. |
| `inactivityRoute` | The route for the `logout` inactivity page. |
| Option | Description |
|-----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `user` | The `slug` of a Collection that you want be used to log in to the Admin dashboard. [More](/docs/admin/overview#the-admin-user-collection) |
| `buildPath` | Specify an absolute path for where to store the built Admin panel bundle used in production. Defaults to `path.resolve(process.cwd(), 'build')`. |
| `meta` | Base meta data to use for the Admin panel. Included properties are `titleSuffix`, `ogImage`, and `favicon`. |
| `disable` | If set to `true`, the entire Admin panel will be disabled. |
| `indexHTML` | Optionally replace the entirety of the `index.html` file used by the Admin panel. Reference the [base index.html file](https://github.com/payloadcms/payload/blob/master/src/admin/index.html) to ensure your replacement has the appropriate HTML elements. |
| `css` | Absolute path to a stylesheet that you can use to override / customize the Admin panel styling. [More](/docs/admin/customizing-css). |
| `scss` | Absolute path to a Sass variables / mixins stylesheet meant to override Payload styles to make for an easy re-skinning of the Admin panel. [More](/docs/admin/customizing-css#overriding-scss-variables). |
| `dateFormat` | Global date format that will be used for all dates in the Admin panel. Any valid [date-fns](https://date-fns.org/) format pattern can be used. |
| `avatar` | Set account profile picture. Options: `gravatar`, `default` or a custom React component. |
| `autoLogin` | Used to automate admin log-in for dev and demonstration convenience. [More](/docs/authentication/config). |
| `components` | Component overrides that affect the entirety of the Admin panel. [More](/docs/admin/components) |
| `webpack` | Customize the Webpack config that's used to generate the Admin panel. [More](/docs/admin/webpack) |
| **`logoutRoute`** | The route for the `logout` page. |
| **`inactivityRoute`** | The route for the `logout` inactivity page. |
### The Admin User Collection
@@ -62,13 +55,13 @@ To specify which Collection to use to log in to the Admin panel, pass the `admin
`payload.config.js`:
```ts
import { buildConfig } from 'payload/config'
import { buildConfig } from "payload/config";
const config = buildConfig({
admin: {
user: 'admins', // highlight-line
user: "admins", // highlight-line
},
})
});
```
By default, if you have not specified a Collection, Payload will automatically provide you with a `User` Collection which will be used to access the Admin panel. You can customize or override the fields and settings of the default `User` Collection by passing your own collection using `users` as its `slug` to Payload. When this is done, Payload will use your provided `User` Collection instead of its default version.

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