Skip to content

Commit c019969

Browse files
authored
feat(ui): updates version status UI to be more informative (#6661)
## Description Improves the status pill in the version archive and version comparison views. - [X] I have read and understand the [CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md) document in this repository. ## Type of change - [X] New feature (non-breaking change which adds functionality) ## Checklist: - [X] Existing test suite passes locally with my changes
1 parent 43b971c commit c019969

Some content is hidden

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

44 files changed

+334
-147
lines changed

packages/next/src/views/Version/Default/SetStepNav.tsx

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,7 @@ export const SetStepNav: React.FC<{
1616
globalConfig?: ClientGlobalConfig
1717
globalSlug?: string
1818
id?: number | string
19-
mostRecentDoc: any
20-
}> = ({
21-
id,
22-
collectionConfig,
23-
collectionSlug,
24-
doc,
25-
fieldMap,
26-
globalConfig,
27-
globalSlug,
28-
mostRecentDoc,
29-
}) => {
19+
}> = ({ id, collectionConfig, collectionSlug, doc, fieldMap, globalConfig, globalSlug }) => {
3020
const config = useConfig()
3121
const { setStepNav } = useStepNav()
3222
const { i18n, t } = useTranslation()
@@ -45,26 +35,27 @@ export const SetStepNav: React.FC<{
4535

4636
const useAsTitle = collectionConfig?.admin?.useAsTitle || 'id'
4737
const pluralLabel = collectionConfig?.labels?.plural
38+
const formattedDoc = doc.version ? doc.version : doc
4839

49-
if (mostRecentDoc) {
40+
if (formattedDoc) {
5041
if (useAsTitle !== 'id') {
5142
const titleField = fieldMap.find((f) => {
5243
const { isFieldAffectingData } = f
5344
const fieldName = 'name' in f ? f.name : undefined
5445
return Boolean(isFieldAffectingData && fieldName === useAsTitle)
5546
})
5647

57-
if (titleField && mostRecentDoc[useAsTitle]) {
48+
if (titleField && formattedDoc[useAsTitle]) {
5849
if (titleField.localized) {
59-
docLabel = mostRecentDoc[useAsTitle]?.[locale.code]
50+
docLabel = formattedDoc[useAsTitle]?.[locale.code]
6051
} else {
61-
docLabel = mostRecentDoc[useAsTitle]
52+
docLabel = formattedDoc[useAsTitle]
6253
}
6354
} else {
6455
docLabel = `[${t('general:untitled')}]`
6556
}
6657
} else {
67-
docLabel = mostRecentDoc.id
58+
docLabel = doc.id
6859
}
6960
}
7061

@@ -114,7 +105,6 @@ export const SetStepNav: React.FC<{
114105
collectionSlug,
115106
globalSlug,
116107
doc,
117-
mostRecentDoc,
118108
id,
119109
locale,
120110
t,

packages/next/src/views/Version/Default/index.tsx

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import RenderFieldsToDiff from '../RenderFieldsToDiff/index.js'
2020
import Restore from '../Restore/index.js'
2121
import { SelectComparison } from '../SelectComparison/index.js'
2222
import { SelectLocales } from '../SelectLocales/index.js'
23-
import { mostRecentVersionOption } from '../shared.js'
2423
import { SetStepNav } from './SetStepNav.js'
2524
import './index.scss'
2625

@@ -30,9 +29,9 @@ export const DefaultVersionView: React.FC<DefaultVersionsViewProps> = ({
3029
doc,
3130
docPermissions,
3231
initialComparisonDoc,
32+
latestDraftVersion,
33+
latestPublishedVersion,
3334
localeOptions,
34-
mostRecentDoc,
35-
publishedDoc,
3635
versionID,
3736
}) => {
3837
const config = useConfig()
@@ -53,7 +52,8 @@ export const DefaultVersionView: React.FC<DefaultVersionsViewProps> = ({
5352
const [globalConfig] = useState(() => config.globals.find((global) => global.slug === globalSlug))
5453

5554
const [locales, setLocales] = useState<OptionObject[]>(localeOptions)
56-
const [compareValue, setCompareValue] = useState<CompareOption>(mostRecentVersionOption)
55+
56+
const [compareValue, setCompareValue] = useState<CompareOption>()
5757

5858
const {
5959
admin: { dateFormat },
@@ -62,34 +62,22 @@ export const DefaultVersionView: React.FC<DefaultVersionsViewProps> = ({
6262
serverURL,
6363
} = config
6464

65-
const formattedCreatedAt = doc?.createdAt
66-
? formatDate({ date: doc.createdAt, i18n, pattern: dateFormat })
65+
const versionCreatedAt = doc?.updatedAt
66+
? formatDate({ date: doc.updatedAt, i18n, pattern: dateFormat })
6767
: ''
6868

69-
const originalDocFetchURL = `${serverURL}${apiRoute}/${globalSlug ? 'globals/' : ''}${
70-
collectionSlug || globalSlug
71-
}${collectionSlug ? `/${id}` : ''}`
72-
7369
const compareBaseURL = `${serverURL}${apiRoute}/${globalSlug ? 'globals/' : ''}${
7470
collectionSlug || globalSlug
7571
}/versions`
7672

77-
const compareFetchURL =
78-
compareValue?.value === 'mostRecent' || compareValue?.value === 'published'
79-
? originalDocFetchURL
80-
: `${compareBaseURL}/${compareValue.value}`
73+
const compareFetchURL = compareValue?.value && `${compareBaseURL}/${compareValue.value}`
8174

8275
const [{ data: currentComparisonDoc }] = usePayloadAPI(compareFetchURL, {
8376
initialData: initialComparisonDoc,
8477
initialParams: { depth: 1, draft: 'true', locale: '*' },
8578
})
8679

87-
const comparison =
88-
compareValue?.value === 'mostRecent'
89-
? mostRecentDoc
90-
: compareValue?.value === 'published'
91-
? publishedDoc
92-
: currentComparisonDoc?.version // the `version` key is only present on `versions` documents
80+
const comparison = compareValue?.value && currentComparisonDoc?.version // the `version` key is only present on `versions` documents
9381

9482
const canUpdate = docPermissions?.update?.permission
9583

@@ -104,7 +92,6 @@ export const DefaultVersionView: React.FC<DefaultVersionsViewProps> = ({
10492
globalConfig={globalConfig}
10593
globalSlug={globalSlug}
10694
id={id}
107-
mostRecentDoc={mostRecentDoc}
10895
/>
10996
<Gutter className={`${baseClass}__wrap`}>
11097
<div className={`${baseClass}__header-wrap`}>
@@ -114,15 +101,15 @@ export const DefaultVersionView: React.FC<DefaultVersionsViewProps> = ({
114101
})}
115102
</p>
116103
<header className={`${baseClass}__header`}>
117-
<h2>{formattedCreatedAt}</h2>
104+
<h2>{versionCreatedAt}</h2>
118105
{canUpdate && (
119106
<Restore
120107
className={`${baseClass}__restore`}
121108
collectionSlug={collectionSlug}
122109
globalSlug={globalSlug}
123110
label={collectionConfig?.labels.singular || globalConfig?.label}
124111
originalDocID={id}
125-
versionDate={formattedCreatedAt}
112+
versionDate={versionCreatedAt}
126113
versionID={versionID}
127114
/>
128115
)}
@@ -131,9 +118,10 @@ export const DefaultVersionView: React.FC<DefaultVersionsViewProps> = ({
131118
<div className={`${baseClass}__controls`}>
132119
<SelectComparison
133120
baseURL={compareBaseURL}
121+
latestDraftVersion={latestDraftVersion}
122+
latestPublishedVersion={latestPublishedVersion}
134123
onChange={setCompareValue}
135124
parentID={id}
136-
publishedDoc={publishedDoc}
137125
value={compareValue}
138126
versionID={versionID}
139127
/>
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { CollectionPermission, Document, GlobalPermission, OptionObject } from 'payload'
22

33
export type CompareOption = {
4-
label: string
4+
label: React.ReactNode | string
55
options?: CompareOption[]
66
relationTo?: string
77
value: string
@@ -11,8 +11,8 @@ export type DefaultVersionsViewProps = {
1111
doc: Document
1212
docPermissions: CollectionPermission | GlobalPermission
1313
initialComparisonDoc: Document
14+
latestDraftVersion?: string
15+
latestPublishedVersion?: string
1416
localeOptions: OptionObject[]
15-
mostRecentDoc: Document
16-
publishedDoc: Document
1717
versionID?: string
1818
}

packages/next/src/views/Version/SelectComparison/index.tsx

Lines changed: 64 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,44 @@
11
'use client'
2+
23
import type { PaginatedDocs, Where } from 'payload'
34

4-
import {
5-
ReactSelect,
6-
fieldBaseClass,
7-
useConfig,
8-
useDocumentInfo,
9-
useTranslation,
10-
} from '@payloadcms/ui'
5+
import { ReactSelect, fieldBaseClass, useConfig, useTranslation } from '@payloadcms/ui'
116
import { formatDate } from '@payloadcms/ui/shared'
127
import * as qs from 'qs-esm'
138
import React, { useCallback, useEffect, useState } from 'react'
149

1510
import type { Props } from './types.js'
1611

17-
import { mostRecentVersionOption, publishedVersionOption } from '../shared.js'
12+
import { renderPill } from '../../Versions/cells/AutosaveCell/index.js'
1813
import './index.scss'
1914

2015
const baseClass = 'compare-version'
2116

2217
const maxResultsPerRequest = 10
2318

24-
const baseOptions = [mostRecentVersionOption]
19+
const baseOptions = []
2520

2621
export const SelectComparison: React.FC<Props> = (props) => {
27-
const { baseURL, onChange, parentID, publishedDoc, value, versionID } = props
22+
const {
23+
baseURL,
24+
latestDraftVersion,
25+
latestPublishedVersion,
26+
onChange,
27+
parentID,
28+
value,
29+
versionID,
30+
} = props
2831

2932
const {
3033
admin: { dateFormat },
3134
} = useConfig()
3235

33-
const { docConfig } = useDocumentInfo()
34-
const [options, setOptions] = useState(baseOptions)
36+
const [options, setOptions] = useState<
37+
{
38+
label: React.ReactNode | string
39+
value: string
40+
}[]
41+
>(baseOptions)
3542
const [lastLoadedPage, setLastLoadedPage] = useState(1)
3643
const [errorLoading, setErrorLoading] = useState('')
3744
const { i18n, t } = useTranslation()
@@ -58,14 +65,6 @@ export const SelectComparison: React.FC<Props> = (props) => {
5865
},
5966
}
6067

61-
if (docConfig.versions?.drafts) {
62-
query.where.and.push({
63-
latest: {
64-
not_equals: true,
65-
},
66-
})
67-
}
68-
6968
if (parentID) {
7069
query.where.and.push({
7170
parent: {
@@ -85,14 +84,41 @@ export const SelectComparison: React.FC<Props> = (props) => {
8584

8685
if (response.ok) {
8786
const data: PaginatedDocs = await response.json()
87+
8888
if (data.docs.length > 0) {
89-
setOptions((existingOptions) => [
90-
...existingOptions,
91-
...data.docs.map((doc) => ({
92-
label: formatDate({ date: doc.updatedAt, i18n, pattern: dateFormat }),
89+
const versionInfo = {
90+
draft: {
91+
currentLabel: t('version:currentDraft'),
92+
latestVersion: latestDraftVersion,
93+
pillStyle: undefined,
94+
previousLabel: t('version:draft'),
95+
},
96+
published: {
97+
currentLabel: t('version:currentPublishedVersion'),
98+
latestVersion: latestPublishedVersion,
99+
pillStyle: 'success',
100+
previousLabel: t('version:previouslyPublished'),
101+
},
102+
}
103+
104+
const additionalOptions = data.docs.map((doc) => {
105+
const status = doc.version._status
106+
const { currentLabel, latestVersion, pillStyle, previousLabel } =
107+
versionInfo[status] || {}
108+
109+
return {
110+
label: (
111+
<div>
112+
{formatDate({ date: doc.updatedAt, i18n, pattern: dateFormat })}
113+
&nbsp;&nbsp;
114+
{renderPill(doc, latestVersion, currentLabel, previousLabel, pillStyle)}
115+
</div>
116+
),
93117
value: doc.id,
94-
})),
95-
])
118+
}
119+
})
120+
121+
setOptions((existingOptions) => [...existingOptions, ...additionalOptions])
96122

97123
if (!data.hasNextPage) {
98124
loadedAllOptionsRef.current = true
@@ -103,13 +129,23 @@ export const SelectComparison: React.FC<Props> = (props) => {
103129
setErrorLoading(t('error:unspecific'))
104130
}
105131
},
106-
[dateFormat, baseURL, parentID, versionID, t, i18n, docConfig.versions?.drafts],
132+
[dateFormat, baseURL, parentID, versionID, t, i18n, latestDraftVersion, latestPublishedVersion],
107133
)
108134

109135
useEffect(() => {
110136
void getResults({ lastLoadedPage: 1 })
111137
}, [getResults])
112138

139+
const filteredOptions = options.filter(
140+
(option, index, self) => self.findIndex((t) => t.value === option.value) === index,
141+
)
142+
143+
useEffect(() => {
144+
if (filteredOptions.length > 0 && !value) {
145+
onChange(filteredOptions[0])
146+
}
147+
}, [filteredOptions, value, onChange])
148+
113149
return (
114150
<div
115151
className={[fieldBaseClass, baseClass, errorLoading && 'error-loading']
@@ -125,10 +161,7 @@ export const SelectComparison: React.FC<Props> = (props) => {
125161
onMenuScrollToBottom={() => {
126162
void getResults({ lastLoadedPage: lastLoadedPage + 1 })
127163
}}
128-
options={[
129-
...(publishedDoc?._status === 'published' ? [publishedVersionOption] : []),
130-
...options,
131-
]}
164+
options={filteredOptions}
132165
placeholder={t('version:selectVersionToCompare')}
133166
value={value}
134167
/>

packages/next/src/views/Version/SelectComparison/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ import type { CompareOption } from '../Default/types.js'
44

55
export type Props = {
66
baseURL: string
7+
latestDraftVersion?: string
8+
latestPublishedVersion?: string
79
onChange: (val: CompareOption) => void
810
parentID?: number | string
9-
publishedDoc: any
1011
value: CompareOption
1112
versionID: string
1213
}

0 commit comments

Comments
 (0)