diff --git a/src/admin/components/views/Revision/RenderFieldsToDiff/Label/index.scss b/src/admin/components/views/Revision/RenderFieldsToDiff/Label/index.scss new file mode 100644 index 0000000000..8291575834 --- /dev/null +++ b/src/admin/components/views/Revision/RenderFieldsToDiff/Label/index.scss @@ -0,0 +1,5 @@ +@import '../../../../../scss/styles.scss'; + +.field-diff-label { + margin-bottom: base(.25); +} diff --git a/src/admin/components/views/Revision/RenderFieldsToDiff/Label/index.tsx b/src/admin/components/views/Revision/RenderFieldsToDiff/Label/index.tsx new file mode 100644 index 0000000000..c4572488e2 --- /dev/null +++ b/src/admin/components/views/Revision/RenderFieldsToDiff/Label/index.tsx @@ -0,0 +1,13 @@ +import React from 'react'; + +import './index.scss'; + +const baseClass = 'field-diff-label'; + +const Label: React.FC = ({ children }) => ( +
+ {children} +
+); + +export default Label; diff --git a/src/admin/components/views/Revision/RenderFieldsToDiff/fields/Iterable/index.tsx b/src/admin/components/views/Revision/RenderFieldsToDiff/fields/Iterable/index.tsx new file mode 100644 index 0000000000..0d0c030ef0 --- /dev/null +++ b/src/admin/components/views/Revision/RenderFieldsToDiff/fields/Iterable/index.tsx @@ -0,0 +1,21 @@ +import React from 'react'; + +const baseClass = 'iterable-diff'; + +type Props = { + revision: string + comparison: string +} + +const Iterable: React.FC = ({ revision, comparison }) => ( +
+
+ {revision} +
+
+ {comparison} +
+
+); + +export default Iterable; diff --git a/src/admin/components/views/Revision/RenderFieldsToDiff/fields/Nested/index.tsx b/src/admin/components/views/Revision/RenderFieldsToDiff/fields/Nested/index.tsx new file mode 100644 index 0000000000..2cc777aff4 --- /dev/null +++ b/src/admin/components/views/Revision/RenderFieldsToDiff/fields/Nested/index.tsx @@ -0,0 +1,21 @@ +import React from 'react'; + +const baseClass = 'nested-diff'; + +type Props = { + revision: string + comparison: string +} + +const Nested: React.FC = ({ revision, comparison }) => ( +
+
+ {revision} +
+
+ {comparison} +
+
+); + +export default Nested; diff --git a/src/admin/components/views/Revision/RenderFieldsToDiff/fields/Text/index.scss b/src/admin/components/views/Revision/RenderFieldsToDiff/fields/Text/index.scss new file mode 100644 index 0000000000..10f858f3af --- /dev/null +++ b/src/admin/components/views/Revision/RenderFieldsToDiff/fields/Text/index.scss @@ -0,0 +1,10 @@ +@import '../../../../../../scss/styles.scss'; + +.text-diff { + &__locale-label { + margin-right: base(.25); + background: $color-background-gray; + padding: base(.25); + border-radius: $style-radius-m; + } +} diff --git a/src/admin/components/views/Revision/RenderFieldsToDiff/fields/Text/index.tsx b/src/admin/components/views/Revision/RenderFieldsToDiff/fields/Text/index.tsx new file mode 100644 index 0000000000..f0b5a7863f --- /dev/null +++ b/src/admin/components/views/Revision/RenderFieldsToDiff/fields/Text/index.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import ReactDiffViewer from 'react-diff-viewer'; +import { Props } from '../types'; +import Label from '../../Label'; + +import './index.scss'; + +const baseClass = 'text-diff'; + +const Text: React.FC = ({ field, locale, revision, comparison }) => { + return ( +
+ + +
+ ); + + return null; +}; + +export default Text; diff --git a/src/admin/components/views/Revision/RenderFieldsToDiff/fields/index.tsx b/src/admin/components/views/Revision/RenderFieldsToDiff/fields/index.tsx new file mode 100644 index 0000000000..ab84291cc8 --- /dev/null +++ b/src/admin/components/views/Revision/RenderFieldsToDiff/fields/index.tsx @@ -0,0 +1,22 @@ +import Text from './Text'; +import Iterable from './Iterable'; +import Nested from './Nested'; + +export default { + text: Text, + textarea: Text, + number: Text, + email: Text, + code: Text, + // group: Nested, + // row: Nested, + // array: Iterable, + blocks: Iterable, + checkbox: Text, + date: Text, + radio: Text, + select: Text, + relationship: Text, + upload: Text, + point: Text, +}; diff --git a/src/admin/components/views/Revision/RenderFieldsToDiff/fields/types.ts b/src/admin/components/views/Revision/RenderFieldsToDiff/fields/types.ts new file mode 100644 index 0000000000..b1377bdd57 --- /dev/null +++ b/src/admin/components/views/Revision/RenderFieldsToDiff/fields/types.ts @@ -0,0 +1,8 @@ +import { Field } from '../../../../../../fields/config/types'; + +export type Props = { + revision: string + comparison: string + field: Field + locale?: string +} diff --git a/src/admin/components/views/Revision/RenderFieldsToDiff/index.scss b/src/admin/components/views/Revision/RenderFieldsToDiff/index.scss new file mode 100644 index 0000000000..fb0b9f42b0 --- /dev/null +++ b/src/admin/components/views/Revision/RenderFieldsToDiff/index.scss @@ -0,0 +1,11 @@ +@import '../../../../scss/styles.scss'; + +.render-field-diffs { + &__field { + margin-bottom: $baseline; + } + + &__locale { + margin-bottom: base(.5); + } +} diff --git a/src/admin/components/views/Revision/RenderFieldsToDiff/index.tsx b/src/admin/components/views/Revision/RenderFieldsToDiff/index.tsx new file mode 100644 index 0000000000..f1021fa3fe --- /dev/null +++ b/src/admin/components/views/Revision/RenderFieldsToDiff/index.tsx @@ -0,0 +1,72 @@ +import React from 'react'; +import { Props } from './types'; +import fieldComponents from './fields'; +import { fieldAffectsData } from '../../../../../fields/config/types'; +import Label from './Label'; + +import './index.scss'; + +const baseClass = 'render-field-diffs'; + +const RenderFieldsToDiff: React.FC = ({ fields, fieldPermissions, revision, comparison, locales }) => ( +
+ {fields.map((field, i) => { + const Component = fieldComponents[field.type]; + if (Component) { + if (fieldAffectsData(field)) { + const revisionValue = revision[field.name]; + const comparisonValue = comparison?.[field.name]; + const hasPermission = fieldPermissions?.[field.name]?.read?.permission; + + if (!hasPermission) return null; + + if (field.localized) { + return ( +
+ {locales.map((locale) => { + const revisionLocaleValue = revisionValue?.[locale]; + const comparisonLocaleValue = comparisonValue?.[locale]; + return ( +
+
+ +
+
+ ); + })} +
+ ); + } + + return ( +
+ +
+ ); + } + } + + return null; + })} +
+); + +export default RenderFieldsToDiff; diff --git a/src/admin/components/views/Revision/RenderFieldsToDiff/types.ts b/src/admin/components/views/Revision/RenderFieldsToDiff/types.ts new file mode 100644 index 0000000000..c56084a456 --- /dev/null +++ b/src/admin/components/views/Revision/RenderFieldsToDiff/types.ts @@ -0,0 +1,10 @@ +import { FieldPermissions } from '../../../../../auth'; +import { Field } from '../../../../../fields/config/types'; + +export type Props = { + fields: Field[] + fieldPermissions: Record + revision: Record + comparison: Record + locales: string[] +} diff --git a/src/admin/components/views/Revision/index.scss b/src/admin/components/views/Revision/index.scss index 04d03cc2dc..861d91b1cf 100644 --- a/src/admin/components/views/Revision/index.scss +++ b/src/admin/components/views/Revision/index.scss @@ -30,7 +30,7 @@ > * { margin-left: base(.5); margin-right: base(.5); - width: 50%; + flex-basis: 100%; } } @@ -53,7 +53,11 @@ &__controls { display: block; - margin: 0 $baseline; + margin: 0 base(.5); + + > * { + margin-bottom: base(.5); + } } &__restore { diff --git a/src/admin/components/views/Revision/index.tsx b/src/admin/components/views/Revision/index.tsx index d75481231a..4c95176fbf 100644 --- a/src/admin/components/views/Revision/index.tsx +++ b/src/admin/components/views/Revision/index.tsx @@ -1,4 +1,4 @@ -import { useConfig } from '@payloadcms/config-provider'; +import { useAuth, useConfig } from '@payloadcms/config-provider'; import React, { useEffect, useState } from 'react'; import { useRouteMatch } from 'react-router-dom'; import format from 'date-fns/format'; @@ -12,9 +12,12 @@ import { LocaleOption, CompareOption, Props } from './types'; import CompareRevision from './Compare'; import { publishedVersionOption } from './shared'; import Restore from './Restore'; +import SelectLocales from './SelectLocales'; +import RenderFieldsToDiff from './RenderFieldsToDiff'; import './index.scss'; -import SelectLocales from './SelectLocales'; +import { Field } from '../../../../fields/config/types'; +import { FieldPermissions } from '../../../../auth'; const baseClass = 'view-revision'; @@ -25,10 +28,13 @@ const ViewRevision: React.FC = ({ collection, global }) => { const [compareValue, setCompareValue] = useState(publishedVersionOption); const [localeOptions] = useState(() => (localization?.locales ? localization.locales.map((locale) => ({ label: locale, value: locale })) : [])); const [locales, setLocales] = useState(localeOptions); + const { permissions } = useAuth(); let originalDocFetchURL: string; let revisionFetchURL: string; let entityLabel: string; + let fields: Field[]; + let fieldPermissions: Record; let compareBaseURL: string; let slug: string; let parentID: string; @@ -40,6 +46,8 @@ const ViewRevision: React.FC = ({ collection, global }) => { compareBaseURL = `${serverURL}${api}/${slug}/revisions`; entityLabel = collection.labels.singular; parentID = id; + fields = collection.fields; + fieldPermissions = permissions.collections[collection.slug].fields; } if (global) { @@ -48,14 +56,16 @@ const ViewRevision: React.FC = ({ collection, global }) => { revisionFetchURL = `${serverURL}${api}/globals/${slug}/revisions/${revisionID}`; compareBaseURL = `${serverURL}${api}/globals/${slug}/revisions`; entityLabel = global.label; + fields = global.fields; + fieldPermissions = permissions.globals[global.slug].fields; } const useAsTitle = collection?.admin?.useAsTitle || 'id'; - const compareFetchURL = compareValue?.value && compareValue?.value !== 'published' ? `${compareBaseURL}/${compareValue.value}` : ''; + const compareFetchURL = compareValue?.value === 'published' ? originalDocFetchURL : `${compareBaseURL}/${compareValue.value}`; - const [{ data: doc, isLoading }] = usePayloadAPI(revisionFetchURL); + const [{ data: doc, isLoading }] = usePayloadAPI(revisionFetchURL, { initialParams: { locale: '*' } }); const [{ data: originalDoc }] = usePayloadAPI(originalDocFetchURL); - const [{ data: compareDoc }] = usePayloadAPI(compareFetchURL); + const [{ data: compareDoc }] = usePayloadAPI(compareFetchURL, { initialParams: { locale: '*' } }); useEffect(() => { let nav: StepNavItem[] = []; @@ -133,25 +143,31 @@ const ViewRevision: React.FC = ({ collection, global }) => { />
+ {localization && ( + + )} -
{isLoading && ( )} {doc?.revision && ( - - hello - + locale.value)} + fields={fields} + fieldPermissions={fieldPermissions} + revision={doc?.revision} + comparison={compareValue?.value === 'published' ? compareDoc : compareDoc?.revision} + /> )}