This PR adds an ecommerce plugin package with both a Payload plugin and
React UI utilities for the frontend. It also adds a new ecommerce
template and new ecommerce test suite.
It also makes a change to the `cpa` package to accept a `--version` flag
to install a specific version of Payload defaulting to the latest.
### Improved tenant assignment flow
This PR improves the tenant assignment flow. I know a lot of users liked
the previous flow where the field was not injected into the document.
But the original flow, confused many of users because the tenant filter
(top left) was being used to set the tenant on the document _and_ filter
the list view.
This change shown below is aiming to solve both of those groups with a
slightly different approach. As always, feedback is welcome while we try
to really make this plugin work for everyone.
https://github.com/user-attachments/assets/ceee8b3a-c5f5-40e9-8648-f583e2412199
Added 2 new localization strings:
```
// shown in the 3 dot menu
'assign-tenant-button-label': 'Assign Tenant',
// shown when needing to assign a tenant to a NEW document
'assign-tenant-modal-title': 'Assign "{{title}}"',
```
Removed 2 localization strings:
```
'confirm-modal-tenant-switch--body',
'confirm-modal-tenant-switch--heading'
```
### What?
Adds comprehensive documentation for the RadioField component in the
form builder plugin documentation.
### Why?
Addresses review feedback from #11908 noting that the RadioField was not
documented after being exported for end-user use.
### How?
- Added RadioField section with complete property documentation
- Added detailed Radio Options sub-section explaining option structure
- Updated Select field documentation for consistency (added missing
placeholder property and detailed options structure)
- Added radio field to configuration example to show all available
fields
Fixes the documentation gap identified in #11908 review comments.
Allows user to override more of the tenant field config. Now you can
override most of the field config with:
### At the root level
```ts
/**
* Field configuration for the field added to all tenant enabled collections
*/
tenantField?: RootTenantFieldConfigOverrides
```
### At the collection level
Setting collection level overrides will replace the root level overrides
shown above.
```ts
collections: {
[key in CollectionSlug]?: {
// ... rest of the types
/**
* Overrides for the tenant field, will override the entire tenantField configuration
*/
tenantFieldOverrides?: CollectionTenantFieldConfigOverrides
}
}
```
## What
Before this PR, an internal link in the Lexical editor could reference a
document from a different tenant than the active one.
Reproduction:
1. `pnpm dev plugin-multi-tenant`
2. Log in with `dev@payloadcms.com` and password `test`
3. Go to `http://localhost:3000/admin/collections/food-items` and switch
between the `Blue Dog` and `Steel Cat` tenants to see which food items
each tenant has.
4. Go to http://localhost:3000/admin/collections/food-items/create, and
in the new richtext field enter an internal link
5. In the relationship select menu, you will see the 6 food items at
once (3 of each of those tenants). In the relationship select menu, you
would previously see all 6 food items at once (3 from each of those
tenants). Now, you'll only see the 3 from the active tenant.
The new test verifies that this is fixed.
## How
`baseListFilter` is used, but now it's called `baseFilter` for obvious
reasons: it doesn't just filter the List View. Having two different
properties where the same function was supposed to be placed wasn't
feasible. `baseListFilter` is still supported for backwards
compatibility. It's used as a fallback if `baseFilter` isn't defined,
and it's documented as deprecated.
`baseFilter` is injected into `filterOptions` of the internal link field
in the Lexical Editor.
### What?
A comma is missing in the example code. This results in not valid JSON.
### Why?
I stumbled upon it, while setting up a Tenant-based Payload for the
first time.
### How?
Adding a comma results in valid JSON.
Fixes #
Added a comma. ;)
prettier doesn't seem to cover that, and horizontal scrolling in the
browser is even more annoying than in the IDE.
Regex used in the search engine: `^[ \t]*\* `
Adds full session functionality into Payload's existing local
authentication strategy.
It's enabled by default, because this is a more secure pattern that we
should enforce. However, we have provided an opt-out pattern for those
that want to stick to stateless JWT authentication by passing
`collectionConfig.auth.useSessions: false`.
Todo:
- [x] @jessrynkar to update the Next.js server functions for refresh and
logout to support these new features
- [x] @jessrynkar resolve build errors
---------
Co-authored-by: Elliot DeNolf <denolfe@gmail.com>
Co-authored-by: Jessica Chowdhury <jessica@trbl.design>
Co-authored-by: Jarrod Flesch <30633324+JarrodMFlesch@users.noreply.github.com>
Co-authored-by: Sasha <64744993+r1tsuu@users.noreply.github.com>
This PR adds int tests with vitest and e2e tests with playwright
directly into our templates.
The following are also updated:
- bumps core turbo to 2.5.4 in monorepo
- blank and website templates moved up to be part of the monorepo
workspace
- this means we now have thes templates filtered out in pnpm commands in
package.json
- they will now by default use workspace packages which we can use for
manual testing and int and e2e tests
- note that turbo doesnt work with these for dev in monorepo context
- CPA script will fetch latest version and then replace `workspace:*` or
the pinned version in the package.json before installation
- blank template no longer uses _template as a base, this is to simplify
management for workspace
- updated the generate template variations script
<!--
Thank you for the PR! Please go through the checklist below and make
sure you've completed all the steps.
Please review the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository if you haven't already.
The following items will ensure that your PR is handled as smoothly as
possible:
- PR Title must follow conventional commits format. For example, `feat:
my new feature`, `fix(plugin-seo): my fix`.
- Minimal description explained as if explained to someone not
immediately familiar with the code.
- Provide before/after screenshots or code diffs if applicable.
- Link any related issues/discussions from GitHub or Discord.
- Add review comments if necessary to explain to the reviewer the logic
behind a change
-->
### What?
Hi Payload Team, this PR is a reply to @DanRibbens's request to document
#11478. Let me know if you'd like to see any changes - thank you!
This makes it possible to add custom logic into how we map the document
data into the CSV data on a field-by-field basis.
- Allow custom data transformation to be added to
`custom.['plugin-import-export'].toCSV inside the field config
- Add type declaration to FieldCustom to improve types
- Export with `depth: 1`
Example:
```ts
{
name: 'customRelationship',
type: 'relationship',
relationTo: 'users',
custom: {
'plugin-import-export': {
toCSV: ({ value, columnName, row, siblingDoc, doc }) => {
row[`${columnName}_id`] = value.id
row[`${columnName}_email`] = value.email
},
},
},
},
```
### What?
Add a warning to the form builder plugin docs about potential GraphQL
type name collisions with custom Blocks or Collections.
### Why?
To help users avoid schema errors caused by conflicting type names and
guide them with resolution options.
The module `@payloadcms/plugin-nested-docs/fields` does not seem to
exist (anymore). Instead `createParentField` and
`createBreadcrumbsField` are exported by
`@payloadcms/plugin-nested-docs`
Closes https://github.com/payloadcms/payload/issues/12355
TabbedUI doesn't always work as intended and it can be affected by
existing fields or the order of other plugins, this mentions that and
links to the recommended direct use of fields example.
- update SantizeCollection to SanitizedCollection in TypeScript section
- fix new issue link in Multi-Tenant plugin section
- correct You can disabled to disable in Core Features
- fix duplicate section links for MongoDB and Postgres in Migrations
section
Adds a new date field to take submission values for.
It can help form serialisers render the right input for this kind of
field as the submissions themselves don't do any validation right now.
Disabled by default as to not cause any conflicts with existing projects
potentially inserting their own date blocks.
Can be enabled like this
```ts
formBuilderPlugin({
fields: {
date: true
}
})
```
Exports the `useTenantSelection` hook from the multi-tenant plugin, this
way other users can import and use the hook along with it's methods.
Can be imported:
```ts
import { useTenantSelection } from '@payloadcms/plugin-multi-tenant/client'
```
The context returned:
```ts
type ContextType = {
/**
* Array of options to select from
*/
options: OptionObject[]
/**
* The currently selected tenant ID
*/
selectedTenantID: number | string | undefined
/**
* Prevents a refresh when the tenant is changed
*
* If not switching tenants while viewing a "global", set to true
*/
setPreventRefreshOnChange: React.Dispatch<React.SetStateAction<boolean>>
/**
* Sets the selected tenant ID
*
* @param args.id - The ID of the tenant to select
* @param args.refresh - Whether to refresh the page after changing the tenant
*/
setTenant: (args: { id: number | string | undefined; refresh?: boolean }) => void
}
```
### What?
In some cases you may want to opt out of using the default access
control that this plugin provides on the tenants collection.
### Why?
Other collections are able to opt out of this already, but the tenants
collection specifically was not configured with an opt out capability.
### How?
Adds new property to the plugin config: `useTenantsCollectionAccess`.
Setting this to `false` allows users to opt out and write their own
access control functions without the plugin merging in its own
constraints for the tenant collection.
Fixes https://github.com/payloadcms/payload/issues/10882
Remove repeated `developers` word.
### What?
There was a typo on the plugins overview page, where `developers
developers` was used twice in a row. Mb that was a quote from Steve
Balmer idk.
### Why?
Docs should be pristine.
### How?
Removed the word.
Running `pnpm add @payloadcms/plugin-multi-tenant@beta` will install
`v.0.0.1` which is outdated:
https://www.npmjs.com/package/@payloadcms/plugin-multi-tenant?activeTab=versions
I suspect the intention was to remove `@beta` from the plugin install
instructions with yesterday's Payload `v3.18.0` release which also
bumped `plugin-multi-tenant` to `v3.18.0`, but this might have been
missed.
### Multi Tenant Plugin
This PR adds a `@payloadcms/plugin-multi-tenant` package. The goal is to
consolidate a source of truth for multi-tenancy. Currently we are
maintaining different implementations for clients, users in discord and
our examples repo. When updates or new paradigms arise we need to
communicate this with everyone and update code examples which is hard to
maintain.
### What does it do?
- adds a tenant selector to the sidebar, above the nav links
- adds a hidden tenant field to every collection that you specify
- adds an array field to your users collection, allowing you to assign
users to tenants
- by default combines the access control (to enabled collections) that
you define, with access control based on the tenants assigned to user on
the request
- by default adds a baseListFilter that filters the documents shown in
the list view with the selected tenant in the admin panel
### What does it not do?
- it does not implement multi-tenancy for your frontend. You will need
to query data for specific tenants to build your website/application
- it does not add a tenants collection, you **NEED** to add a tenants
collection, where you can define what types of fields you would like on
it
### The plugin config
Most of the options listed below are _optional_, but it is easier to
just lay out all of the configuration options.
**TS Type**
```ts
type MultiTenantPluginConfig<ConfigTypes = unknown> = {
/**
* After a tenant is deleted, the plugin will attempt to clean up related documents
* - removing documents with the tenant ID
* - removing the tenant from users
*
* @default true
*/
cleanupAfterTenantDelete?: boolean
/**
* Automatically
*/
collections: {
[key in CollectionSlug]?: {
/**
* Set to `true` if you want the collection to behave as a global
*
* @default false
*/
isGlobal?: boolean
/**
* Set to `false` if you want to manually apply the baseListFilter
*
* @default true
*/
useBaseListFilter?: boolean
/**
* Set to `false` if you want to handle collection access manually without the multi-tenant constraints applied
*
* @default true
*/
useTenantAccess?: boolean
}
}
/**
* Enables debug mode
* - Makes the tenant field visible in the admin UI within applicable collections
*
* @default false
*/
debug?: boolean
/**
* Enables the multi-tenant plugin
*
* @default true
*/
enabled?: boolean
/**
* Field configuration for the field added to all tenant enabled collections
*/
tenantField?: {
access?: RelationshipField['access']
/**
* The name of the field added to all tenant enabled collections
*
* @default 'tenant'
*/
name?: string
}
/**
* Field configuration for the field added to the users collection
*
* If `includeDefaultField` is `false`, you must include the field on your users collection manually
* This is useful if you want to customize the field or place the field in a specific location
*/
tenantsArrayField?:
| {
/**
* Access configuration for the array field
*/
arrayFieldAccess?: ArrayField['access']
/**
* When `includeDefaultField` is `true`, the field will be added to the users collection automatically
*/
includeDefaultField?: true
/**
* Additional fields to include on the tenants array field
*/
rowFields?: Field[]
/**
* Access configuration for the tenant field
*/
tenantFieldAccess?: RelationshipField['access']
}
| {
arrayFieldAccess?: never
/**
* When `includeDefaultField` is `false`, you must include the field on your users collection manually
*/
includeDefaultField?: false
rowFields?: never
tenantFieldAccess?: never
}
/**
* The slug for the tenant collection
*
* @default 'tenants'
*/
tenantsSlug?: string
/**
* Function that determines if a user has access to _all_ tenants
*
* Useful for super-admin type users
*/
userHasAccessToAllTenants?: (
user: ConfigTypes extends { user: User } ? ConfigTypes['user'] : User,
) => boolean
}
```
**Example usage**
```ts
import type { Config } from './payload-types'
import { buildConfig } from 'payload'
export default buildConfig({
plugins: [
multiTenantPlugin<Config>({
collections: {
pages: {},
},
userHasAccessToAllTenants: (user) => isSuperAdmin(user),
}),
],
})
```
### How to configure Collections as Globals for multi-tenant
When using multi-tenant, globals need to actually be configured as
collections so the content can be specific per tenant.
To do that, you can mark a collection with `isGlobal` and it will behave
like a global and users will not see the list view.
```ts
multiTenantPlugin({
collections: {
navigation: {
isGlobal: true,
},
},
})
```
### What?
This PR fixes numerous links across the docs, both internal docs links
and external links. This PR also fixes some minor formatting issues in
some places, as well as optically aligns the markdown tables in tables
that had broken links.
### Why?
To properly link readers to the correct location in the docs, and for
better formatting and easier consumption.
### How?
Changes to many `.mdx` files in the `docs` folder.
Notes:
- There are duplicative section id's in `docs/authentication/email.mdx`,
I've fixed one such link, but have left it as is for now.
Updates the plugin template and adds it to the monorepo
Includes:
* Integration testing setup
* Adding custom client / server components via a plugin
* The same building setup that we use for our plugins in the monorepo
* `create-payload-app` dynamically configures the project based on the
name:`dev/tsconfig.json`, `src/index.ts`, `dev/payload.config.ts`
For example, from project name: `payload-plugin-cool`
`src/index.ts`:
```ts
export type PayloadPluginCoolConfig = {
/**
* List of collections to add a custom field
*/
collections?: Partial<Record<CollectionSlug, true>>
disabled?: boolean
}
export const payloadPluginCool =
(pluginOptions: PayloadPluginCoolConfig) =>
/// ...
```
`dev/tsconfig.json`:
```json
{
"extends": "../tsconfig.json",
"exclude": [],
"include": [
"**/*.ts",
"**/*.tsx",
"../src/**/*.ts",
"../src/**/*.tsx",
"next.config.mjs",
".next/types/**/*.ts"
],
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@payload-config": [
"./payload.config.ts"
],
"payload-plugin-cool": [
"../src/index.ts"
],
"payload-plugin-cool/client": [
"../src/exports/client.ts"
],
"payload-plugin-cool/rsc": [
"../src/exports/rsc.ts"
]
},
"noEmit": true
}
}
```
`./dev/payload.config.ts`
```
import { payloadPluginCool } from 'payload-plugin-cool'
///
plugins: [
payloadPluginCool({
collections: {
posts: true,
},
}),
],
```
Example of published plugin
https://www.npmjs.com/package/payload-plugin-cool
- Adds missing types, especially the `Where` type. Will be helpful for
people to see that they can type their queries like that
- Mention pnpm first and prefer pnpm > npm > yarn throughout docs
- Add `payload` to function arguments in examples to discourage people
from doing `import payload from 'payload'`
- PNPM => pnpm, NPM => npm
- Fix some typos
Adds documentation for the feature introduced with [plugin-search
collection reindexing](https://github.com/payloadcms/payload/pull/9391).
This also fixes an invalid scss import in one of the examples.
Credit to @rilrom for the invalid css import find!
### What?
The examples in nestedDocs and formBuilder plugins were referencing the
plugins incorrectly.
### Why?
To prevent confusion for readers.
### How?
Changes to `docs/plugins/form-builder.mdx` and
`docs/plugins/nested-docs.mdx`.
### What?
This PR fixes a variety of links around the docs.
### Why?
To link readers to the correct location in the docs
### How?
Changes and fixes to a number of doc links.
Removes examples that are now duplicative or unnecessary due to new
features in 3.0:
### Custom server
This one can be removed in favor of [Next.js
documentation](https://nextjs.org/docs/pages/building-your-application/configuring/custom-server)
### Hierarchy
The new `join` field can solve for many of the use cases for the
`hierarchy` example. Bi-directional relationships with the `join` field
should be preferred here.
### Nested Docs, Redirects
Our website template showcases how to use the `nested-docs` and
`redirects` plugins in-depth, with real-world examples.
### Virtual Fields
Virtual fields have gotten significantly easier and can now be defined
by specifying `virtual: true`. Not a big need for a full example any
longer.
---------
Co-authored-by: Jacob Fletcher <jacobsfletch@gmail.com>
## The SEO plugin now takes in a function to override or add in new
fields
- `fieldOverrides` has been removed
- `fields` is now a function that takes in `defaultFields` and expects
an array of fields in return
This makes it a lot easier for end users to override and extend existing
fields and add new ones. This change also brings this plugin inline with
the pattern that we use in our other plugins.
```ts
// before
seoPlugin({
fieldOverrides: {
title: {
required: true,
},
},
fields: [
{
name: 'customField',
type: 'text',
}
]
})
// after
seoPlugin({
fields: ({ defaultFields }) => {
const modifiedFields = defaultFields.map((field) => {
// Override existing fields
if ('name' in field && field.name === 'title') {
return {
...field,
required: true,
}
}
return field
})
return [
...modifiedFields,
// Add a new field
{
name: 'ogTitle',
type: 'text',
label: 'og:title',
},
]
},
})
```
## Also fixes
- Localization labels not showing up on default fields
- The inability to add before and after inputs to default fields
https://github.com/payloadcms/payload/issues/8893
### What?
I noticed a spelling error in the banner of the beta docs and decided I
could save everyone some time by *running the entirety of the beta docs*
through a spellchecker.
### Why?
To fix many spelling and formatting mistakes at once.
### How?
By enabling `edit mode` in my browser and letting the built-in
spellchecker perform its magic (and changing _only_ where it made
sense).
~~Ironically, the original spelling mistake that inspired me to do this
remains unchanged as that is a part of the website repo. [PR for that is
here](https://github.com/payloadcms/website/pull/388).~~