Skip to content

Commit 082c4f0

Browse files
authored
fix(ui): fixed issue with updatedAt timestamps not updating in the UI when drafts are updated (#10503)
Fixes #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.
1 parent 0252681 commit 082c4f0

File tree

6 files changed

+139
-7
lines changed

6 files changed

+139
-7
lines changed

packages/payload/src/versions/saveVersion.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ export const saveVersion = async ({
4242
if (draft) {
4343
versionData._status = 'draft'
4444
}
45+
46+
if (collection?.timestamps && draft) {
47+
versionData.updatedAt = now
48+
}
49+
4550
if (versionData._id) {
4651
delete versionData._id
4752
}

packages/ui/src/elements/Autosave/index.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export const Autosave: React.FC<Props> = ({ id, collection, global: globalDoc })
4949
setLastUpdateTime,
5050
setMostRecentVersionIsAutosaved,
5151
setUnpublishedVersionCount,
52+
updateSavedDocumentData,
5253
} = useDocumentInfo()
5354
const queueRef = useRef([])
5455
const isProcessingRef = useRef(false)
@@ -180,9 +181,9 @@ export const Autosave: React.FC<Props> = ({ id, collection, global: globalDoc })
180181
setMostRecentVersionIsAutosaved(true)
181182
setUnpublishedVersionCount((prev) => prev + 1)
182183
}
183-
} else {
184-
return res.json()
185184
}
185+
186+
return res.json()
186187
})
187188
.then((json) => {
188189
if (
@@ -231,6 +232,14 @@ export const Autosave: React.FC<Props> = ({ id, collection, global: globalDoc })
231232
setSaving(false)
232233
return
233234
}
235+
} else {
236+
// If it's not an error then we can update the document data inside the context
237+
const document = json?.doc || json?.result
238+
239+
// Manually update the data since this function doesn't fire the `submit` function from useForm
240+
if (document) {
241+
updateSavedDocumentData(document)
242+
}
234243
}
235244
})
236245
.then(() => {

packages/ui/src/views/Edit/index.tsx

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -225,10 +225,12 @@ export const DefaultEditView: React.FC<ClientSideEditViewProps> = ({
225225
async (json): Promise<FormState> => {
226226
const controller = handleAbortRef(abortOnSaveRef)
227227

228+
const document = json?.doc || json?.result
229+
228230
reportUpdate({
229231
id,
230232
entitySlug,
231-
updatedAt: json?.result?.updatedAt || new Date().toISOString(),
233+
updatedAt: document?.updatedAt || new Date().toISOString(),
232234
})
233235

234236
// If we're editing the doc of the logged-in user,
@@ -240,21 +242,27 @@ export const DefaultEditView: React.FC<ClientSideEditViewProps> = ({
240242
incrementVersionCount()
241243

242244
if (typeof updateSavedDocumentData === 'function') {
243-
void updateSavedDocumentData(json?.doc || {})
245+
void updateSavedDocumentData(document || {})
244246
}
245247

246248
if (typeof onSaveFromContext === 'function') {
249+
const operation = id ? 'update' : 'create'
250+
247251
void onSaveFromContext({
248252
...json,
249-
operation: id ? 'update' : 'create',
253+
operation,
254+
updatedAt:
255+
operation === 'update'
256+
? new Date().toISOString()
257+
: document?.updatedAt || new Date().toISOString(),
250258
})
251259
}
252260

253261
if (!isEditing && depth < 2) {
254262
// Redirect to the same locale if it's been set
255263
const redirectRoute = formatAdminURL({
256264
adminRoute,
257-
path: `/collections/${collectionSlug}/${json?.doc?.id}${locale ? `?locale=${locale}` : ''}`,
265+
path: `/collections/${collectionSlug}/${document?.id}${locale ? `?locale=${locale}` : ''}`,
258266
})
259267
router.push(redirectRoute)
260268
} else {
@@ -269,7 +277,7 @@ export const DefaultEditView: React.FC<ClientSideEditViewProps> = ({
269277
const { state } = await getFormState({
270278
id,
271279
collectionSlug,
272-
data: json?.doc || json?.result,
280+
data: document,
273281
docPermissions,
274282
docPreferences,
275283
globalSlug,

test/versions/config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ export default buildConfigWithDefaults({
2323
importMap: {
2424
baseDir: path.resolve(dirname),
2525
},
26+
// The autosave test uses this format in order to compare timestamps in the UI
27+
dateFormat: 'MMMM do yyyy, h:mm:ss a',
2628
},
2729
collections: [
2830
DisablePublish,

test/versions/e2e.spec.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,59 @@ describe('Versions', () => {
426426
await expect(drawer.locator('.id-label')).toBeVisible()
427427
})
428428

429+
test('collection - should update updatedAt', async () => {
430+
await page.goto(url.create)
431+
await page.waitForURL(`**/${url.create}`)
432+
433+
// fill out doc in english
434+
await page.locator('#field-title').fill('title')
435+
await page.locator('#field-description').fill('initial description')
436+
await saveDocAndAssert(page)
437+
438+
const updatedAtWrapper = await page.locator(
439+
'.doc-controls .doc-controls__content .doc-controls__list-item',
440+
{
441+
hasText: 'Last Modified',
442+
},
443+
)
444+
const initialUpdatedAt = await updatedAtWrapper.locator('.doc-controls__value').textContent()
445+
446+
// wait for 1 second so that the timestamp can be different
447+
await wait(1000)
448+
449+
await page.locator('#field-description').fill('changed description')
450+
await saveDocAndAssert(page)
451+
452+
const newUpdatedAt = await updatedAtWrapper.locator('.doc-controls__value').textContent()
453+
454+
expect(newUpdatedAt).not.toEqual(initialUpdatedAt)
455+
})
456+
457+
test('collection - should update updatedAt on autosave', async () => {
458+
await page.goto(autosaveURL.create)
459+
await page.locator('#field-title').fill('autosave title')
460+
await waitForAutoSaveToRunAndComplete(page)
461+
await expect(page.locator('#field-title')).toHaveValue('autosave title')
462+
463+
const updatedAtWrapper = await page.locator(
464+
'.doc-controls .doc-controls__content .doc-controls__list-item',
465+
{
466+
hasText: 'Last Modified',
467+
},
468+
)
469+
const initialUpdatedAt = await updatedAtWrapper.locator('.doc-controls__value').textContent()
470+
471+
// wait for 1 second so that the timestamp can be different
472+
await wait(1000)
473+
474+
await page.locator('#field-title').fill('autosave title updated')
475+
await waitForAutoSaveToRunAndComplete(page)
476+
477+
const newUpdatedAt = await updatedAtWrapper.locator('.doc-controls__value').textContent()
478+
479+
expect(newUpdatedAt).not.toEqual(initialUpdatedAt)
480+
})
481+
429482
test('global - should autosave', async () => {
430483
const url = new AdminUrlUtil(serverURL, autoSaveGlobalSlug)
431484
await page.goto(url.global(autoSaveGlobalSlug))

test/versions/int.spec.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,61 @@ describe('Versions', () => {
593593
expect(draftPost.title.es).toBe(spanishTitle)
594594
})
595595

596+
it('should have correct updatedAt timestamps when saving drafts', async () => {
597+
const created = await payload.create({
598+
collection: draftCollectionSlug,
599+
data: {
600+
description: 'desc',
601+
title: 'title',
602+
},
603+
draft: true,
604+
})
605+
606+
await wait(10)
607+
608+
const updated = await payload.update({
609+
id: created.id,
610+
collection: draftCollectionSlug,
611+
data: {
612+
title: 'updated title',
613+
},
614+
draft: true,
615+
})
616+
617+
const createdUpdatedAt = new Date(created.updatedAt)
618+
const updatedUpdatedAt = new Date(updated.updatedAt)
619+
620+
expect(Number(updatedUpdatedAt)).toBeGreaterThan(Number(createdUpdatedAt))
621+
})
622+
623+
it('should have correct updatedAt timestamps when saving drafts with autosave', async () => {
624+
const created = await payload.create({
625+
collection: draftCollectionSlug,
626+
data: {
627+
description: 'desc',
628+
title: 'title',
629+
},
630+
draft: true,
631+
})
632+
633+
await wait(10)
634+
635+
const updated = await payload.update({
636+
id: created.id,
637+
collection: draftCollectionSlug,
638+
data: {
639+
title: 'updated title',
640+
},
641+
draft: true,
642+
autosave: true,
643+
})
644+
645+
const createdUpdatedAt = new Date(created.updatedAt)
646+
const updatedUpdatedAt = new Date(updated.updatedAt)
647+
648+
expect(Number(updatedUpdatedAt)).toBeGreaterThan(Number(createdUpdatedAt))
649+
})
650+
596651
it('should validate when publishing with the draft arg', async () => {
597652
// no title (not valid for publishing)
598653
const doc = await payload.create({

0 commit comments

Comments
 (0)