further types to admin

This commit is contained in:
James
2020-12-26 14:51:07 -05:00
parent e2d370a415
commit 569ce08174
56 changed files with 230 additions and 456 deletions

View File

@@ -7,3 +7,8 @@ export type Props = {
collection: CollectionConfig,
handleChange: (newState) => void,
}
export type ListControls = {
where?: unknown
columns?: string[]
}

View File

@@ -1,12 +1,14 @@
import React from 'react';
export type Props = {
columns: {
accessor: string,
components: {
Heading: React.ReactNode,
renderCell: (row: any, data: any) => React.ReactNode,
},
}[],
data: []
export type Column = {
accessor: string,
components: {
Heading: React.ReactNode,
renderCell: (row: any, data: any) => React.ReactNode,
},
}
export type Props = {
columns: Column[],
data: unknown[]
}

View File

@@ -16,7 +16,7 @@ const UploadGallery: React.FC<Props> = (props) => {
{docs.map((doc, i) => (
<li key={i}>
<UploadCard
{...doc}
{...doc as {id: string, filesize: number, mimeType: string, filename: string}}
{...{ collection }}
onClick={() => onCardClick(doc)}
/>

View File

@@ -1,5 +1,7 @@
import { CollectionConfig } from '../../../../collections/config/types';
export type Props = {
docs?: [],
collection: unknown,
docs?: unknown[],
collection: CollectionConfig,
onCardClick: (doc) => void,
}

View File

@@ -1,5 +1,5 @@
export type Props = {
label?: string
label?: string | JSX.Element
required?: boolean
htmlFor?: string
}

View File

@@ -11,8 +11,8 @@ export type Context = {
export type Props = {
className?: string
operation?: Operation
readOnly: boolean
permissions: {
readOnly?: boolean
permissions?: {
[field: string]: FieldPermissions
}
filter?: (field: Field) => boolean

View File

@@ -32,7 +32,7 @@ const Text: React.FC<Props> = (props) => {
return validationResult;
}, [validate, maxLength, minLength, required]);
const fieldType = useFieldType({
const fieldType = useFieldType<string>({
path,
validate: memoizedValidate,
enableDebouncedValue: true,
@@ -70,7 +70,7 @@ const Text: React.FC<Props> = (props) => {
required={required}
/>
<input
value={value as string || ''}
value={value || ''}
onChange={setValue}
disabled={readOnly}
placeholder={placeholder}

View File

@@ -66,7 +66,6 @@ const AddUploadModal: React.FC<Props> = (props) => {
</header>
<Upload
{...collection.upload}
fieldTypes={fieldTypes}
/>
<NegativeFieldGutterProvider allow>
<RenderFields

View File

@@ -7,7 +7,7 @@ import { Options, FieldType } from './types';
import './index.scss';
const useFieldType = (options: Options): FieldType => {
const useFieldType = <T extends unknown>(options: Options): FieldType<T> => {
const {
path,
validate,

View File

@@ -9,8 +9,8 @@ export type Options = {
stringify?: boolean
}
export type FieldType = {
value: unknown
export type FieldType<T> = {
value: T
errorMessage?: string
showError: boolean
formSubmitted: boolean

View File

@@ -11,7 +11,7 @@ import RenderCustomComponent from '../../utilities/RenderCustomComponent';
import { NegativeFieldGutterProvider } from '../../forms/FieldTypeGutter/context';
const AccountView: React.FC = () => {
const { state: locationState } = useLocation();
const { state: locationState } = useLocation<{ data: unknown }>();
const locale = useLocale();
const { setStepNav } = useStepNav();
const { user, permissions } = useAuth();

View File

@@ -1,20 +1,22 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useConfig } from '@payloadcms/config-provider';
import { useConfig, useAuth } from '@payloadcms/config-provider';
import MinimalTemplate from '../../templates/Minimal';
import Meta from '../../utilities/Meta';
import Form from '../../forms/Form';
import RenderFields from '../../forms/RenderFields';
import fieldTypes from '../../forms/field-types';
import FormSubmit from '../../forms/Submit';
import { useAuth } from '@payloadcms/config-provider';
import { Props } from './types';
import { Field } from '../../../../fields/config/types';
import { NegativeFieldGutterProvider } from '../../forms/FieldTypeGutter/context';
import './index.scss';
const baseClass = 'create-first-user';
const CreateFirstUser = (props) => {
const CreateFirstUser: React.FC<Props> = (props) => {
const { setInitialized } = props;
const { setToken } = useAuth();
const {
@@ -43,7 +45,7 @@ const CreateFirstUser = (props) => {
type: 'password',
required: true,
},
];
] as Field[];
return (
<MinimalTemplate className={baseClass}>
@@ -56,7 +58,7 @@ const CreateFirstUser = (props) => {
/>
<Form
onSuccess={onSuccess}
method="POST"
method="post"
redirect={admin}
action={`${serverURL}${api}/${userSlug}/first-register`}
>

View File

@@ -0,0 +1,3 @@
export type Props = {
setInitialized: (initialized: boolean) => void
}

View File

@@ -5,7 +5,7 @@ import { useStepNav } from '../../elements/StepNav';
import RenderCustomComponent from '../../utilities/RenderCustomComponent';
import DefaultDashboard from './Default';
const Dashboard = () => {
const Dashboard: React.FC = () => {
const { permissions } = useAuth();
const { setStepNav } = useStepNav();
const [filteredGlobals, setFilteredGlobals] = useState([]);
@@ -15,7 +15,9 @@ const Dashboard = () => {
globals,
admin: {
components: {
Dashboard: CustomDashboard,
views: {
Dashboard: CustomDashboard,
} = {},
} = {},
} = {},
} = useConfig();

View File

@@ -1,20 +1,20 @@
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import { toast } from 'react-toastify';
import { useConfig } from '@payloadcms/config-provider';
import { useConfig, useAuth } from '@payloadcms/config-provider';
import MinimalTemplate from '../../templates/Minimal';
import Form from '../../forms/Form';
import Email from '../../forms/field-types/Email';
import FormSubmit from '../../forms/Submit';
import Button from '../../elements/Button';
import Meta from '../../utilities/Meta';
import { useAuth } from '@payloadcms/config-provider';
import './index.scss';
const baseClass = 'forgot-password';
const ForgotPassword = () => {
const ForgotPassword: React.FC = () => {
const [hasSubmitted, setHasSubmitted] = useState(false);
const { user } = useAuth();
const {
@@ -78,9 +78,8 @@ const ForgotPassword = () => {
return (
<MinimalTemplate className={baseClass}>
<Form
novalidate
handleResponse={handleResponse}
method="POST"
method="post"
action={`${serverURL}${api}/${userSlug}/forgot-password`}
>
<h1>Forgot Password</h1>
@@ -88,7 +87,7 @@ const ForgotPassword = () => {
<Email
label="Email Address"
name="email"
autoComplete="email"
admin={{ autoComplete: 'email' }}
required
/>
<FormSubmit>Submit</FormSubmit>

View File

@@ -1,5 +1,4 @@
import React from 'react';
import PropTypes from 'prop-types';
import format from 'date-fns/format';
import Eyebrow from '../../elements/Eyebrow';
import Form from '../../forms/Form';
@@ -10,12 +9,13 @@ import CopyToClipboard from '../../elements/CopyToClipboard';
import Meta from '../../utilities/Meta';
import fieldTypes from '../../forms/field-types';
import LeaveWithoutSaving from '../../modals/LeaveWithoutSaving';
import { Props } from './types';
import './index.scss';
const baseClass = 'global-edit';
const DefaultGlobalView = (props) => {
const DefaultGlobalView: React.FC<Props> = (props) => {
const {
global, data, onSave, permissions, action, apiURL, initialState,
} = props;
@@ -58,7 +58,7 @@ const DefaultGlobalView = (props) => {
operation="update"
readOnly={!hasSavePermission}
permissions={permissions.fields}
filter={(field) => (!field.position || (field.position && field.position !== 'sidebar'))}
filter={(field) => (!field.admin.position || (field.admin.position && field.admin.position !== 'sidebar'))}
fieldTypes={fieldTypes}
fieldSchema={fields}
/>
@@ -92,8 +92,7 @@ const DefaultGlobalView = (props) => {
operation="update"
readOnly={!hasSavePermission}
permissions={permissions.fields}
filter={(field) => field.position === 'sidebar'}
position="sidebar"
filter={(field) => field.admin.position === 'sidebar'}
fieldTypes={fieldTypes}
fieldSchema={fields}
/>
@@ -103,7 +102,7 @@ const DefaultGlobalView = (props) => {
{data.updatedAt && (
<li>
<div className={`${baseClass}__label`}>Last Modified</div>
<div>{format(new Date(data.updatedAt), 'MMMM do yyyy, h:mm a')}</div>
<div>{format(new Date(data.updatedAt as string), 'MMMM do yyyy, h:mm a')}</div>
</li>
)}
</ul>
@@ -114,29 +113,4 @@ const DefaultGlobalView = (props) => {
);
};
DefaultGlobalView.defaultProps = {
data: undefined,
};
DefaultGlobalView.propTypes = {
global: PropTypes.shape({
label: PropTypes.string.isRequired,
fields: PropTypes.arrayOf(PropTypes.shape({})),
preview: PropTypes.func,
}).isRequired,
data: PropTypes.shape({
updatedAt: PropTypes.string,
}),
onSave: PropTypes.func.isRequired,
permissions: PropTypes.shape({
update: PropTypes.shape({
permission: PropTypes.bool,
}),
fields: PropTypes.shape({}),
}).isRequired,
action: PropTypes.string.isRequired,
apiURL: PropTypes.string.isRequired,
initialState: PropTypes.shape({}).isRequired,
};
export default DefaultGlobalView;

View File

@@ -1,5 +1,4 @@
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import { useHistory, useLocation } from 'react-router-dom';
import { useConfig, useAuth } from '@payloadcms/config-provider';
import { useStepNav } from '../../elements/StepNav';
@@ -11,9 +10,10 @@ import RenderCustomComponent from '../../utilities/RenderCustomComponent';
import DefaultGlobal from './Default';
import buildStateFromSchema from '../../forms/Form/buildStateFromSchema';
import { NegativeFieldGutterProvider } from '../../forms/FieldTypeGutter/context';
import { IndexProps } from './types';
const GlobalView = (props) => {
const { state: locationState } = useLocation();
const GlobalView: React.FC<IndexProps> = (props) => {
const { state: locationState } = useLocation<{data?: Record<string, unknown>}>();
const history = useHistory();
const locale = useLocale();
const { setStepNav } = useStepNav();
@@ -97,18 +97,4 @@ const GlobalView = (props) => {
</NegativeFieldGutterProvider>
);
};
GlobalView.propTypes = {
global: PropTypes.shape({
label: PropTypes.string.isRequired,
slug: PropTypes.string.isRequired,
fields: PropTypes.arrayOf(PropTypes.shape({})),
admin: PropTypes.shape({
components: PropTypes.shape({
Edit: PropTypes.node,
}),
}),
}).isRequired,
};
export default GlobalView;

View File

@@ -0,0 +1,17 @@
import { GlobalPermission } from '../../../../auth/types';
import { GlobalConfig } from '../../../../globals/config/types';
import { Fields } from '../../forms/Form/types';
export type IndexProps = {
global: GlobalConfig
}
export type Props = {
global: GlobalConfig
data: Record<string, unknown>
onSave: () => void
permissions: GlobalPermission
action: string
apiURL: string
initialState: Fields
}

View File

@@ -15,7 +15,7 @@ import './index.scss';
const baseClass = 'login';
const Login = () => {
const Login: React.FC = () => {
const history = useHistory();
const { user, setToken } = useAuth();
const { admin: { user: userSlug }, serverURL, routes: { admin, api } } = useConfig();
@@ -71,17 +71,16 @@ const Login = () => {
disableSuccessStatus
waitForAutocomplete
onSuccess={onSuccess}
method="POST"
method="post"
action={`${serverURL}${api}/${userSlug}/login`}
>
<Email
label="Email Address"
name="email"
autoComplete="email"
admin={{ autoComplete: 'email' }}
required
/>
<Password
error="password"
label="Password"
name="password"
autoComplete="off"

View File

@@ -1,7 +1,6 @@
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import { useConfig } from '@payloadcms/config-provider';
import { useAuth } from '@payloadcms/config-provider';
import { useConfig, useAuth } from '@payloadcms/config-provider';
import Minimal from '../../templates/Minimal';
import Button from '../../elements/Button';
import Meta from '../../utilities/Meta';
@@ -10,7 +9,7 @@ import './index.scss';
const baseClass = 'logout';
const Logout = (props) => {
const Logout: React.FC<{inactivity?: boolean}> = (props) => {
const { inactivity } = props;
const { logOut } = useAuth();
@@ -47,12 +46,4 @@ const Logout = (props) => {
);
};
Logout.defaultProps = {
inactivity: false,
};
Logout.propTypes = {
inactivity: PropTypes.bool,
};
export default Logout;

View File

@@ -5,7 +5,7 @@ import { useStepNav } from '../../elements/StepNav';
import Button from '../../elements/Button';
import Meta from '../../utilities/Meta';
const NotFound = () => {
const NotFound: React.FC = () => {
const { setStepNav } = useStepNav();
const { routes: { admin } } = useConfig();

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { Link, useHistory, useParams } from 'react-router-dom';
import { useConfig } from '@payloadcms/config-provider';
import { useConfig, useAuth } from '@payloadcms/config-provider';
import MinimalTemplate from '../../templates/Minimal';
import Form from '../../forms/Form';
import Password from '../../forms/field-types/Password';
@@ -8,16 +8,16 @@ import ConfirmPassword from '../../forms/field-types/ConfirmPassword';
import FormSubmit from '../../forms/Submit';
import Button from '../../elements/Button';
import Meta from '../../utilities/Meta';
import { useAuth } from '@payloadcms/config-provider';
import './index.scss';
import HiddenInput from '../../forms/field-types/HiddenInput';
const baseClass = 'reset-password';
const ResetPassword = () => {
const ResetPassword: React.FC = () => {
const { admin: { user: userSlug }, serverURL, routes: { admin, api } } = useConfig();
const { token } = useParams();
const { token } = useParams<{token?: string}>();
const history = useHistory();
const { user, setToken } = useAuth();
@@ -65,12 +65,11 @@ const ResetPassword = () => {
<h1>Reset Password</h1>
<Form
onSuccess={onSuccess}
method="POST"
method="post"
action={`${serverURL}${api}/${userSlug}/reset-password`}
redirect={admin}
>
<Password
error="password"
label="New Password"
name="password"
autoComplete="off"

View File

@@ -4,7 +4,7 @@ import Button from '../../elements/Button';
import Meta from '../../utilities/Meta';
import MinimalTemplate from '../../templates/Minimal';
const Unauthorized = () => {
const Unauthorized: React.FC = () => {
const { routes: { admin } } = useConfig();
return (

View File

@@ -1,23 +1,22 @@
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { useParams } from 'react-router-dom';
import { useConfig, useAuth } from '@payloadcms/config-provider';
import Logo from '../../graphics/Logo';
import MinimalTemplate from '../../templates/Minimal';
import Button from '../../elements/Button';
import Meta from '../../utilities/Meta';
import { CollectionConfig } from '../../../../collections/config/types';
import { useConfig } from '@payloadcms/config-provider';
import { useAuth } from '@payloadcms/config-provider';
import Login from '../Login';
import './index.scss';
const baseClass = 'verify';
const Verify = ({ collection }) => {
const Verify: React.FC<{ collection: CollectionConfig }> = ({ collection }) => {
const { slug: collectionSlug } = collection;
const { user } = useAuth();
const { token } = useParams();
const { token } = useParams<{token?: string}>();
const { serverURL, routes: { admin: adminRoute }, admin: { user: adminUser } } = useConfig();
const isAdminUser = collectionSlug === adminUser;
@@ -66,10 +65,4 @@ const Verify = ({ collection }) => {
</MinimalTemplate>
);
};
Verify.propTypes = {
collection: PropTypes.shape({
slug: PropTypes.string,
}).isRequired,
};
export default Verify;

View File

@@ -13,7 +13,7 @@ const path = 'apiKey';
const baseClass = 'api-key';
const validate = (val) => text(val, { minLength: 24, maxLength: 48 });
const APIKey = () => {
const APIKey: React.FC = () => {
const [initialAPIKey, setInitialAPIKey] = useState(null);
const [highlightedField, setHighlightedField] = useState(false);
@@ -28,7 +28,7 @@ const APIKey = () => {
<span>
API Key
</span>
<CopyToClipboard value={apiKeyValue} />
<CopyToClipboard value={apiKeyValue as string} />
</div>
), [apiKeyValue]);
@@ -83,9 +83,9 @@ const APIKey = () => {
label={APIKeyLabel}
/>
<input
value={value || ''}
value={value as string || ''}
className={highlightedField ? 'highlight' : undefined}
disabled="disabled"
disabled
type="text"
id="apiKey"
name="apiKey"

View File

@@ -100,7 +100,6 @@ const DefaultEditView: React.FC<Props> = (props) => {
<Upload
data={data}
{...upload}
fieldTypes={fieldTypes}
/>
)}
<RenderFields

View File

@@ -1,11 +1,11 @@
import React, {
useState, useRef, useEffect, useCallback,
} from 'react';
import PropTypes from 'prop-types';
import useFieldType from '../../../../forms/useFieldType';
import Button from '../../../../elements/Button';
import FileDetails from '../../../../elements/FileDetails';
import Error from '../../../../forms/Error';
import { Props, Data } from './types';
import './index.scss';
@@ -24,9 +24,9 @@ const validate = (value) => {
return true;
};
const File = (props) => {
const inputRef = useRef();
const dropRef = useRef();
const File: React.FC<Props> = (props) => {
const inputRef = useRef(null);
const dropRef = useRef(null);
const [fileList, setFileList] = useState(undefined);
const [selectingFile, setSelectingFile] = useState(false);
const [dragging, setDragging] = useState(false);
@@ -34,7 +34,9 @@ const File = (props) => {
const [replacingFile, setReplacingFile] = useState(false);
const {
data = {}, adminThumbnail, staticURL,
data = {} as Data,
adminThumbnail,
staticURL,
} = props;
const { filename } = data;
@@ -44,7 +46,7 @@ const File = (props) => {
setValue,
showError,
errorMessage,
} = useFieldType({
} = useFieldType<{name: string}>({
path: 'file',
validate,
});
@@ -111,7 +113,7 @@ const File = (props) => {
};
}
return () => { };
return () => null;
}, [handleDragIn, handleDragOut, handleDrop, dropRef]);
useEffect(() => {
@@ -197,20 +199,4 @@ const File = (props) => {
);
};
File.defaultProps = {
data: undefined,
adminThumbnail: undefined,
};
File.propTypes = {
fieldTypes: PropTypes.shape({}).isRequired,
data: PropTypes.shape({
filename: PropTypes.string,
mimeType: PropTypes.string,
filesize: PropTypes.number,
}),
staticURL: PropTypes.string.isRequired,
adminThumbnail: PropTypes.string,
};
export default File;

View File

@@ -0,0 +1,11 @@
export type Data = {
filename: string
mimeType: string
filesize: number
}
export type Props = {
data?: Data
adminThumbnail?: string
staticURL: string
}

View File

@@ -4,16 +4,15 @@ import { useConfig, useAuth } from '@payloadcms/config-provider';
import { useStepNav } from '../../../elements/StepNav';
import usePayloadAPI from '../../../../hooks/usePayloadAPI';
import RenderCustomComponent from '../../../utilities/RenderCustomComponent';
import DefaultEdit from './Default';
import buildStateFromSchema from '../../../forms/Form/buildStateFromSchema';
import { NegativeFieldGutterProvider } from '../../../forms/FieldTypeGutter/context';
import { useLocale } from '../../../utilities/Locale';
import { Props } from './types';
import { IndexProps } from './types';
import { StepNavItem } from '../../../elements/StepNav/types';
const EditView: React.FC<Props> = (props) => {
const EditView: React.FC<IndexProps> = (props) => {
const { collection, isEditing } = props;
const {

View File

@@ -5,7 +5,7 @@ import { Fields } from '../../../forms/Form/types';
export type IndexProps = {
collection: CollectionConfig
isEditing: boolean
isEditing?: boolean
}
export type Props = IndexProps & {

View File

@@ -1,9 +1,9 @@
/* eslint-disable react/jsx-max-props-per-line */
import React from 'react';
import { render } from '@testing-library/react';
import BlocksCell from './types/Blocks';
import DateCell from './types/Date';
import Checkbox from './types/Checkbox';
import BlocksCell from './field-types/Blocks';
import DateCell from './field-types/Date';
import Checkbox from './field-types/Checkbox';
describe('Cell Types', () => {
describe('Blocks', () => {

View File

@@ -1,5 +1,4 @@
import React from 'react';
import PropTypes from 'prop-types';
const BlocksCell = ({ data, field }) => {
const selectedBlocks = data ? data.map(({ blockType }) => blockType) : [];
@@ -24,21 +23,4 @@ const BlocksCell = ({ data, field }) => {
<span>{label}</span>
);
};
BlocksCell.defaultProps = {
data: [],
};
BlocksCell.propTypes = {
data: PropTypes.arrayOf(
PropTypes.shape({}),
),
field: PropTypes.shape({
label: PropTypes.string,
blocks: PropTypes.arrayOf(
PropTypes.shape({}),
),
}).isRequired,
};
export default BlocksCell;

View File

@@ -1,5 +1,4 @@
import React from 'react';
import PropTypes from 'prop-types';
import './index.scss';
@@ -9,13 +8,4 @@ const Checkbox = ({ data }) => (
<span>{JSON.stringify(data)}</span>
</code>
);
Checkbox.defaultProps = {
data: undefined,
};
Checkbox.propTypes = {
data: PropTypes.bool,
};
export default Checkbox;

View File

@@ -1,5 +1,4 @@
import React from 'react';
import PropTypes from 'prop-types';
import './index.scss';
@@ -12,12 +11,4 @@ const CodeCell = ({ data }) => {
);
};
CodeCell.defaultProps = {
data: '',
};
CodeCell.propTypes = {
data: PropTypes.string,
};
export default CodeCell;

View File

@@ -1,5 +1,4 @@
import React from 'react';
import PropTypes from 'prop-types';
import format from 'date-fns/format';
const DateCell = ({ data }) => (
@@ -8,12 +7,4 @@ const DateCell = ({ data }) => (
</span>
);
DateCell.defaultProps = {
data: undefined,
};
DateCell.propTypes = {
data: PropTypes.string,
};
export default DateCell;

View File

@@ -1,5 +1,4 @@
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import { useConfig } from '@payloadcms/config-provider';
const RelationshipCell = (props) => {
@@ -48,24 +47,4 @@ const RelationshipCell = (props) => {
);
};
RelationshipCell.defaultProps = {
data: undefined,
};
RelationshipCell.propTypes = {
data: PropTypes.oneOfType([
PropTypes.shape({}),
PropTypes.array,
PropTypes.string,
]),
field: PropTypes.shape({
relationTo: PropTypes.oneOfType([
PropTypes.arrayOf(
PropTypes.string,
),
PropTypes.string,
]),
}).isRequired,
};
export default RelationshipCell;

View File

@@ -0,0 +1,11 @@
import React from 'react';
const RichTextCell = ({ data }) => {
const flattenedText = data?.map((i) => i?.children?.map((c) => c.text)).join(' ');
const textToShow = flattenedText.length > 100 ? `${flattenedText.slice(0, 100)}\u2026` : flattenedText;
return (
<span>{textToShow}</span>
);
};
export default RichTextCell;

View File

@@ -0,0 +1,24 @@
import React from 'react';
const SelectCell = ({ data, field }) => {
const findLabel = (items) => items.map((i) => {
const found = field.options.filter((f) => f.value === i)?.[0]?.label;
return found;
}).join(', ');
let content = '';
if (field?.options?.[0]?.value) {
content = (Array.isArray(data))
? findLabel(data) // hasMany
: findLabel([data]);
} else {
content = data.join(', ');
}
return (
<span>
{content}
</span>
);
};
export default SelectCell;

View File

@@ -1,5 +1,4 @@
import React from 'react';
import PropTypes from 'prop-types';
const TextareaCell = ({ data }) => {
const textToShow = data.length > 100 ? `${data.substr(0, 100)}\u2026` : data;
@@ -8,15 +7,4 @@ const TextareaCell = ({ data }) => {
);
};
TextareaCell.defaultProps = {
data: [],
};
TextareaCell.propTypes = {
data: PropTypes.string,
field: PropTypes.shape({
label: PropTypes.string,
}).isRequired,
};
export default TextareaCell;

View File

@@ -1,5 +1,4 @@
import React from 'react';
import PropTypes from 'prop-types';
import './index.scss';
@@ -16,18 +15,4 @@ const UploadCell = ({ data }) => (
</React.Fragment>
);
UploadCell.defaultProps = {
data: {},
};
UploadCell.propTypes = {
data: PropTypes.oneOfType([
PropTypes.shape({
filename: PropTypes.string,
mimeType: PropTypes.string,
}),
PropTypes.string,
]),
};
export default UploadCell;

View File

@@ -1,11 +1,11 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import { useConfig } from '@payloadcms/config-provider';
import RenderCustomComponent from '../../../../utilities/RenderCustomComponent';
import cellComponents from './types';
import cellComponents from './field-types';
import { Props } from './types';
const DefaultCell = (props) => {
const DefaultCell: React.FC<Props> = (props) => {
const {
field,
colIndex,
@@ -20,9 +20,11 @@ const DefaultCell = (props) => {
const { routes: { admin } } = useConfig();
let WrapElement = 'span';
let WrapElement: React.ComponentType | string = 'span';
const wrapElementProps = {};
const wrapElementProps: {
to?: string
} = {};
if (colIndex === 0) {
WrapElement = Link;
@@ -52,7 +54,7 @@ const DefaultCell = (props) => {
);
};
const Cell = (props) => {
const Cell: React.FC<Props> = (props) => {
const {
colIndex,
collection,
@@ -83,44 +85,4 @@ const Cell = (props) => {
);
};
const defaultProps = {
cellData: undefined,
};
const propTypes = {
colIndex: PropTypes.number.isRequired,
collection: PropTypes.shape({
slug: PropTypes.string,
upload: PropTypes.shape({
adminThumbnail: PropTypes.string,
}),
}).isRequired,
cellData: PropTypes.oneOfType([
PropTypes.shape({}),
PropTypes.string,
PropTypes.number,
PropTypes.instanceOf(Date),
PropTypes.array,
PropTypes.bool,
]),
rowData: PropTypes.shape({
id: PropTypes.string,
}).isRequired,
field: PropTypes.shape({
name: PropTypes.string,
type: PropTypes.string,
label: PropTypes.string,
admin: PropTypes.shape({
components: PropTypes.shape({
Cell: PropTypes.func,
}),
}),
}).isRequired,
};
DefaultCell.defaultProps = defaultProps;
DefaultCell.propTypes = propTypes;
Cell.defaultProps = defaultProps;
Cell.propTypes = propTypes;
export default Cell;

View File

@@ -0,0 +1,12 @@
import { Field } from '../../../../../../fields/config/types';
import { CollectionConfig } from '../../../../../../collections/config/types';
export type Props = {
field: Field
colIndex: number
collection: CollectionConfig
cellData: unknown
rowData: {
[path: string]: unknown
}
}

View File

@@ -1,34 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
const RichTextCell = ({ data }) => {
const flattenedText = data?.map((i) => i?.children?.map((c) => c.text)).join(' ');
const textToShow = flattenedText.length > 100 ? `${flattenedText.slice(0, 100)}\u2026` : flattenedText;
return (
<span>{textToShow}</span>
);
};
RichTextCell.defaultProps = {
data: [],
};
RichTextCell.propTypes = {
data: PropTypes.arrayOf(
PropTypes.shape({
children: PropTypes.arrayOf(
PropTypes.shape({
bold: PropTypes.string,
text: PropTypes.string,
}),
),
}),
),
field: PropTypes.shape({
label: PropTypes.string,
}).isRequired,
};
export default RichTextCell;

View File

@@ -1,54 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
const SelectCell = ({ data, field }) => {
const findLabel = (items) => items.map((i) => {
const found = field.options.filter((f) => f.value === i)?.[0]?.label;
return found;
}).join(', ');
let content = '';
if (field?.options?.[0]?.value) {
content = (Array.isArray(data))
? findLabel(data) // hasMany
: findLabel([data]);
} else {
content = data.join(', ');
}
return (
<span>
{content}
</span>
);
};
SelectCell.defaultProps = {
data: [],
};
SelectCell.propTypes = {
data: PropTypes.oneOfType(
[
PropTypes.string,
PropTypes.arrayOf(PropTypes.string),
PropTypes.arrayOf(PropTypes.shape({
value: PropTypes.string,
label: PropTypes.string,
})),
],
),
field: PropTypes.shape({
label: PropTypes.string,
options: PropTypes.arrayOf(
PropTypes.oneOfType([
PropTypes.string,
PropTypes.shape({
value: PropTypes.string,
label: PropTypes.string,
}),
]),
),
}).isRequired,
};
export default SelectCell;

View File

@@ -1,6 +1,5 @@
import React from 'react';
import { useHistory, useLocation } from 'react-router-dom';
import PropTypes from 'prop-types';
import { useConfig } from '@payloadcms/config-provider';
import UploadGallery from '../../../elements/UploadGallery';
import Eyebrow from '../../../elements/Eyebrow';
@@ -10,12 +9,13 @@ import Pill from '../../../elements/Pill';
import Button from '../../../elements/Button';
import Table from '../../../elements/Table';
import Meta from '../../../utilities/Meta';
import { Props } from './types';
import './index.scss';
const baseClass = 'collection-list';
const DefaultList = (props) => {
const DefaultList: React.FC<Props> = (props) => {
const {
collection,
collection: {
@@ -132,49 +132,4 @@ const DefaultList = (props) => {
);
};
DefaultList.defaultProps = {
data: null,
};
DefaultList.propTypes = {
collection: PropTypes.shape({
upload: PropTypes.shape({}),
labels: PropTypes.shape({
singular: PropTypes.string,
plural: PropTypes.string,
}),
slug: PropTypes.string,
admin: PropTypes.shape({
useAsTitle: PropTypes.string,
}),
fields: PropTypes.arrayOf(PropTypes.shape),
timestamps: PropTypes.bool,
}).isRequired,
newDocumentURL: PropTypes.string.isRequired,
data: PropTypes.shape({
docs: PropTypes.arrayOf(
PropTypes.shape({}),
),
limit: PropTypes.number,
nextPage: PropTypes.number,
prevPage: PropTypes.number,
totalDocs: PropTypes.number,
hasNextPage: PropTypes.bool,
hasPrevPage: PropTypes.bool,
page: PropTypes.number,
totalPages: PropTypes.number,
}),
setListControls: PropTypes.func.isRequired,
setSort: PropTypes.func.isRequired,
listControls: PropTypes.shape({
columns: PropTypes.arrayOf(
PropTypes.string,
),
}).isRequired,
hasCreatePermission: PropTypes.bool.isRequired,
columns: PropTypes.arrayOf(
PropTypes.shape({}),
).isRequired,
};
export default DefaultList;

View File

@@ -1,8 +1,11 @@
import React from 'react';
import Cell from './Cell';
import SortColumn from '../../../elements/SortColumn';
import { CollectionConfig } from '../../../../../collections/config/types';
import { Column } from '../../../elements/Table/types';
import { fieldHasSubFields, Field } from '../../../../../fields/config/types';
const buildColumns = (collection, columns, setSort) => (columns || []).reduce((cols, col, colIndex) => {
const buildColumns = (collection: CollectionConfig, columns: string[], setSort: (sort: string) => void): Column[] => (columns || []).reduce((cols, col, colIndex) => {
let field = null;
const fields = [
@@ -11,17 +14,17 @@ const buildColumns = (collection, columns, setSort) => (columns || []).reduce((c
name: 'id',
type: 'text',
label: 'ID',
},
} as Field,
{
name: 'updatedAt',
type: 'date',
label: 'Updated At',
},
} as Field,
{
name: 'createdAt',
type: 'date',
label: 'Created At',
},
} as Field,
];
fields.forEach((fieldToCheck) => {
@@ -29,7 +32,7 @@ const buildColumns = (collection, columns, setSort) => (columns || []).reduce((c
field = fieldToCheck;
}
if (!fieldToCheck.name && Array.isArray(fieldToCheck.fields)) {
if (!fieldToCheck.name && fieldHasSubFields(fieldToCheck)) {
fieldToCheck.fields.forEach((subField) => {
if (subField.name === col) {
field = subField;

View File

@@ -1,5 +1,4 @@
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import queryString from 'qs';
import { useLocation } from 'react-router-dom';
import { useConfig, useAuth } from '@payloadcms/config-provider';
@@ -8,10 +7,12 @@ import usePayloadAPI from '../../../../hooks/usePayloadAPI';
import DefaultList from './Default';
import RenderCustomComponent from '../../../utilities/RenderCustomComponent';
import { useStepNav } from '../../../elements/StepNav';
import { ListControls } from '../../../elements/ListControls/types';
import formatFields from './formatFields';
import buildColumns from './buildColumns';
import { ListIndexProps } from './types';
const ListView = (props) => {
const ListView: React.FC<ListIndexProps> = (props) => {
const {
collection,
collection: {
@@ -21,8 +22,14 @@ const ListView = (props) => {
},
admin: {
components: {
List: CustomList,
} = {},
views: {
List: CustomList,
},
} = {
views: {
List: undefined,
},
},
},
},
} = props;
@@ -33,7 +40,7 @@ const ListView = (props) => {
const { setStepNav } = useStepNav();
const [fields] = useState(() => formatFields(collection));
const [listControls, setListControls] = useState({});
const [listControls, setListControls] = useState<ListControls>({});
const [columns, setColumns] = useState([]);
const [sort, setSort] = useState(null);
@@ -51,6 +58,9 @@ const ListView = (props) => {
useEffect(() => {
const params = {
depth: 1,
page: undefined,
sort: undefined,
where: undefined,
};
if (page) params.page = page;
@@ -90,21 +100,4 @@ const ListView = (props) => {
);
};
ListView.propTypes = {
collection: PropTypes.shape({
labels: PropTypes.shape({
singular: PropTypes.string,
plural: PropTypes.string,
}),
admin: PropTypes.shape({
components: PropTypes.shape({
List: PropTypes.node,
}),
}),
slug: PropTypes.string,
fields: PropTypes.arrayOf(PropTypes.shape),
timestamps: PropTypes.bool,
}).isRequired,
};
export default ListView;

View File

@@ -0,0 +1,16 @@
import { CollectionConfig, PaginatedDocs } from '../../../../../collections/config/types';
import { Column } from '../../../elements/Table/types';
export type Props = {
collection: CollectionConfig
data: PaginatedDocs
newDocumentURL: string
setListControls: (controls: unknown) => void
setSort: (sort: string) => void
columns: Column[]
hasCreatePermission: boolean
}
export type ListIndexProps = {
collection: CollectionConfig
}

View File

@@ -15,7 +15,7 @@ const useIntersect = ({
const [node, setNode] = useState(null);
const observer = useRef(
new window.IntersectionObserver(([entry]) => updateEntry(entry), {
new window.IntersectionObserver(([ent]) => updateEntry(ent), {
root,
rootMargin,
threshold,

View File

@@ -69,6 +69,9 @@ export type PayloadConfig = {
Icon?: React.ComponentType
Logo?: React.ComponentType
}
views?: {
Dashboard?: React.ComponentType
}
}
};
collections?: PayloadCollectionConfig[];

View File

@@ -1,3 +1,4 @@
import React from 'react';
import { Model, Document } from 'mongoose';
import { DeepRequired } from 'ts-essentials';
import { Access } from '../../config/types';
@@ -8,6 +9,7 @@ export type GlobalModel = Model<Document>
export type PayloadGlobalConfig = {
slug: string
label?: string
preview?: (doc: Document, token: string) => string
access?: {
create?: Access;
read?: Access;
@@ -16,6 +18,13 @@ export type PayloadGlobalConfig = {
admin?: Access;
}
fields: Field[];
admin?: {
components?: {
views?: {
Edit?: React.ComponentType
}
}
}
}
export type GlobalConfig = DeepRequired<PayloadGlobalConfig>