This reverts commit 0f6d748365.
# Conflicts:
# docs/configuration/overview.mdx
# docs/experimental/overview.mdx
# packages/ui/src/elements/Status/index.tsx
This commit is contained in:
@@ -131,29 +131,6 @@ localization: {
|
|||||||
|
|
||||||
Since the filtering happens at the root level of the application and its result is not calculated every time you navigate to a new page, you may want to call `router.refresh` in a custom component that watches when values that affect the result change. In the example above, you would want to do this when `supportedLocales` changes on the tenant document.
|
Since the filtering happens at the root level of the application and its result is not calculated every time you navigate to a new page, you may want to call `router.refresh` in a custom component that watches when values that affect the result change. In the example above, you would want to do this when `supportedLocales` changes on the tenant document.
|
||||||
|
|
||||||
## Experimental Options
|
|
||||||
|
|
||||||
Experimental options are features that may not be fully stable and may change or be removed in future releases.
|
|
||||||
|
|
||||||
These options can be enabled in your Payload Config under the `experimental` key. You can set them like this:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { buildConfig } from 'payload'
|
|
||||||
|
|
||||||
export default buildConfig({
|
|
||||||
// ...
|
|
||||||
experimental: {
|
|
||||||
localizeStatus: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
The following experimental options are available related to localization:
|
|
||||||
|
|
||||||
| Option | Description |
|
|
||||||
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
||||||
| **`localizeStatus`** | **Boolean.** When `true`, shows document status per locale in the admin panel instead of always showing the latest overall status. Opt-in for backwards compatibility. Defaults to `false`. |
|
|
||||||
|
|
||||||
## Field Localization
|
## Field Localization
|
||||||
|
|
||||||
Payload Localization works on a **field** level—not a document level. In addition to configuring the base Payload Config to support Localization, you need to specify each field that you would like to localize.
|
Payload Localization works on a **field** level—not a document level. In addition to configuring the base Payload Config to support Localization, you need to specify each field that you would like to localize.
|
||||||
|
|||||||
@@ -70,7 +70,6 @@ The following options are available:
|
|||||||
| **`admin`** | The configuration options for the Admin Panel, including Custom Components, Live Preview, etc. [More details](../admin/overview#admin-options). |
|
| **`admin`** | The configuration options for the Admin Panel, including Custom Components, Live Preview, etc. [More details](../admin/overview#admin-options). |
|
||||||
| **`bin`** | Register custom bin scripts for Payload to execute. [More Details](#custom-bin-scripts). |
|
| **`bin`** | Register custom bin scripts for Payload to execute. [More Details](#custom-bin-scripts). |
|
||||||
| **`editor`** | The Rich Text Editor which will be used by `richText` fields. [More details](../rich-text/overview). |
|
| **`editor`** | The Rich Text Editor which will be used by `richText` fields. [More details](../rich-text/overview). |
|
||||||
| **`experimental`** | Configure experimental features for Payload. These may be unstable and may change or be removed in future releases. [More details](../experimental/overview). |
|
|
||||||
| **`db`** \* | The Database Adapter which will be used by Payload. [More details](../database/overview). |
|
| **`db`** \* | The Database Adapter which will be used by Payload. [More details](../database/overview). |
|
||||||
| **`serverURL`** | A string used to define the absolute URL of your app. This includes the protocol, for example `https://example.com`. No paths allowed, only protocol, domain and (optionally) port. |
|
| **`serverURL`** | A string used to define the absolute URL of your app. This includes the protocol, for example `https://example.com`. No paths allowed, only protocol, domain and (optionally) port. |
|
||||||
| **`collections`** | An array of Collections for Payload to manage. [More details](./collections). |
|
| **`collections`** | An array of Collections for Payload to manage. [More details](./collections). |
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
---
|
|
||||||
title: Experimental Features
|
|
||||||
label: Overview
|
|
||||||
order: 10
|
|
||||||
desc: Enable and configure experimental functionality within Payload. These features may be unstable and may change or be removed without notice.
|
|
||||||
keywords: experimental, unstable, beta, preview, features, configuration, Payload, cms, headless, javascript, node, react, nextjs
|
|
||||||
---
|
|
||||||
|
|
||||||
Experimental features allow you to try out new functionality before it becomes a stable part of Payload. These features may still be in active development, may have incomplete functionality, and can change or be removed in future releases without warning.
|
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
Experimental features are configured via the root-level `experimental` property in your [Payload Config](../configuration/overview). This property contains individual feature flags, each flag can be configured independently, allowing you to selectively opt into specific functionality.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { buildConfig } from 'payload'
|
|
||||||
|
|
||||||
const config = buildConfig({
|
|
||||||
// ...
|
|
||||||
experimental: {
|
|
||||||
localizeStatus: true, // highlight-line
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Experimental Options
|
|
||||||
|
|
||||||
The following options are available:
|
|
||||||
|
|
||||||
| Option | Description |
|
|
||||||
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
||||||
| **`localizeStatus`** | **Boolean.** When `true`, shows document status per locale in the admin panel instead of always showing the latest overall status. Opt-in for backwards compatibility. Defaults to `false`. |
|
|
||||||
|
|
||||||
This list may change without notice.
|
|
||||||
|
|
||||||
## When to Use Experimental Features
|
|
||||||
|
|
||||||
You might enable an experimental feature when:
|
|
||||||
|
|
||||||
- You want early access to new capabilities before their stable release.
|
|
||||||
- You can accept the risks of using potentially unstable functionality.
|
|
||||||
- You are testing new features in a development or staging environment.
|
|
||||||
- You wish to provide feedback to the Payload team on new functionality.
|
|
||||||
|
|
||||||
If you are working on a production application, carefully evaluate whether the benefits outweigh the risks. For most stable applications, it is recommended to wait until the feature is officially released.
|
|
||||||
|
|
||||||
<Banner type="success">
|
|
||||||
<strong>Tip:</strong> To stay up to date on experimental features or share
|
|
||||||
your feedback, visit the{' '}
|
|
||||||
<a
|
|
||||||
href="https://github.com/payloadcms/payload/discussions"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
Payload GitHub Discussions
|
|
||||||
</a>{' '}
|
|
||||||
or{' '}
|
|
||||||
<a
|
|
||||||
href="https://github.com/payloadcms/payload/issues"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
open an issue
|
|
||||||
</a>
|
|
||||||
.
|
|
||||||
</Banner>
|
|
||||||
@@ -12,7 +12,6 @@ export const createGlobalVersion: CreateGlobalVersion = async function createGlo
|
|||||||
autosave,
|
autosave,
|
||||||
createdAt,
|
createdAt,
|
||||||
globalSlug,
|
globalSlug,
|
||||||
localeStatus,
|
|
||||||
parent,
|
parent,
|
||||||
publishedLocale,
|
publishedLocale,
|
||||||
req,
|
req,
|
||||||
@@ -34,7 +33,6 @@ export const createGlobalVersion: CreateGlobalVersion = async function createGlo
|
|||||||
autosave,
|
autosave,
|
||||||
createdAt,
|
createdAt,
|
||||||
latest: true,
|
latest: true,
|
||||||
localeStatus,
|
|
||||||
parent,
|
parent,
|
||||||
publishedLocale,
|
publishedLocale,
|
||||||
snapshot,
|
snapshot,
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ export const createVersion: CreateVersion = async function createVersion(
|
|||||||
autosave,
|
autosave,
|
||||||
collectionSlug,
|
collectionSlug,
|
||||||
createdAt,
|
createdAt,
|
||||||
localeStatus,
|
|
||||||
parent,
|
parent,
|
||||||
publishedLocale,
|
publishedLocale,
|
||||||
req,
|
req,
|
||||||
@@ -38,7 +37,6 @@ export const createVersion: CreateVersion = async function createVersion(
|
|||||||
autosave,
|
autosave,
|
||||||
createdAt,
|
createdAt,
|
||||||
latest: true,
|
latest: true,
|
||||||
localeStatus,
|
|
||||||
parent,
|
parent,
|
||||||
publishedLocale,
|
publishedLocale,
|
||||||
snapshot,
|
snapshot,
|
||||||
|
|||||||
@@ -179,13 +179,6 @@ export const queryDrafts: QueryDrafts = async function queryDrafts(
|
|||||||
|
|
||||||
for (let i = 0; i < result.docs.length; i++) {
|
for (let i = 0; i < result.docs.length; i++) {
|
||||||
const id = result.docs[i].parent
|
const id = result.docs[i].parent
|
||||||
|
|
||||||
const localeStatus = result.docs[i].localeStatus || {}
|
|
||||||
if (locale && localeStatus[locale]) {
|
|
||||||
result.docs[i].status = localeStatus[locale]
|
|
||||||
result.docs[i].version._status = localeStatus[locale]
|
|
||||||
}
|
|
||||||
|
|
||||||
result.docs[i] = result.docs[i].version ?? {}
|
result.docs[i] = result.docs[i].version ?? {}
|
||||||
result.docs[i].id = id
|
result.docs[i].id = id
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ export async function createGlobalVersion<T extends TypeWithID>(
|
|||||||
autosave,
|
autosave,
|
||||||
createdAt,
|
createdAt,
|
||||||
globalSlug,
|
globalSlug,
|
||||||
localeStatus,
|
|
||||||
publishedLocale,
|
publishedLocale,
|
||||||
req,
|
req,
|
||||||
returning,
|
returning,
|
||||||
@@ -36,7 +35,6 @@ export async function createGlobalVersion<T extends TypeWithID>(
|
|||||||
autosave,
|
autosave,
|
||||||
createdAt,
|
createdAt,
|
||||||
latest: true,
|
latest: true,
|
||||||
localeStatus,
|
|
||||||
publishedLocale,
|
publishedLocale,
|
||||||
snapshot,
|
snapshot,
|
||||||
updatedAt,
|
updatedAt,
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ export async function createVersion<T extends TypeWithID>(
|
|||||||
autosave,
|
autosave,
|
||||||
collectionSlug,
|
collectionSlug,
|
||||||
createdAt,
|
createdAt,
|
||||||
localeStatus,
|
|
||||||
parent,
|
parent,
|
||||||
publishedLocale,
|
publishedLocale,
|
||||||
req,
|
req,
|
||||||
@@ -41,7 +40,6 @@ export async function createVersion<T extends TypeWithID>(
|
|||||||
autosave,
|
autosave,
|
||||||
createdAt,
|
createdAt,
|
||||||
latest: true,
|
latest: true,
|
||||||
localeStatus,
|
|
||||||
parent,
|
parent,
|
||||||
publishedLocale,
|
publishedLocale,
|
||||||
snapshot,
|
snapshot,
|
||||||
|
|||||||
@@ -36,17 +36,15 @@ export const queryDrafts: QueryDrafts = async function queryDrafts(
|
|||||||
where: combinedWhere,
|
where: combinedWhere,
|
||||||
})
|
})
|
||||||
|
|
||||||
for (let i = 0; i < result.docs.length; i++) {
|
return {
|
||||||
const id = result.docs[i].parent
|
...result,
|
||||||
const localeStatus = result.docs[i].localeStatus || {}
|
docs: result.docs.map((doc) => {
|
||||||
if (locale && localeStatus[locale]) {
|
doc = {
|
||||||
result.docs[i].status = localeStatus[locale]
|
id: doc.parent,
|
||||||
result.docs[i].version._status = localeStatus[locale]
|
...doc.version,
|
||||||
}
|
}
|
||||||
|
|
||||||
result.docs[i] = result.docs[i].version ?? {}
|
return doc
|
||||||
result.docs[i].id = id
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ type AutosaveCellProps = {
|
|||||||
rowData: {
|
rowData: {
|
||||||
autosave?: boolean
|
autosave?: boolean
|
||||||
id: number | string
|
id: number | string
|
||||||
localeStatus?: Record<string, 'draft' | 'published'>
|
|
||||||
publishedLocale?: string
|
publishedLocale?: string
|
||||||
version: {
|
version: {
|
||||||
_status: string
|
_status: string
|
||||||
|
|||||||
@@ -289,7 +289,6 @@ export const createOperation = async <
|
|||||||
autosave,
|
autosave,
|
||||||
collection: collectionConfig,
|
collection: collectionConfig,
|
||||||
docWithLocales: result,
|
docWithLocales: result,
|
||||||
locale,
|
|
||||||
operation: 'create',
|
operation: 'create',
|
||||||
payload,
|
payload,
|
||||||
publishSpecificLocale,
|
publishSpecificLocale,
|
||||||
|
|||||||
@@ -317,7 +317,6 @@ export const updateDocument = async <
|
|||||||
collection: collectionConfig,
|
collection: collectionConfig,
|
||||||
docWithLocales: result,
|
docWithLocales: result,
|
||||||
draft: shouldSaveDraft,
|
draft: shouldSaveDraft,
|
||||||
locale,
|
|
||||||
operation: 'update',
|
operation: 'update',
|
||||||
payload,
|
payload,
|
||||||
publishSpecificLocale,
|
publishSpecificLocale,
|
||||||
|
|||||||
@@ -223,16 +223,6 @@ export const createClientConfig = ({
|
|||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'experimental':
|
|
||||||
if (config.experimental) {
|
|
||||||
clientConfig.experimental = {}
|
|
||||||
if (config.experimental?.localizeStatus) {
|
|
||||||
clientConfig.experimental.localizeStatus = config.experimental.localizeStatus
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break
|
|
||||||
|
|
||||||
case 'folders':
|
case 'folders':
|
||||||
if (config.folders) {
|
if (config.folders) {
|
||||||
clientConfig.folders = {
|
clientConfig.folders = {
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ export const defaults: Omit<Config, 'db' | 'editor' | 'secret'> = {
|
|||||||
defaultDepth: 2,
|
defaultDepth: 2,
|
||||||
defaultMaxTextLength: 40000,
|
defaultMaxTextLength: 40000,
|
||||||
endpoints: [],
|
endpoints: [],
|
||||||
experimental: {},
|
|
||||||
globals: [],
|
globals: [],
|
||||||
graphQL: {
|
graphQL: {
|
||||||
disablePlaygroundInProduction: true,
|
disablePlaygroundInProduction: true,
|
||||||
@@ -122,7 +121,6 @@ export const addDefaultsToConfig = (config: Config): Config => {
|
|||||||
config.defaultDepth = config.defaultDepth ?? 2
|
config.defaultDepth = config.defaultDepth ?? 2
|
||||||
config.defaultMaxTextLength = config.defaultMaxTextLength ?? 40000
|
config.defaultMaxTextLength = config.defaultMaxTextLength ?? 40000
|
||||||
config.endpoints = config.endpoints ?? []
|
config.endpoints = config.endpoints ?? []
|
||||||
config.experimental = config.experimental ?? {}
|
|
||||||
config.globals = config.globals ?? []
|
config.globals = config.globals ?? []
|
||||||
config.graphQL = {
|
config.graphQL = {
|
||||||
disableIntrospectionInProduction: true,
|
disableIntrospectionInProduction: true,
|
||||||
|
|||||||
@@ -726,14 +726,6 @@ export type ImportMapGenerators = Array<
|
|||||||
}) => void
|
}) => void
|
||||||
>
|
>
|
||||||
|
|
||||||
/**
|
|
||||||
* Experimental features.
|
|
||||||
* These may be unstable and may change or be removed in future releases.
|
|
||||||
*/
|
|
||||||
export type ExperimentalConfig = {
|
|
||||||
localizeStatus?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export type AfterErrorHook = (
|
export type AfterErrorHook = (
|
||||||
args: AfterErrorHookArgs,
|
args: AfterErrorHookArgs,
|
||||||
) => AfterErrorResult | Promise<AfterErrorResult>
|
) => AfterErrorResult | Promise<AfterErrorResult>
|
||||||
@@ -1069,12 +1061,6 @@ export type Config = {
|
|||||||
email?: EmailAdapter | Promise<EmailAdapter>
|
email?: EmailAdapter | Promise<EmailAdapter>
|
||||||
/** Custom REST endpoints */
|
/** Custom REST endpoints */
|
||||||
endpoints?: Endpoint[]
|
endpoints?: Endpoint[]
|
||||||
/**
|
|
||||||
* Configure experimental features for Payload.
|
|
||||||
*
|
|
||||||
* These features may be unstable and may change or be removed in future releases.
|
|
||||||
*/
|
|
||||||
experimental?: ExperimentalConfig
|
|
||||||
/**
|
/**
|
||||||
* Options for folder view within the admin panel
|
* Options for folder view within the admin panel
|
||||||
*
|
*
|
||||||
@@ -1344,7 +1330,6 @@ export type SanitizedConfig = {
|
|||||||
/** Default richtext editor to use for richText fields */
|
/** Default richtext editor to use for richText fields */
|
||||||
editor?: RichTextAdapter<any, any, any>
|
editor?: RichTextAdapter<any, any, any>
|
||||||
endpoints: Endpoint[]
|
endpoints: Endpoint[]
|
||||||
experimental?: ExperimentalConfig
|
|
||||||
globals: SanitizedGlobalConfig[]
|
globals: SanitizedGlobalConfig[]
|
||||||
i18n: Required<I18nOptions>
|
i18n: Required<I18nOptions>
|
||||||
jobs: SanitizedJobsConfig
|
jobs: SanitizedJobsConfig
|
||||||
|
|||||||
@@ -390,7 +390,6 @@ export type CreateVersionArgs<T = TypeWithID> = {
|
|||||||
autosave: boolean
|
autosave: boolean
|
||||||
collectionSlug: CollectionSlug
|
collectionSlug: CollectionSlug
|
||||||
createdAt: string
|
createdAt: string
|
||||||
localeStatus?: Record<string, 'draft' | 'published'>
|
|
||||||
/** ID of the parent document for which the version should be created for */
|
/** ID of the parent document for which the version should be created for */
|
||||||
parent: number | string
|
parent: number | string
|
||||||
publishedLocale?: string
|
publishedLocale?: string
|
||||||
@@ -415,7 +414,6 @@ export type CreateGlobalVersionArgs<T = TypeWithID> = {
|
|||||||
autosave: boolean
|
autosave: boolean
|
||||||
createdAt: string
|
createdAt: string
|
||||||
globalSlug: GlobalSlug
|
globalSlug: GlobalSlug
|
||||||
localeStatus?: Record<string, 'draft' | 'published'>
|
|
||||||
/** ID of the parent document for which the version should be created for */
|
/** ID of the parent document for which the version should be created for */
|
||||||
parent: number | string
|
parent: number | string
|
||||||
publishedLocale?: string
|
publishedLocale?: string
|
||||||
|
|||||||
@@ -302,7 +302,6 @@ export const updateOperation = async <
|
|||||||
docWithLocales: result,
|
docWithLocales: result,
|
||||||
draft: shouldSaveDraft,
|
draft: shouldSaveDraft,
|
||||||
global: globalConfig,
|
global: globalConfig,
|
||||||
locale,
|
|
||||||
operation: 'update',
|
operation: 'update',
|
||||||
payload,
|
payload,
|
||||||
publishSpecificLocale,
|
publishSpecificLocale,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
import type { SanitizedConfig } from '../config/types.js'
|
|
||||||
import type { CheckboxField, Field, Option } from '../fields/config/types.js'
|
import type { CheckboxField, Field, Option } from '../fields/config/types.js'
|
||||||
|
|
||||||
export const statuses: Option[] = [
|
export const statuses: Option[] = [
|
||||||
@@ -44,23 +43,3 @@ export const versionSnapshotField: CheckboxField = {
|
|||||||
},
|
},
|
||||||
index: true,
|
index: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildLocaleStatusField(config: SanitizedConfig): Field[] {
|
|
||||||
if (!config.localization || !config.localization.locales) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
return config.localization.locales.map((locale) => {
|
|
||||||
const code = typeof locale === 'string' ? locale : locale.code
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: code,
|
|
||||||
type: 'select',
|
|
||||||
index: true,
|
|
||||||
options: [
|
|
||||||
{ label: ({ t }) => t('version:draft'), value: 'draft' },
|
|
||||||
{ label: ({ t }) => t('version:published'), value: 'published' },
|
|
||||||
],
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { SanitizedCollectionConfig } from '../collections/config/types.js'
|
|||||||
import type { SanitizedConfig } from '../config/types.js'
|
import type { SanitizedConfig } from '../config/types.js'
|
||||||
import type { Field, FlattenedField } from '../fields/config/types.js'
|
import type { Field, FlattenedField } from '../fields/config/types.js'
|
||||||
|
|
||||||
import { buildLocaleStatusField, versionSnapshotField } from './baseFields.js'
|
import { versionSnapshotField } from './baseFields.js'
|
||||||
|
|
||||||
export const buildVersionCollectionFields = <T extends boolean = false>(
|
export const buildVersionCollectionFields = <T extends boolean = false>(
|
||||||
config: SanitizedConfig,
|
config: SanitizedConfig,
|
||||||
@@ -62,23 +62,6 @@ export const buildVersionCollectionFields = <T extends boolean = false>(
|
|||||||
return locale.code
|
return locale.code
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
if (config.experimental?.localizeStatus) {
|
|
||||||
const localeStatusFields = buildLocaleStatusField(config)
|
|
||||||
|
|
||||||
fields.push({
|
|
||||||
name: 'localeStatus',
|
|
||||||
type: 'group',
|
|
||||||
admin: {
|
|
||||||
disableBulkEdit: true,
|
|
||||||
disabled: true,
|
|
||||||
},
|
|
||||||
fields: localeStatusFields,
|
|
||||||
...(flatten && {
|
|
||||||
flattenedFields: localeStatusFields as FlattenedField[],
|
|
||||||
})!,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fields.push({
|
fields.push({
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { SanitizedConfig } from '../config/types.js'
|
|||||||
import type { Field, FlattenedField } from '../fields/config/types.js'
|
import type { Field, FlattenedField } from '../fields/config/types.js'
|
||||||
import type { SanitizedGlobalConfig } from '../globals/config/types.js'
|
import type { SanitizedGlobalConfig } from '../globals/config/types.js'
|
||||||
|
|
||||||
import { buildLocaleStatusField, versionSnapshotField } from './baseFields.js'
|
import { versionSnapshotField } from './baseFields.js'
|
||||||
|
|
||||||
export const buildVersionGlobalFields = <T extends boolean = false>(
|
export const buildVersionGlobalFields = <T extends boolean = false>(
|
||||||
config: SanitizedConfig,
|
config: SanitizedConfig,
|
||||||
@@ -56,23 +56,6 @@ export const buildVersionGlobalFields = <T extends boolean = false>(
|
|||||||
return locale.code
|
return locale.code
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
if (config.experimental.localizeStatus) {
|
|
||||||
const localeStatusFields = buildLocaleStatusField(config)
|
|
||||||
|
|
||||||
fields.push({
|
|
||||||
name: 'localeStatus',
|
|
||||||
type: 'group',
|
|
||||||
admin: {
|
|
||||||
disableBulkEdit: true,
|
|
||||||
disabled: true,
|
|
||||||
},
|
|
||||||
fields: localeStatusFields,
|
|
||||||
...(flatten && {
|
|
||||||
flattenedFields: localeStatusFields as FlattenedField[],
|
|
||||||
})!,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fields.push({
|
fields.push({
|
||||||
|
|||||||
@@ -111,12 +111,6 @@ export const replaceWithDraftIfAvailable = async <T extends TypeWithID>({
|
|||||||
draft.version = {} as T
|
draft.version = {} as T
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lift locale status from version data if available
|
|
||||||
const localeStatus = draft.localeStatus || {}
|
|
||||||
if (locale && localeStatus[locale]) {
|
|
||||||
;(draft.version as { _status?: string })['_status'] = localeStatus[locale]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disregard all other draft content at this point,
|
// Disregard all other draft content at this point,
|
||||||
// Only interested in the version itself.
|
// Only interested in the version itself.
|
||||||
// Operations will handle firing hooks, etc.
|
// Operations will handle firing hooks, etc.
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { version } from 'os'
|
|
||||||
|
|
||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
import type { SanitizedCollectionConfig, TypeWithID } from '../collections/config/types.js'
|
import type { SanitizedCollectionConfig, TypeWithID } from '../collections/config/types.js'
|
||||||
import type { SanitizedGlobalConfig } from '../globals/config/types.js'
|
import type { SanitizedGlobalConfig } from '../globals/config/types.js'
|
||||||
@@ -18,7 +16,6 @@ type Args = {
|
|||||||
draft?: boolean
|
draft?: boolean
|
||||||
global?: SanitizedGlobalConfig
|
global?: SanitizedGlobalConfig
|
||||||
id?: number | string
|
id?: number | string
|
||||||
locale?: null | string
|
|
||||||
operation?: 'create' | 'restoreVersion' | 'update'
|
operation?: 'create' | 'restoreVersion' | 'update'
|
||||||
payload: Payload
|
payload: Payload
|
||||||
publishSpecificLocale?: string
|
publishSpecificLocale?: string
|
||||||
@@ -34,7 +31,6 @@ export const saveVersion = async ({
|
|||||||
docWithLocales: doc,
|
docWithLocales: doc,
|
||||||
draft,
|
draft,
|
||||||
global,
|
global,
|
||||||
locale,
|
|
||||||
operation,
|
operation,
|
||||||
payload,
|
payload,
|
||||||
publishSpecificLocale,
|
publishSpecificLocale,
|
||||||
@@ -46,7 +42,6 @@ export const saveVersion = async ({
|
|||||||
let createNewVersion = true
|
let createNewVersion = true
|
||||||
const now = new Date().toISOString()
|
const now = new Date().toISOString()
|
||||||
const versionData = deepCopyObjectSimple(doc)
|
const versionData = deepCopyObjectSimple(doc)
|
||||||
|
|
||||||
if (draft) {
|
if (draft) {
|
||||||
versionData._status = 'draft'
|
versionData._status = 'draft'
|
||||||
}
|
}
|
||||||
@@ -60,6 +55,7 @@ export const saveVersion = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (autosave) {
|
||||||
let docs
|
let docs
|
||||||
const findVersionArgs = {
|
const findVersionArgs = {
|
||||||
limit: 1,
|
limit: 1,
|
||||||
@@ -92,7 +88,6 @@ export const saveVersion = async ({
|
|||||||
}
|
}
|
||||||
const [latestVersion] = docs
|
const [latestVersion] = docs
|
||||||
|
|
||||||
if (autosave) {
|
|
||||||
// overwrite the latest version if it's set to autosave
|
// overwrite the latest version if it's set to autosave
|
||||||
if (latestVersion && 'autosave' in latestVersion && latestVersion.autosave === true) {
|
if (latestVersion && 'autosave' in latestVersion && latestVersion.autosave === true) {
|
||||||
createNewVersion = false
|
createNewVersion = false
|
||||||
@@ -130,53 +125,11 @@ export const saveVersion = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (createNewVersion) {
|
if (createNewVersion) {
|
||||||
let localeStatus = {}
|
|
||||||
const localizationEnabled =
|
|
||||||
payload.config.localization && payload.config.localization.locales.length > 0
|
|
||||||
|
|
||||||
if (
|
|
||||||
localizationEnabled &&
|
|
||||||
payload.config.localization !== false &&
|
|
||||||
payload.config.experimental?.localizeStatus
|
|
||||||
) {
|
|
||||||
const allLocales = (
|
|
||||||
(payload.config.localization && payload.config.localization?.locales) ||
|
|
||||||
[]
|
|
||||||
).map((locale) => (typeof locale === 'string' ? locale : locale.code))
|
|
||||||
|
|
||||||
// If `publish all`, set all locales to published
|
|
||||||
if (versionData._status === 'published' && !publishSpecificLocale) {
|
|
||||||
localeStatus = Object.fromEntries(allLocales.map((code) => [code, 'published']))
|
|
||||||
} else if (publishSpecificLocale || (locale && versionData._status === 'draft')) {
|
|
||||||
const status: 'draft' | 'published' = publishSpecificLocale ? 'published' : 'draft'
|
|
||||||
const incomingLocale = String(publishSpecificLocale || locale)
|
|
||||||
const existing = latestVersion?.localeStatus
|
|
||||||
|
|
||||||
// If no locale statuses are set, set it and set all others to draft
|
|
||||||
if (!existing) {
|
|
||||||
localeStatus = {
|
|
||||||
...Object.fromEntries(
|
|
||||||
allLocales.filter((code) => code !== incomingLocale).map((code) => [code, 'draft']),
|
|
||||||
),
|
|
||||||
[incomingLocale]: status,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// If locales already exist, update the status for the incoming locale
|
|
||||||
const { [incomingLocale]: _, ...rest } = existing
|
|
||||||
localeStatus = {
|
|
||||||
...rest,
|
|
||||||
[incomingLocale]: status,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const createVersionArgs = {
|
const createVersionArgs = {
|
||||||
autosave: Boolean(autosave),
|
autosave: Boolean(autosave),
|
||||||
collectionSlug: undefined as string | undefined,
|
collectionSlug: undefined as string | undefined,
|
||||||
createdAt: operation === 'restoreVersion' ? versionData.createdAt : now,
|
createdAt: operation === 'restoreVersion' ? versionData.createdAt : now,
|
||||||
globalSlug: undefined as string | undefined,
|
globalSlug: undefined as string | undefined,
|
||||||
localeStatus,
|
|
||||||
parent: collection ? id : undefined,
|
parent: collection ? id : undefined,
|
||||||
publishedLocale: publishSpecificLocale || undefined,
|
publishedLocale: publishSpecificLocale || undefined,
|
||||||
req,
|
req,
|
||||||
|
|||||||
@@ -122,7 +122,6 @@ export type SanitizedGlobalVersions = {
|
|||||||
export type TypeWithVersion<T> = {
|
export type TypeWithVersion<T> = {
|
||||||
createdAt: string
|
createdAt: string
|
||||||
id: string
|
id: string
|
||||||
localeStatus: Record<string, 'draft' | 'published'>
|
|
||||||
parent: number | string
|
parent: number | string
|
||||||
publishedLocale?: string
|
publishedLocale?: string
|
||||||
snapshot?: boolean
|
snapshot?: boolean
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ export const Status: React.FC = () => {
|
|||||||
hasPublishedDoc,
|
hasPublishedDoc,
|
||||||
incrementVersionCount,
|
incrementVersionCount,
|
||||||
isTrashed,
|
isTrashed,
|
||||||
savedDocumentData: doc,
|
|
||||||
setHasPublishedDoc,
|
setHasPublishedDoc,
|
||||||
setMostRecentVersionIsAutosaved,
|
setMostRecentVersionIsAutosaved,
|
||||||
setUnpublishedVersionCount,
|
setUnpublishedVersionCount,
|
||||||
@@ -38,7 +37,6 @@ export const Status: React.FC = () => {
|
|||||||
routes: { api },
|
routes: { api },
|
||||||
serverURL,
|
serverURL,
|
||||||
},
|
},
|
||||||
getEntityConfig,
|
|
||||||
} = useConfig()
|
} = useConfig()
|
||||||
|
|
||||||
const { reset: resetForm } = useForm()
|
const { reset: resetForm } = useForm()
|
||||||
@@ -48,22 +46,16 @@ export const Status: React.FC = () => {
|
|||||||
const unPublishModalSlug = `confirm-un-publish-${id}`
|
const unPublishModalSlug = `confirm-un-publish-${id}`
|
||||||
const revertModalSlug = `confirm-revert-${id}`
|
const revertModalSlug = `confirm-revert-${id}`
|
||||||
|
|
||||||
let statusToRender: 'changed' | 'draft' | 'published' = 'draft'
|
let statusToRender: 'changed' | 'draft' | 'published'
|
||||||
|
|
||||||
const collectionConfig = getEntityConfig({ collectionSlug })
|
if (unpublishedVersionCount > 0 && hasPublishedDoc) {
|
||||||
const globalConfig = getEntityConfig({ globalSlug })
|
statusToRender = 'changed'
|
||||||
|
} else if (!hasPublishedDoc) {
|
||||||
const docConfig = collectionConfig || globalConfig
|
statusToRender = 'draft'
|
||||||
const autosaveEnabled =
|
} else if (hasPublishedDoc && unpublishedVersionCount <= 0) {
|
||||||
typeof docConfig?.versions?.drafts === 'object' ? docConfig.versions.drafts.autosave : false
|
statusToRender = 'published'
|
||||||
|
|
||||||
if (autosaveEnabled) {
|
|
||||||
if (hasPublishedDoc) {
|
|
||||||
statusToRender = unpublishedVersionCount > 0 ? 'changed' : 'published'
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
statusToRender = doc._status || 'draft'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const displayStatusKey = isTrashed
|
const displayStatusKey = isTrashed
|
||||||
? hasPublishedDoc
|
? hasPublishedDoc
|
||||||
? 'previouslyPublished'
|
? 'previouslyPublished'
|
||||||
@@ -201,7 +193,7 @@ export const Status: React.FC = () => {
|
|||||||
{!isTrashed &&
|
{!isTrashed &&
|
||||||
canUpdate &&
|
canUpdate &&
|
||||||
hasPublishedDoc &&
|
hasPublishedDoc &&
|
||||||
(statusToRender === 'changed' || statusToRender === 'draft') && (
|
statusToRender === 'changed' && (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
—
|
—
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -428,9 +428,6 @@ export default buildConfigWithDefaults({
|
|||||||
slug: 'global-text',
|
slug: 'global-text',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
experimental: {
|
|
||||||
localizeStatus: true,
|
|
||||||
},
|
|
||||||
localization: {
|
localization: {
|
||||||
filterAvailableLocales: ({ locales }) => {
|
filterAvailableLocales: ({ locales }) => {
|
||||||
return locales.filter((locale) => locale.code !== 'xx')
|
return locales.filter((locale) => locale.code !== 'xx')
|
||||||
|
|||||||
@@ -673,33 +673,6 @@ describe('Localization', () => {
|
|||||||
await changeLocale(page, defaultLocale)
|
await changeLocale(page, defaultLocale)
|
||||||
await expect(page.locator('#field-title')).toBeEmpty()
|
await expect(page.locator('#field-title')).toBeEmpty()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should show localized status in collection list', async () => {
|
|
||||||
await page.goto(urlPostsWithDrafts.create)
|
|
||||||
const engTitle = 'Eng published'
|
|
||||||
const spanTitle = 'Spanish draft'
|
|
||||||
|
|
||||||
await changeLocale(page, defaultLocale)
|
|
||||||
await fillValues({ title: engTitle })
|
|
||||||
await saveDocAndAssert(page)
|
|
||||||
|
|
||||||
await changeLocale(page, spanishLocale)
|
|
||||||
await fillValues({ title: spanTitle })
|
|
||||||
await saveDocAndAssert(page, '#action-save-draft')
|
|
||||||
|
|
||||||
await page.goto(urlPostsWithDrafts.list)
|
|
||||||
|
|
||||||
const columns = page.getByRole('button', { name: 'Columns' })
|
|
||||||
await columns.click()
|
|
||||||
await page.locator('#_status').click()
|
|
||||||
|
|
||||||
await expect(page.locator('.row-1 .cell-title')).toContainText(spanTitle)
|
|
||||||
await expect(page.locator('.row-1 .cell-_status')).toContainText('Draft')
|
|
||||||
|
|
||||||
await changeLocale(page, defaultLocale)
|
|
||||||
await expect(page.locator('.row-1 .cell-title')).toContainText(engTitle)
|
|
||||||
await expect(page.locator('.row-1 .cell-_status')).toContainText('Published')
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should not show publish specific locale button when no localized fields exist', async () => {
|
test('should not show publish specific locale button when no localized fields exist', async () => {
|
||||||
|
|||||||
@@ -1097,7 +1097,7 @@ describe('Versions', () => {
|
|||||||
|
|
||||||
await textField.fill('spanish draft')
|
await textField.fill('spanish draft')
|
||||||
await saveDocAndAssert(page, '#action-save-draft')
|
await saveDocAndAssert(page, '#action-save-draft')
|
||||||
await expect(status).toContainText('Draft')
|
await expect(status).toContainText('Changed')
|
||||||
|
|
||||||
await changeLocale(page, 'en')
|
await changeLocale(page, 'en')
|
||||||
await textField.fill('english published')
|
await textField.fill('english published')
|
||||||
@@ -1130,7 +1130,7 @@ describe('Versions', () => {
|
|||||||
|
|
||||||
const publishedDoc = data.docs[0]
|
const publishedDoc = data.docs[0]
|
||||||
|
|
||||||
expect(publishedDoc?.text).toStrictEqual({
|
expect(publishedDoc.text).toStrictEqual({
|
||||||
en: 'english published',
|
en: 'english published',
|
||||||
es: 'spanish published',
|
es: 'spanish published',
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user