fix(ui): fixed issue with updatedAt timestamps not updating in the UI when drafts are updated (#10503)

Fixes https://github.com/payloadcms/payload/issues/10436

Fixes an issue where drafts' updatedAt timestamp is not being updated.
We weren't updating the `versionData` to have the right timestamp in the
saveVersion operation when drafts were being updated.

Added e2e tests to make sure 'Last Modified' is always different in both
autosave and non-autosave drafts.
This commit is contained in:
Paul
2025-01-13 09:01:34 -06:00
committed by GitHub
parent 0252681313
commit 082c4f0d71
6 changed files with 139 additions and 7 deletions

View File

@@ -42,6 +42,11 @@ export const saveVersion = async ({
if (draft) {
versionData._status = 'draft'
}
if (collection?.timestamps && draft) {
versionData.updatedAt = now
}
if (versionData._id) {
delete versionData._id
}

View File

@@ -49,6 +49,7 @@ export const Autosave: React.FC<Props> = ({ id, collection, global: globalDoc })
setLastUpdateTime,
setMostRecentVersionIsAutosaved,
setUnpublishedVersionCount,
updateSavedDocumentData,
} = useDocumentInfo()
const queueRef = useRef([])
const isProcessingRef = useRef(false)
@@ -180,9 +181,9 @@ export const Autosave: React.FC<Props> = ({ id, collection, global: globalDoc })
setMostRecentVersionIsAutosaved(true)
setUnpublishedVersionCount((prev) => prev + 1)
}
} else {
return res.json()
}
return res.json()
})
.then((json) => {
if (
@@ -231,6 +232,14 @@ export const Autosave: React.FC<Props> = ({ id, collection, global: globalDoc })
setSaving(false)
return
}
} else {
// If it's not an error then we can update the document data inside the context
const document = json?.doc || json?.result
// Manually update the data since this function doesn't fire the `submit` function from useForm
if (document) {
updateSavedDocumentData(document)
}
}
})
.then(() => {

View File

@@ -225,10 +225,12 @@ export const DefaultEditView: React.FC<ClientSideEditViewProps> = ({
async (json): Promise<FormState> => {
const controller = handleAbortRef(abortOnSaveRef)
const document = json?.doc || json?.result
reportUpdate({
id,
entitySlug,
updatedAt: json?.result?.updatedAt || new Date().toISOString(),
updatedAt: document?.updatedAt || new Date().toISOString(),
})
// If we're editing the doc of the logged-in user,
@@ -240,13 +242,19 @@ export const DefaultEditView: React.FC<ClientSideEditViewProps> = ({
incrementVersionCount()
if (typeof updateSavedDocumentData === 'function') {
void updateSavedDocumentData(json?.doc || {})
void updateSavedDocumentData(document || {})
}
if (typeof onSaveFromContext === 'function') {
const operation = id ? 'update' : 'create'
void onSaveFromContext({
...json,
operation: id ? 'update' : 'create',
operation,
updatedAt:
operation === 'update'
? new Date().toISOString()
: document?.updatedAt || new Date().toISOString(),
})
}
@@ -254,7 +262,7 @@ export const DefaultEditView: React.FC<ClientSideEditViewProps> = ({
// Redirect to the same locale if it's been set
const redirectRoute = formatAdminURL({
adminRoute,
path: `/collections/${collectionSlug}/${json?.doc?.id}${locale ? `?locale=${locale}` : ''}`,
path: `/collections/${collectionSlug}/${document?.id}${locale ? `?locale=${locale}` : ''}`,
})
router.push(redirectRoute)
} else {
@@ -269,7 +277,7 @@ export const DefaultEditView: React.FC<ClientSideEditViewProps> = ({
const { state } = await getFormState({
id,
collectionSlug,
data: json?.doc || json?.result,
data: document,
docPermissions,
docPreferences,
globalSlug,

View File

@@ -23,6 +23,8 @@ export default buildConfigWithDefaults({
importMap: {
baseDir: path.resolve(dirname),
},
// The autosave test uses this format in order to compare timestamps in the UI
dateFormat: 'MMMM do yyyy, h:mm:ss a',
},
collections: [
DisablePublish,

View File

@@ -426,6 +426,59 @@ describe('Versions', () => {
await expect(drawer.locator('.id-label')).toBeVisible()
})
test('collection - should update updatedAt', async () => {
await page.goto(url.create)
await page.waitForURL(`**/${url.create}`)
// fill out doc in english
await page.locator('#field-title').fill('title')
await page.locator('#field-description').fill('initial description')
await saveDocAndAssert(page)
const updatedAtWrapper = await page.locator(
'.doc-controls .doc-controls__content .doc-controls__list-item',
{
hasText: 'Last Modified',
},
)
const initialUpdatedAt = await updatedAtWrapper.locator('.doc-controls__value').textContent()
// wait for 1 second so that the timestamp can be different
await wait(1000)
await page.locator('#field-description').fill('changed description')
await saveDocAndAssert(page)
const newUpdatedAt = await updatedAtWrapper.locator('.doc-controls__value').textContent()
expect(newUpdatedAt).not.toEqual(initialUpdatedAt)
})
test('collection - should update updatedAt on autosave', async () => {
await page.goto(autosaveURL.create)
await page.locator('#field-title').fill('autosave title')
await waitForAutoSaveToRunAndComplete(page)
await expect(page.locator('#field-title')).toHaveValue('autosave title')
const updatedAtWrapper = await page.locator(
'.doc-controls .doc-controls__content .doc-controls__list-item',
{
hasText: 'Last Modified',
},
)
const initialUpdatedAt = await updatedAtWrapper.locator('.doc-controls__value').textContent()
// wait for 1 second so that the timestamp can be different
await wait(1000)
await page.locator('#field-title').fill('autosave title updated')
await waitForAutoSaveToRunAndComplete(page)
const newUpdatedAt = await updatedAtWrapper.locator('.doc-controls__value').textContent()
expect(newUpdatedAt).not.toEqual(initialUpdatedAt)
})
test('global - should autosave', async () => {
const url = new AdminUrlUtil(serverURL, autoSaveGlobalSlug)
await page.goto(url.global(autoSaveGlobalSlug))

View File

@@ -593,6 +593,61 @@ describe('Versions', () => {
expect(draftPost.title.es).toBe(spanishTitle)
})
it('should have correct updatedAt timestamps when saving drafts', async () => {
const created = await payload.create({
collection: draftCollectionSlug,
data: {
description: 'desc',
title: 'title',
},
draft: true,
})
await wait(10)
const updated = await payload.update({
id: created.id,
collection: draftCollectionSlug,
data: {
title: 'updated title',
},
draft: true,
})
const createdUpdatedAt = new Date(created.updatedAt)
const updatedUpdatedAt = new Date(updated.updatedAt)
expect(Number(updatedUpdatedAt)).toBeGreaterThan(Number(createdUpdatedAt))
})
it('should have correct updatedAt timestamps when saving drafts with autosave', async () => {
const created = await payload.create({
collection: draftCollectionSlug,
data: {
description: 'desc',
title: 'title',
},
draft: true,
})
await wait(10)
const updated = await payload.update({
id: created.id,
collection: draftCollectionSlug,
data: {
title: 'updated title',
},
draft: true,
autosave: true,
})
const createdUpdatedAt = new Date(created.updatedAt)
const updatedUpdatedAt = new Date(updated.updatedAt)
expect(Number(updatedUpdatedAt)).toBeGreaterThan(Number(createdUpdatedAt))
})
it('should validate when publishing with the draft arg', async () => {
// no title (not valid for publishing)
const doc = await payload.create({