Skip to content

Commit 34ead72

Browse files
fix(ui): copyToLocale should not pass any id's to avoid duplicates (#11887)
### What? Using the `Copy To Locale` function causes validation errors on content with `id` fields in postgres, since these should be unique. ``` key not found: error:valueMustBeUnique key not found: error:followingFieldsInvalid [13:11:29] ERROR: There was an error copying data from "en" to "de" err: { "type": "ValidationError", "message": "error:followingFieldsInvalid id", "stack": ValidationError: error:followingFieldsInvalid id ``` ### Why? In `packages/ui/src/utilities/copyDataFromLocale.ts` we are passing all data from `fromLocaleData` including the `id` fields, which causes duplicates on fields with unique id's like `Blocks` and `Arrays`. ### How? To resolve this i implemented a function that recursively remove any `id` field on the passed data. ### Fixes - #10684 - https://discord.com/channels/967097582721572934/1351497930984521800 --------- Co-authored-by: Jessica Chowdhury <jessica@trbl.design>
1 parent caae598 commit 34ead72

File tree

2 files changed

+42
-5
lines changed

2 files changed

+42
-5
lines changed

packages/ui/src/utilities/copyDataFromLocale.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,17 @@ function mergeData(
183183
return toLocaleData
184184
}
185185

186+
function removeIds(data: Data): Data {
187+
if (Array.isArray(data)) {
188+
return data.map(removeIds)
189+
}
190+
if (typeof data === 'object' && data !== null) {
191+
const { id: _id, ...rest } = data
192+
return Object.fromEntries(Object.entries(rest).map(([key, value]) => [key, removeIds(value)]))
193+
}
194+
return data
195+
}
196+
186197
export const copyDataFromLocaleHandler = async (args: CopyDataFromLocaleArgs) => {
187198
const { req } = args
188199

@@ -288,16 +299,17 @@ export const copyDataFromLocale = async (args: CopyDataFromLocaleArgs) => {
288299
throw new Error(`Error fetching data from locale "${toLocale}"`)
289300
}
290301

291-
const { id, ...fromLocaleDataWithoutID } = fromLocaleData.value
302+
const fromLocaleDataWithoutID = removeIds(fromLocaleData.value)
303+
const toLocaleDataWithoutID = removeIds(toLocaleData.value)
292304

293305
return globalSlug
294306
? await payload.updateGlobal({
295307
slug: globalSlug,
296308
data: overrideData
297309
? fromLocaleDataWithoutID
298310
: mergeData(
299-
fromLocaleData.value,
300-
toLocaleData.value,
311+
fromLocaleDataWithoutID,
312+
toLocaleDataWithoutID,
301313
globals[globalSlug].config.fields,
302314
req,
303315
false,
@@ -313,8 +325,8 @@ export const copyDataFromLocale = async (args: CopyDataFromLocaleArgs) => {
313325
data: overrideData
314326
? fromLocaleDataWithoutID
315327
: mergeData(
316-
fromLocaleData.value,
317-
toLocaleData.value,
328+
fromLocaleDataWithoutID,
329+
toLocaleDataWithoutID,
318330
collections[collectionSlug].config.fields,
319331
req,
320332
false,

test/localization/e2e.spec.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
import { AdminUrlUtil } from '../helpers/adminUrlUtil.js'
2727
import { initPayloadE2ENoConfig } from '../helpers/initPayloadE2ENoConfig.js'
2828
import { POLL_TOPASS_TIMEOUT, TEST_TIMEOUT_LONG } from '../playwright.config.js'
29+
import { blocksCollectionSlug } from './collections/Blocks/index.js'
2930
import { nestedToArrayAndBlockCollectionSlug } from './collections/NestedToArrayAndBlock/index.js'
3031
import { richTextSlug } from './collections/RichText/index.js'
3132
import {
@@ -427,6 +428,30 @@ describe('Localization', () => {
427428
await expect(arrayField).toHaveValue(sampleText)
428429
})
429430

431+
test('should copy block to locale', async () => {
432+
const sampleText = 'Copy this text'
433+
const blocksCollection = new AdminUrlUtil(serverURL, blocksCollectionSlug)
434+
await page.goto(blocksCollection.create)
435+
await changeLocale(page, 'pt')
436+
const addBlock = page.locator('.blocks-field__drawer-toggler')
437+
await addBlock.click()
438+
const selectBlock = page.locator('.blocks-drawer__block button')
439+
await selectBlock.click()
440+
const addContentButton = page.locator('#field-content__0__content button')
441+
await addContentButton.click()
442+
await selectBlock.click()
443+
const textField = page.locator('#field-content__0__content__0__text')
444+
await expect(textField).toBeVisible()
445+
await textField.fill(sampleText)
446+
await saveDocAndAssert(page)
447+
448+
await openCopyToLocaleDrawer(page)
449+
await setToLocale(page, 'English')
450+
await runCopy(page)
451+
452+
await expect(textField).toHaveValue(sampleText)
453+
})
454+
430455
test('should default source locale to current locale', async () => {
431456
await changeLocale(page, spanishLocale)
432457
await createAndSaveDoc(page, url, { title })

0 commit comments

Comments
 (0)