feat: allows selection of revisions in certain locales to compare

This commit is contained in:
James
2021-12-13 14:09:13 -05:00
parent 740d6b15e5
commit f57223024a
14 changed files with 233 additions and 19 deletions

View File

@@ -10,6 +10,7 @@ div.react-select {
.rs__value-container {
padding: base(.25) 0;
min-height: base(1.5);
> * {
margin-top: 0;
@@ -20,6 +21,8 @@ div.react-select {
&--is-multi {
margin-left: - base(.25);
padding-top: 0;
padding-bottom: 0;
}
}

View File

@@ -8,4 +8,8 @@
background-color: $color-red;
color: white;
}
&__label {
margin-bottom: base(.25);
}
}

View File

@@ -1,9 +1,11 @@
import React, { useState, useCallback, useEffect } from 'react';
import qs from 'qs';
import { useConfig } from '@payloadcms/config-provider';
import format from 'date-fns/format';
import { Props } from './types';
import ReactSelect from '../../../elements/ReactSelect';
import { PaginatedDocs } from '../../../../../mongoose/types';
import { publishedVersionOption } from '../shared';
import './index.scss';
@@ -11,8 +13,12 @@ const baseClass = 'compare-revision';
const maxResultsPerRequest = 10;
const baseOptions = [
publishedVersionOption,
];
const CompareRevision: React.FC<Props> = (props) => {
const { onChange, value, baseURL } = props;
const { onChange, value, baseURL, parentID } = props;
const {
admin: {
@@ -20,14 +26,30 @@ const CompareRevision: React.FC<Props> = (props) => {
},
} = useConfig();
const [options, setOptions] = useState([]);
const [options, setOptions] = useState(baseOptions);
const [lastLoadedPage, setLastLoadedPage] = useState(1);
const [errorLoading, setErrorLoading] = useState('');
const getResults = useCallback(async ({
lastLoadedPage: lastLoadedPageArg,
} = {}) => {
const response = await fetch(`${baseURL}?limit=${maxResultsPerRequest}&page=${lastLoadedPageArg}&depth=0`);
const query = {
limit: maxResultsPerRequest,
page: lastLoadedPageArg,
depth: 0,
where: undefined,
};
if (parentID) {
query.where = {
parent: {
equals: parentID,
},
};
}
const search = qs.stringify(query);
const response = await fetch(`${baseURL}?${search}`);
if (response.ok) {
const data: PaginatedDocs<any> = await response.json();
@@ -44,7 +66,7 @@ const CompareRevision: React.FC<Props> = (props) => {
} else {
setErrorLoading('An error has occurred.');
}
}, [dateFormat, baseURL]);
}, [dateFormat, baseURL, parentID]);
const classes = [
'field-type',
@@ -58,6 +80,9 @@ const CompareRevision: React.FC<Props> = (props) => {
return (
<div className={classes}>
<div className={`${baseClass}__label`}>
Compare revision against:
</div>
{!errorLoading && (
<ReactSelect
isSearchable={false}

View File

@@ -1,18 +1,14 @@
import { SanitizedCollectionConfig } from '../../../../../collections/config/types';
import { PaginatedDocs } from '../../../../../mongoose/types';
import { CompareOption } from '../types';
export type Props = {
onChange: (val: unknown) => void,
value: Option,
value: CompareOption,
baseURL: string
parentID?: string
}
export type Option = {
label: string
value: string
relationTo?: string
options?: Option[]
}
type CLEAR = {
type: 'CLEAR'

View File

@@ -0,0 +1,20 @@
@import '../../../../scss/styles.scss';
.restore-revision {
cursor: pointer;
&__modal {
@include blur-bg;
display: flex;
align-items: center;
height: 100%;
&__toggle {
@extend %btn-reset;
}
.btn {
margin-right: $baseline;
}
}
}

View File

@@ -0,0 +1,54 @@
import React, { Fragment, useCallback, useState } from 'react';
import { Modal, useModal } from '@faceless-ui/modal';
import { Button, MinimalTemplate, Pill } from '../../..';
import { Props } from './types';
import './index.scss';
const baseClass = 'restore-revision';
const modalSlug = 'restore-revision';
const Restore: React.FC<Props> = ({ collection, global, className }) => {
const { toggle } = useModal();
const [processing, setProcessing] = useState(false);
const handleRestore = useCallback(() => {
console.log(collection, global);
}, [collection, global]);
return (
<Fragment>
<Pill
onClick={() => toggle(modalSlug)}
className={[baseClass, className].filter(Boolean).join(' ')}
>
Restore this revision
</Pill>
<Modal
slug={modalSlug}
className={`${baseClass}__modal`}
>
<MinimalTemplate>
<h1>Confirm revision restoration</h1>
<p>
You are about to restore this document. Are you sure?
</p>
<Button
buttonStyle="secondary"
type="button"
onClick={processing ? undefined : () => toggle(modalSlug)}
>
Cancel
</Button>
<Button
onClick={processing ? undefined : handleRestore}
>
{processing ? 'Restoring...' : 'Confirm'}
</Button>
</MinimalTemplate>
</Modal>
</Fragment>
);
};
export default Restore;

View File

@@ -0,0 +1,8 @@
import { SanitizedCollectionConfig } from '../../../../../collections/config/types';
import { SanitizedGlobalConfig } from '../../../../../globals/config/types';
export type Props = {
collection?: SanitizedCollectionConfig
global?: SanitizedGlobalConfig
className?: string
}

View File

@@ -0,0 +1,9 @@
@import '../../../../scss/styles.scss';
.select-revision-locales {
flex-grow: 1;
&__label {
margin-bottom: base(.25);
}
}

View File

@@ -0,0 +1,24 @@
import React from 'react';
import ReactSelect from '../../../elements/ReactSelect';
import { Props } from './types';
import './index.scss';
const baseClass = 'select-revision-locales';
const SelectLocales: React.FC<Props> = ({ onChange, value, options }) => (
<div className={baseClass}>
<div className={`${baseClass}__label`}>
Show locales:
</div>
<ReactSelect
isMulti
placeholder="Select locales to display"
onChange={onChange}
value={value}
options={options}
/>
</div>
);
export default SelectLocales;

View File

@@ -0,0 +1,7 @@
import { LocaleOption } from '../types';
export type Props = {
onChange: (options: LocaleOption[]) => void
value: LocaleOption[]
options: LocaleOption[]
}

View File

@@ -12,10 +12,30 @@
&__header {
margin-bottom: $baseline;
display: flex;
align-items: center;
flex-wrap: wrap;
h2 {
margin: 0;
}
}
&__intro {
margin-bottom: base(.5);
&__controls {
display: flex;
margin-bottom: $baseline;
margin-left: base(-.5);
margin-right: base(-.5);
> * {
margin-left: base(.5);
margin-right: base(.5);
width: 50%;
}
}
&__restore {
margin: 0 0 0 $baseline;
}
@include mid-break {
@@ -24,9 +44,20 @@
margin-right: 0;
}
&__intro,
&__header {
padding-left: $baseline;
padding-right: $baseline;
display: block;
}
&__controls {
display: block;
margin: 0 $baseline;
}
&__restore {
margin: base(.5) 0 0 0;
}
}
}

View File

@@ -8,25 +8,30 @@ import Loading from '../../elements/Loading';
import { useStepNav } from '../../elements/StepNav';
import { StepNavItem } from '../../elements/StepNav/types';
import Meta from '../../utilities/Meta';
import { Props } from './types';
import { LocaleOption, CompareOption, Props } from './types';
import CompareRevision from './Compare';
import { Option } from './Compare/types';
import { publishedVersionOption } from './shared';
import Restore from './Restore';
import './index.scss';
import SelectLocales from './SelectLocales';
const baseClass = 'view-revision';
const ViewRevision: React.FC<Props> = ({ collection, global }) => {
const { serverURL, routes: { admin, api }, admin: { dateFormat } } = useConfig();
const { serverURL, routes: { admin, api }, admin: { dateFormat }, localization } = useConfig();
const { setStepNav } = useStepNav();
const { params: { id, revisionID } } = useRouteMatch<{ id?: string, revisionID: string }>();
const [compareValue, setCompareValue] = useState<Option>();
const [compareValue, setCompareValue] = useState<CompareOption>(publishedVersionOption);
const [localeOptions] = useState<LocaleOption[]>(() => (localization?.locales ? localization.locales.map((locale) => ({ label: locale, value: locale })) : []));
const [locales, setLocales] = useState<LocaleOption[]>(localeOptions);
let originalDocFetchURL: string;
let revisionFetchURL: string;
let entityLabel: string;
let compareBaseURL: string;
let slug: string;
let parentID: string;
if (collection) {
({ slug } = collection);
@@ -34,6 +39,7 @@ const ViewRevision: React.FC<Props> = ({ collection, global }) => {
revisionFetchURL = `${serverURL}${api}/${slug}/revisions/${revisionID}`;
compareBaseURL = `${serverURL}${api}/${slug}/revisions`;
entityLabel = collection.labels.singular;
parentID = id;
}
if (global) {
@@ -45,7 +51,7 @@ const ViewRevision: React.FC<Props> = ({ collection, global }) => {
}
const useAsTitle = collection?.admin?.useAsTitle || 'id';
const compareFetchURL = compareValue?.value ? `${compareBaseURL}/${compareValue.value}/${compareValue?.value}` : '';
const compareFetchURL = compareValue?.value && compareValue?.value !== 'published' ? `${compareBaseURL}/${compareValue.value}` : '';
const [{ data: doc, isLoading }] = usePayloadAPI(revisionFetchURL);
const [{ data: originalDoc }] = usePayloadAPI(originalDocFetchURL);
@@ -115,18 +121,29 @@ const ViewRevision: React.FC<Props> = ({ collection, global }) => {
/>
<Eyebrow />
<div className={`${baseClass}__wrap`}>
<div className={`${baseClass}__intro`}>Revision created on:</div>
<header className={`${baseClass}__header`}>
<div className={`${baseClass}__intro`}>Revision created on:</div>
<h2>
{formattedCreatedAt}
</h2>
<Restore
className={`${baseClass}__restore`}
collection={collection}
global={global}
/>
</header>
<div className={`${baseClass}__controls`}>
<CompareRevision
baseURL={compareBaseURL}
parentID={parentID}
value={compareValue}
onChange={setCompareValue}
/>
<SelectLocales
onChange={setLocales}
options={localeOptions}
value={locales}
/>
</div>
{isLoading && (
<Loading />

View File

@@ -0,0 +1,4 @@
export const publishedVersionOption = {
label: 'Most recently published',
value: 'published',
};

View File

@@ -1,6 +1,18 @@
import { SanitizedCollectionConfig } from '../../../../collections/config/types';
import { SanitizedGlobalConfig } from '../../../../globals/config/types';
export type LocaleOption = {
label: string
value: string
}
export type CompareOption = {
label: string
value: string
relationTo?: string
options?: CompareOption[]
}
export type Props = {
collection?: SanitizedCollectionConfig
global?: SanitizedGlobalConfig