Skip to content

Commit 565680d

Browse files
authored
fix(next, ui): show status "changed" in List View (#14555)
### Description This PR displays `draft (has published version)` in the List View, similar to how the Document View displays `changed`. The reason we don't display `changed` in the same way as in the Document View is that the change here is only in the UI. In the database, the only two options are `draft` and `published`. Therefore, it would be confusing since the "changed" option wouldn't appear in the filters. In version 4, we can reconsider two options: 1. Add `changed` to the database options enum. This is very unlikely since that breaking change would be extremely aggressive. 2. Add a new `changed` column to the database with a boolean value. This is the most plausible option. ### How: The solution addresses a performance concern discussed internally by using one query for all documents on the page rather than N queries per row, and transferring only parent IDs. I added a TODO comment documenting that a future `findDistinctVersions()` API could optimize this further. ### After <img width="1088" height="436" alt="image" src="https://github.com/user-attachments/assets/1952e74e-783c-4c23-af52-deeec8e8134b" /> Fixes #14449
1 parent d60ea6e commit 565680d

File tree

50 files changed

+245
-9
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+245
-9
lines changed
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import type { PaginatedDocs, PayloadRequest, SanitizedCollectionConfig } from 'payload'
2+
3+
/**
4+
* Enriches list view documents with correct draft status display.
5+
* When draft=true is used in the query, Payload returns the latest draft version if it exists.
6+
* This function checks if draft documents also have a published version to determine "changed" status.
7+
*
8+
* Performance: Uses a single query to find all documents with "changed" status instead of N queries.
9+
*/
10+
export async function enrichDocsWithVersionStatus({
11+
collectionConfig,
12+
data,
13+
req,
14+
}: {
15+
collectionConfig: SanitizedCollectionConfig
16+
data: PaginatedDocs
17+
req: PayloadRequest
18+
}): Promise<PaginatedDocs> {
19+
const draftsEnabled = collectionConfig?.versions?.drafts
20+
21+
if (!draftsEnabled || !data?.docs?.length) {
22+
return data
23+
}
24+
25+
// Find all draft documents
26+
// When querying with draft:true, we get the latest draft if it exists
27+
// We need to check if these drafts have a published version
28+
const draftDocs = data.docs.filter((doc) => doc._status === 'draft')
29+
30+
if (draftDocs.length === 0) {
31+
return data
32+
}
33+
34+
const draftDocIds = draftDocs.map((doc) => doc.id).filter(Boolean)
35+
36+
if (draftDocIds.length === 0) {
37+
return data
38+
}
39+
40+
// OPTIMIZATION: Single query to find all document IDs that have BOTH:
41+
// 1. A draft version (latest=true, _status='draft')
42+
// 2. A published version (_status='published')
43+
// These are the documents with "changed" status
44+
try {
45+
// TODO: This could be more efficient with a findDistinctVersions() API:
46+
// const { values } = await req.payload.findDistinctVersions({
47+
// collection: collectionConfig.slug,
48+
// field: 'parent',
49+
// where: {
50+
// and: [
51+
// { parent: { in: draftDocIds } },
52+
// { 'version._status': { equals: 'published' } },
53+
// ],
54+
// },
55+
// })
56+
// const hasPublishedVersionSet = new Set(values)
57+
//
58+
// For now, we query all published versions but only select the 'parent' field
59+
// to minimize data transfer, then deduplicate with a Set
60+
const publishedVersions = await req.payload.findVersions({
61+
collection: collectionConfig.slug,
62+
depth: 0,
63+
limit: 0,
64+
pagination: false,
65+
select: {
66+
parent: true,
67+
},
68+
where: {
69+
and: [
70+
{
71+
parent: {
72+
in: draftDocIds,
73+
},
74+
},
75+
{
76+
'version._status': {
77+
equals: 'published',
78+
},
79+
},
80+
],
81+
},
82+
})
83+
84+
// Create a Set of document IDs that have published versions
85+
const hasPublishedVersionSet = new Set(
86+
publishedVersions.docs.map((version) => version.parent).filter(Boolean),
87+
)
88+
89+
// Enrich documents with display status
90+
const enrichedDocs = data.docs.map((doc) => {
91+
// If it's a draft and has a published version, show "changed"
92+
if (doc._status === 'draft' && hasPublishedVersionSet.has(doc.id)) {
93+
return {
94+
...doc,
95+
_displayStatus: 'changed' as const,
96+
}
97+
}
98+
99+
return {
100+
...doc,
101+
_displayStatus: doc._status as 'draft' | 'published',
102+
}
103+
})
104+
105+
return {
106+
...data,
107+
docs: enrichedDocs,
108+
}
109+
} catch (error) {
110+
// If there's an error querying versions, just return the original data
111+
req.payload.logger.error({
112+
err: error,
113+
msg: `Error checking version status for collection ${collectionConfig.slug}`,
114+
})
115+
return data
116+
}
117+
}

packages/next/src/views/List/index.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
import React, { Fragment } from 'react'
2929

3030
import { getDocumentPermissions } from '../Document/getDocumentPermissions.js'
31+
import { enrichDocsWithVersionStatus } from './enrichDocsWithVersionStatus.js'
3132
import { handleGroupBy } from './handleGroupBy.js'
3233
import { renderListViewSlots } from './renderListViewSlots.js'
3334
import { resolveAllFilterOptions } from './resolveAllFilterOptions.js'
@@ -269,6 +270,13 @@ export const renderListView = async (
269270
viewType,
270271
where: whereWithMergedSearch,
271272
}))
273+
274+
// Enrich documents with correct display status for drafts
275+
data = await enrichDocsWithVersionStatus({
276+
collectionConfig,
277+
data,
278+
req,
279+
})
272280
} else {
273281
data = await req.payload.find({
274282
collection: collectionSlug,
@@ -287,6 +295,13 @@ export const renderListView = async (
287295
user,
288296
where: whereWithMergedSearch,
289297
})
298+
299+
// Enrich documents with correct display status for drafts
300+
data = await enrichDocsWithVersionStatus({
301+
collectionConfig,
302+
data,
303+
req,
304+
})
290305
;({ columnState, Table } = renderTable({
291306
clientCollectionConfig,
292307
collectionConfig,

packages/translations/src/clientKeys.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,7 @@ export const clientTranslationKeys = createClientTranslationKeys([
474474
'version:currentPublishedVersion',
475475
'version:currentlyPublished',
476476
'version:draft',
477+
'version:draftHasPublishedVersion',
477478
'version:draftSavedSuccessfully',
478479
'version:lastSavedAgo',
479480
'version:modifiedOnly',

packages/translations/src/languages/ar.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,7 @@ export const arTranslations: DefaultTranslationsObject = {
553553
currentlyViewing: 'تمت المشاهدة حاليا',
554554
currentPublishedVersion: 'النسخة المنشورة الحالية',
555555
draft: 'مسودّة',
556+
draftHasPublishedVersion: 'مسودة (لديها نسخة منشورة)',
556557
draftSavedSuccessfully: 'تمّ حفظ المسودّة بنجاح.',
557558
lastSavedAgo: 'تم الحفظ آخر مرة قبل {{distance}}',
558559
modifiedOnly: 'تم التعديل فقط',

packages/translations/src/languages/az.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,7 @@ export const azTranslations: DefaultTranslationsObject = {
571571
currentlyViewing: 'Hazırda baxılır',
572572
currentPublishedVersion: 'Hazırki Nəşr Versiyası',
573573
draft: 'Qaralama',
574+
draftHasPublishedVersion: 'Qaralamalar (nəşr olunmuş versiyası var)',
574575
draftSavedSuccessfully: 'Qaralama uğurla yadda saxlandı.',
575576
lastSavedAgo: '{{distance}} əvvəl son yadda saxlanıldı',
576577
modifiedOnly: 'Yalnızca dəyişdirilmişdir',

packages/translations/src/languages/bg.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,7 @@ export const bgTranslations: DefaultTranslationsObject = {
567567
currentlyViewing: 'В момента преглеждате',
568568
currentPublishedVersion: 'Текуща публикувана версия',
569569
draft: 'Чернова',
570+
draftHasPublishedVersion: 'Чернова (има публикувана версия)',
570571
draftSavedSuccessfully: 'Чернова запазена успешно.',
571572
lastSavedAgo: 'последно запазено преди {{distance}}',
572573
modifiedOnly: 'Само променени',

packages/translations/src/languages/bnBd.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,7 @@ export const bnBdTranslations: DefaultTranslationsObject = {
573573
currentlyViewing: 'বর্তমানে দেখছেন',
574574
currentPublishedVersion: 'বর্তমান প্রকাশিত সংস্করণ',
575575
draft: 'খসড়া',
576+
draftHasPublishedVersion: 'খসড়া (প্রকাশিত সংস্করণ রয়েছে)',
576577
draftSavedSuccessfully: 'খসড়া সফলভাবে সংরক্ষিত হয়েছে।',
577578
lastSavedAgo: 'সর্বশেষ সংরক্ষণ করা হয়েছে {{distance}} আগে',
578579
modifiedOnly: 'শুধুমাত্র পরিবর্তিত',

packages/translations/src/languages/bnIn.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,7 @@ export const bnInTranslations: DefaultTranslationsObject = {
572572
currentlyViewing: 'বর্তমানে দেখছেন',
573573
currentPublishedVersion: 'বর্তমান প্রকাশিত সংস্করণ',
574574
draft: 'খসড়া',
575+
draftHasPublishedVersion: 'খসড়া (প্রকাশিত সংস্করণ রয়েছে)',
575576
draftSavedSuccessfully: 'খসড়া সফলভাবে সংরক্ষিত হয়েছে।',
576577
lastSavedAgo: 'সর্বশেষ সংরক্ষণ করা হয়েছে {{distance}} আগে',
577578
modifiedOnly: 'শুধুমাত্র পরিবর্তিত',

packages/translations/src/languages/ca.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,7 @@ export const caTranslations: DefaultTranslationsObject = {
572572
currentlyViewing: 'Actualment veient',
573573
currentPublishedVersion: 'Versió publicada actual',
574574
draft: 'Borrador',
575+
draftHasPublishedVersion: 'Esborrany (té versió publicada)',
575576
draftSavedSuccessfully: 'Borrador desat amb èxit.',
576577
lastSavedAgo: 'Últim desament fa {{distance}}',
577578
modifiedOnly: 'Només modificat',

packages/translations/src/languages/cs.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,7 @@ export const csTranslations: DefaultTranslationsObject = {
565565
currentlyViewing: 'Aktuálně prohlížíte',
566566
currentPublishedVersion: 'Aktuálně publikovaná verze',
567567
draft: 'Koncept',
568+
draftHasPublishedVersion: 'Koncept (má publikovanou verzi)',
568569
draftSavedSuccessfully: 'Koncept úspěšně uložen.',
569570
lastSavedAgo: 'Naposledy uloženo před {{distance}}',
570571
modifiedOnly: 'Pouze upraveno',

0 commit comments

Comments
 (0)