fix: adds enableStatusLocalization option to reflects current locale status across UI
This commit is contained in:
@@ -78,12 +78,13 @@ export default buildConfig({
|
||||
|
||||
The following options are available:
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`locales`** | Array of all the languages that you would like to support. [More details](#locales) |
|
||||
| **`defaultLocale`** | Required string that matches one of the locale codes from the array provided. By default, if no locale is specified, documents will be returned in this locale. |
|
||||
| **`fallback`** | Boolean enabling "fallback" locale functionality. If a document is requested in a locale, but a field does not have a localized value corresponding to the requested locale, then if this property is enabled, the document will automatically fall back to the fallback locale value. If this property is not enabled, the value will not be populated unless a fallback is explicitly provided in the request. True by default. |
|
||||
| **`filterAvailableLocales`** | A function that is called with the array of `locales` and the `req`, it should return locales to show in admin UI selector. [See more](#filter-available-options). |
|
||||
| Option | Description |
|
||||
| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`locales`** | Array of all the languages that you would like to support. [More details](#locales) |
|
||||
| **`defaultLocale`** | Required string that matches one of the locale codes from the array provided. By default, if no locale is specified, documents will be returned in this locale. |
|
||||
| **`enableStatusLocalization`** | **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`. |
|
||||
| **`fallback`** | Boolean enabling "fallback" locale functionality. If a document is requested in a locale, but a field does not have a localized value corresponding to the requested locale, then if this property is enabled, the document will automatically fall back to the fallback locale value. If this property is not enabled, the value will not be populated unless a fallback is explicitly provided in the request. True by default. |
|
||||
| **`filterAvailableLocales`** | A function that is called with the array of `locales` and the `req`, it should return locales to show in admin UI selector. [See more](#filter-available-options). |
|
||||
|
||||
### Locales
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ export const createGlobalVersion: CreateGlobalVersion = async function createGlo
|
||||
autosave,
|
||||
createdAt,
|
||||
globalSlug,
|
||||
localeStatus,
|
||||
parent,
|
||||
publishedLocale,
|
||||
req,
|
||||
@@ -31,6 +32,7 @@ export const createGlobalVersion: CreateGlobalVersion = async function createGlo
|
||||
autosave,
|
||||
createdAt,
|
||||
latest: true,
|
||||
localeStatus,
|
||||
parent,
|
||||
publishedLocale,
|
||||
snapshot,
|
||||
|
||||
@@ -12,6 +12,7 @@ export const createVersion: CreateVersion = async function createVersion(
|
||||
autosave,
|
||||
collectionSlug,
|
||||
createdAt,
|
||||
localeStatus,
|
||||
parent,
|
||||
publishedLocale,
|
||||
req,
|
||||
@@ -35,6 +36,7 @@ export const createVersion: CreateVersion = async function createVersion(
|
||||
autosave,
|
||||
createdAt,
|
||||
latest: true,
|
||||
localeStatus,
|
||||
parent,
|
||||
publishedLocale,
|
||||
snapshot,
|
||||
|
||||
@@ -167,6 +167,13 @@ export const queryDrafts: QueryDrafts = async function queryDrafts(
|
||||
|
||||
for (let i = 0; i < result.docs.length; i++) {
|
||||
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].id = id
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ export async function createGlobalVersion<T extends TypeWithID>(
|
||||
autosave,
|
||||
createdAt,
|
||||
globalSlug,
|
||||
localeStatus,
|
||||
publishedLocale,
|
||||
req,
|
||||
returning,
|
||||
@@ -35,6 +36,7 @@ export async function createGlobalVersion<T extends TypeWithID>(
|
||||
autosave,
|
||||
createdAt,
|
||||
latest: true,
|
||||
localeStatus,
|
||||
publishedLocale,
|
||||
snapshot,
|
||||
updatedAt,
|
||||
|
||||
@@ -15,6 +15,7 @@ export async function createVersion<T extends TypeWithID>(
|
||||
autosave,
|
||||
collectionSlug,
|
||||
createdAt,
|
||||
localeStatus,
|
||||
parent,
|
||||
publishedLocale,
|
||||
req,
|
||||
@@ -40,6 +41,7 @@ export async function createVersion<T extends TypeWithID>(
|
||||
autosave,
|
||||
createdAt,
|
||||
latest: true,
|
||||
localeStatus,
|
||||
parent,
|
||||
publishedLocale,
|
||||
snapshot,
|
||||
|
||||
@@ -116,7 +116,7 @@ export const VersionPillLabel: React.FC<{
|
||||
)}
|
||||
</React.Fragment>
|
||||
)}
|
||||
{localeLabel && <Pill>{localeLabel}</Pill>}
|
||||
{localeLabel && <Pill size="small">{localeLabel}</Pill>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ type AutosaveCellProps = {
|
||||
rowData: {
|
||||
autosave?: boolean
|
||||
id: number | string
|
||||
localeStatus?: Record<string, 'draft' | 'published'>
|
||||
publishedLocale?: string
|
||||
version: {
|
||||
_status: string
|
||||
|
||||
@@ -285,6 +285,7 @@ export const createOperation = async <
|
||||
autosave,
|
||||
collection: collectionConfig,
|
||||
docWithLocales: result,
|
||||
locale,
|
||||
payload,
|
||||
req,
|
||||
})
|
||||
|
||||
@@ -314,6 +314,7 @@ export const updateDocument = async <
|
||||
collection: collectionConfig,
|
||||
docWithLocales: result,
|
||||
draft: shouldSaveDraft,
|
||||
locale,
|
||||
payload,
|
||||
publishSpecificLocale,
|
||||
req,
|
||||
|
||||
@@ -193,6 +193,11 @@ export const createClientConfig = ({
|
||||
config.localization.defaultLocalePublishOption
|
||||
}
|
||||
|
||||
if (config.localization.enableStatusLocalization) {
|
||||
clientConfig.localization.enableStatusLocalization =
|
||||
config.localization.enableStatusLocalization
|
||||
}
|
||||
|
||||
if (config.localization.fallback) {
|
||||
clientConfig.localization.fallback = config.localization.fallback
|
||||
}
|
||||
|
||||
@@ -486,6 +486,12 @@ export type BaseLocalizationConfig = {
|
||||
* @default 'all'
|
||||
*/
|
||||
defaultLocalePublishOption?: 'active' | 'all'
|
||||
/**
|
||||
* Enable localization of the status of the document.
|
||||
* If enabled, the status will reflect the current locale throughout the Admin UI.
|
||||
* @default false
|
||||
*/
|
||||
enableStatusLocalization?: boolean
|
||||
/** Set to `true` to let missing values in localised fields fall back to the values in `defaultLocale`
|
||||
*
|
||||
* If false, then no requests will fallback unless a fallbackLocale is specified in the request.
|
||||
|
||||
@@ -386,6 +386,7 @@ export type CreateVersionArgs<T = TypeWithID> = {
|
||||
autosave: boolean
|
||||
collectionSlug: CollectionSlug
|
||||
createdAt: string
|
||||
localeStatus: Record<string, 'draft' | 'published'>
|
||||
/** ID of the parent document for which the version should be created for */
|
||||
parent: number | string
|
||||
publishedLocale?: string
|
||||
@@ -410,6 +411,7 @@ export type CreateGlobalVersionArgs<T = TypeWithID> = {
|
||||
autosave: boolean
|
||||
createdAt: string
|
||||
globalSlug: GlobalSlug
|
||||
localeStatus: Record<string, 'draft' | 'published'>
|
||||
/** ID of the parent document for which the version should be created for */
|
||||
parent: number | string
|
||||
publishedLocale?: string
|
||||
|
||||
@@ -282,6 +282,7 @@ export const updateOperation = async <
|
||||
docWithLocales: result,
|
||||
draft: shouldSaveDraft,
|
||||
global: globalConfig,
|
||||
locale,
|
||||
payload,
|
||||
publishSpecificLocale,
|
||||
req,
|
||||
|
||||
@@ -62,6 +62,35 @@ export const buildVersionCollectionFields = <T extends boolean = false>(
|
||||
return locale.code
|
||||
}),
|
||||
})
|
||||
|
||||
if (config.localization.enableStatusLocalization) {
|
||||
const localeStatusFields: Field[] = config.localization.locales.map((locale) => {
|
||||
const code = typeof locale === 'string' ? locale : locale.code
|
||||
|
||||
return {
|
||||
name: code,
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'Draft', value: 'draft' },
|
||||
{ label: 'Published', value: 'published' },
|
||||
],
|
||||
}
|
||||
})
|
||||
|
||||
fields.push({
|
||||
name: 'localeStatus',
|
||||
type: 'group',
|
||||
admin: {
|
||||
disableBulkEdit: true,
|
||||
disabled: true,
|
||||
},
|
||||
fields: localeStatusFields,
|
||||
index: true,
|
||||
...(flatten && {
|
||||
flattenedFields: localeStatusFields as FlattenedField[],
|
||||
})!,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fields.push({
|
||||
|
||||
@@ -56,6 +56,35 @@ export const buildVersionGlobalFields = <T extends boolean = false>(
|
||||
return locale.code
|
||||
}),
|
||||
})
|
||||
|
||||
if (config.localization.enableStatusLocalization) {
|
||||
const localeStatusFields: Field[] = config.localization.locales.map((locale) => {
|
||||
const code = typeof locale === 'string' ? locale : locale.code
|
||||
|
||||
return {
|
||||
name: code,
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'Draft', value: 'draft' },
|
||||
{ label: 'Published', value: 'published' },
|
||||
],
|
||||
}
|
||||
})
|
||||
|
||||
fields.push({
|
||||
name: 'localeStatus',
|
||||
type: 'group',
|
||||
admin: {
|
||||
disableBulkEdit: true,
|
||||
disabled: true,
|
||||
},
|
||||
fields: localeStatusFields,
|
||||
index: true,
|
||||
...(flatten && {
|
||||
flattenedFields: localeStatusFields as FlattenedField[],
|
||||
})!,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fields.push({
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { version } from 'os'
|
||||
|
||||
// @ts-strict-ignore
|
||||
import type { SanitizedCollectionConfig, TypeWithID } from '../collections/config/types.js'
|
||||
import type { SanitizedGlobalConfig } from '../globals/config/types.js'
|
||||
@@ -16,6 +18,7 @@ type Args = {
|
||||
draft?: boolean
|
||||
global?: SanitizedGlobalConfig
|
||||
id?: number | string
|
||||
locale?: null | string
|
||||
payload: Payload
|
||||
publishSpecificLocale?: string
|
||||
req?: PayloadRequest
|
||||
@@ -30,6 +33,7 @@ export const saveVersion = async ({
|
||||
docWithLocales: doc,
|
||||
draft,
|
||||
global,
|
||||
locale,
|
||||
payload,
|
||||
publishSpecificLocale,
|
||||
req,
|
||||
@@ -40,6 +44,7 @@ export const saveVersion = async ({
|
||||
let createNewVersion = true
|
||||
const now = new Date().toISOString()
|
||||
const versionData = deepCopyObjectSimple(doc)
|
||||
|
||||
if (draft) {
|
||||
versionData._status = 'draft'
|
||||
}
|
||||
@@ -53,39 +58,39 @@ export const saveVersion = async ({
|
||||
}
|
||||
|
||||
try {
|
||||
if (autosave) {
|
||||
let docs
|
||||
const findVersionArgs = {
|
||||
let docs
|
||||
const findVersionArgs = {
|
||||
limit: 1,
|
||||
pagination: false,
|
||||
req,
|
||||
sort: '-updatedAt',
|
||||
}
|
||||
|
||||
if (collection) {
|
||||
;({ docs } = await payload.db.findVersions({
|
||||
...findVersionArgs,
|
||||
collection: collection.slug,
|
||||
limit: 1,
|
||||
pagination: false,
|
||||
req,
|
||||
sort: '-updatedAt',
|
||||
}
|
||||
|
||||
if (collection) {
|
||||
;({ docs } = await payload.db.findVersions({
|
||||
...findVersionArgs,
|
||||
collection: collection.slug,
|
||||
limit: 1,
|
||||
pagination: false,
|
||||
req,
|
||||
where: {
|
||||
parent: {
|
||||
equals: id,
|
||||
},
|
||||
where: {
|
||||
parent: {
|
||||
equals: id,
|
||||
},
|
||||
}))
|
||||
} else {
|
||||
;({ docs } = await payload.db.findGlobalVersions({
|
||||
...findVersionArgs,
|
||||
global: global!.slug,
|
||||
limit: 1,
|
||||
pagination: false,
|
||||
req,
|
||||
}))
|
||||
}
|
||||
const [latestVersion] = docs
|
||||
},
|
||||
}))
|
||||
} else {
|
||||
;({ docs } = await payload.db.findGlobalVersions({
|
||||
...findVersionArgs,
|
||||
global: global!.slug,
|
||||
limit: 1,
|
||||
pagination: false,
|
||||
req,
|
||||
}))
|
||||
}
|
||||
const [latestVersion] = docs
|
||||
|
||||
if (autosave) {
|
||||
// overwrite the latest version if it's set to autosave
|
||||
if (latestVersion && 'autosave' in latestVersion && latestVersion.autosave === true) {
|
||||
createNewVersion = false
|
||||
@@ -123,11 +128,53 @@ export const saveVersion = async ({
|
||||
}
|
||||
|
||||
if (createNewVersion) {
|
||||
let localeStatus = {}
|
||||
const localizationEnabled =
|
||||
payload.config.localization && payload.config.localization.locales.length > 0
|
||||
|
||||
if (
|
||||
localizationEnabled &&
|
||||
payload.config.localization !== false &&
|
||||
payload.config.localization.enableStatusLocalization
|
||||
) {
|
||||
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 = {
|
||||
autosave: Boolean(autosave),
|
||||
collectionSlug: undefined as string | undefined,
|
||||
createdAt: now,
|
||||
globalSlug: undefined as string | undefined,
|
||||
localeStatus,
|
||||
parent: collection ? id : undefined,
|
||||
publishedLocale: publishSpecificLocale || undefined,
|
||||
req,
|
||||
|
||||
@@ -122,6 +122,7 @@ export type SanitizedGlobalVersions = {
|
||||
export type TypeWithVersion<T> = {
|
||||
createdAt: string
|
||||
id: string
|
||||
localeStatus: Record<string, 'draft' | 'published'>
|
||||
parent: number | string
|
||||
publishedLocale?: string
|
||||
snapshot?: boolean
|
||||
|
||||
@@ -36,6 +36,7 @@ export const Status: React.FC = () => {
|
||||
routes: { api },
|
||||
serverURL,
|
||||
},
|
||||
getEntityConfig,
|
||||
} = useConfig()
|
||||
|
||||
const { reset: resetForm } = useForm()
|
||||
@@ -47,8 +48,19 @@ export const Status: React.FC = () => {
|
||||
|
||||
let statusToRender: 'changed' | 'draft' | 'published'
|
||||
|
||||
const collectionConfig = getEntityConfig({ collectionSlug })
|
||||
const globalConfig = getEntityConfig({ globalSlug })
|
||||
|
||||
const docConfig = collectionConfig || globalConfig
|
||||
const autosaveEnabled =
|
||||
typeof docConfig?.versions?.drafts === 'object' ? docConfig.versions.drafts.autosave : false
|
||||
|
||||
if (unpublishedVersionCount > 0 && hasPublishedDoc) {
|
||||
statusToRender = 'changed'
|
||||
if (autosaveEnabled) {
|
||||
statusToRender = 'changed'
|
||||
} else {
|
||||
statusToRender = 'draft'
|
||||
}
|
||||
} else if (!hasPublishedDoc) {
|
||||
statusToRender = 'draft'
|
||||
} else if (hasPublishedDoc && unpublishedVersionCount <= 0) {
|
||||
@@ -183,7 +195,7 @@ export const Status: React.FC = () => {
|
||||
/>
|
||||
</React.Fragment>
|
||||
)}
|
||||
{canUpdate && statusToRender === 'changed' && (
|
||||
{canUpdate && (statusToRender === 'draft' || statusToRender === 'changed') && (
|
||||
<React.Fragment>
|
||||
—
|
||||
<Button
|
||||
|
||||
@@ -432,6 +432,7 @@ export default buildConfigWithDefaults({
|
||||
},
|
||||
defaultLocale,
|
||||
fallback: true,
|
||||
enableStatusLocalization: true,
|
||||
locales: [
|
||||
{
|
||||
code: 'xx',
|
||||
|
||||
@@ -618,6 +618,28 @@ describe('Localization', () => {
|
||||
await expect(searchInput).toBeVisible()
|
||||
await expect(searchInput).toHaveAttribute('placeholder', 'Search by Full title')
|
||||
})
|
||||
|
||||
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)
|
||||
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')
|
||||
})
|
||||
})
|
||||
|
||||
async function fillValues(data: Partial<LocalizedPost>) {
|
||||
|
||||
Reference in New Issue
Block a user