chore: fixes admin view types (#3398)

This commit is contained in:
Jacob Fletcher
2023-09-27 10:56:15 -04:00
committed by GitHub
parent 6e81fa68e8
commit d61eef23d1
28 changed files with 718 additions and 679 deletions

View File

@@ -1,3 +1,2 @@
export { default as Edit } from '../../dist/admin/components/views/collections/Edit/Default';
export type { Props } from '../../dist/admin/components/views/collections/Edit/types';
//# sourceMappingURL=Edit.d.ts.map

View File

@@ -15,4 +15,4 @@ function _interop_require_default(obj) {
};
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9leHBvcnRzL2NvbXBvbmVudHMvdmlld3MvRWRpdC50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgeyBkZWZhdWx0IGFzIEVkaXQgfSBmcm9tICcuLi8uLi8uLi9hZG1pbi9jb21wb25lbnRzL3ZpZXdzL2NvbGxlY3Rpb25zL0VkaXQvRGVmYXVsdCdcbmV4cG9ydCB0eXBlIHsgUHJvcHMgfSBmcm9tICcuLi8uLi8uLi9hZG1pbi9jb21wb25lbnRzL3ZpZXdzL2NvbGxlY3Rpb25zL0VkaXQvdHlwZXMnXG4iXSwibmFtZXMiOlsiRWRpdCJdLCJtYXBwaW5ncyI6Ijs7OzsrQkFBb0JBOzs7ZUFBQUEsZ0JBQUk7OztnRUFBUSJ9
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9leHBvcnRzL2NvbXBvbmVudHMvdmlld3MvRWRpdC50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgeyBkZWZhdWx0IGFzIEVkaXQgfSBmcm9tICcuLi8uLi8uLi9hZG1pbi9jb21wb25lbnRzL3ZpZXdzL2NvbGxlY3Rpb25zL0VkaXQvRGVmYXVsdCdcbiJdLCJuYW1lcyI6WyJFZGl0Il0sIm1hcHBpbmdzIjoiOzs7OytCQUFvQkE7OztlQUFBQSxnQkFBSTs7O2dFQUFRIn0=

View File

@@ -1,7 +1,6 @@
import type React from 'react'
import type { HTMLAttributes } from 'react'
import type { Props as EditViewProps } from '../../views/collections/Edit/types'
import { EditViewProps } from '../../views/types'
export type DocumentDrawerProps = {
collectionSlug: string

View File

@@ -1,6 +1,5 @@
import type { CollectionEditViewConfig } from '../../../../../collections/config/types'
import { EditViewComponent, EditViewConfig } from '../../../../../exports/config'
import type { SanitizedCollectionConfig, SanitizedGlobalConfig } from '../../../../../exports/types'
import type { GlobalEditViewConfig } from '../../../../../globals/config/types'
import { defaultGlobalViews } from '../../../views/Global/Routes/CustomComponent'
import { defaultCollectionViews } from '../../../views/collections/Edit/Routes/CustomComponent'
@@ -8,10 +7,10 @@ import { defaultCollectionViews } from '../../../views/collections/Edit/Routes/C
export const getCustomViews = (args: {
collection: SanitizedCollectionConfig
global: SanitizedGlobalConfig
}): (CollectionEditViewConfig | GlobalEditViewConfig)[] => {
}): EditViewConfig[] => {
const { collection, global } = args
let customViews: (CollectionEditViewConfig | GlobalEditViewConfig)[]
let customViews: EditViewConfig[]
if (collection) {
const collectionViewsConfig =

View File

@@ -2,7 +2,6 @@ import React, { Fragment, useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import type { SanitizedCollectionConfig } from '../../../../../../collections/config/types'
import type { Props as EditViewProps } from '../../../../views/collections/Edit/types'
import type { Value } from '../types'
import type { Props } from './types'
@@ -16,6 +15,7 @@ import { useAuth } from '../../../../utilities/Auth'
import { useConfig } from '../../../../utilities/Config'
import './index.scss'
import { useRelatedCollections } from './useRelatedCollections'
import { EditViewProps } from '../../../../views/types'
const baseClass = 'relationship-add-new'

View File

@@ -2,7 +2,6 @@ import React, { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import type { Translation } from '../../../../translations/type'
import type { Props } from './types'
import { DocumentControls } from '../../elements/DocumentControls'
import { DocumentHeader } from '../../elements/DocumentHeader'
@@ -20,125 +19,132 @@ import { OperationContext } from '../../utilities/OperationProvider'
import Auth from '../collections/Edit/Auth'
import { ToggleTheme } from './ToggleTheme'
import './index.scss'
import { EditViewProps } from '../types'
const baseClass = 'account'
const DefaultAccount: React.FC<Props> = (props) => {
const {
action,
apiURL,
collection,
data,
hasSavePermission,
initialState,
isLoading,
onSave: onSaveFromProps,
permissions,
} = props
const DefaultAccount: React.FC<EditViewProps> = (props) => {
if ('collection' in props) {
const {
action,
apiURL,
collection,
data,
hasSavePermission,
initialState,
isLoading,
onSave: onSaveFromProps,
permissions,
} = props
const { auth, fields } = collection
const { auth, fields } = collection
const { refreshCookieAsync } = useAuth()
const { i18n, t } = useTranslation('authentication')
const { refreshCookieAsync } = useAuth()
const { i18n, t } = useTranslation('authentication')
const languageOptions = Object.entries(i18n.options.resources).map(([language, resource]) => ({
label: (resource as Translation).general.thisLanguage,
value: language,
}))
const languageOptions = Object.entries(i18n.options.resources).map(([language, resource]) => ({
label: (resource as Translation).general.thisLanguage,
value: language,
}))
const onSave = useCallback(async () => {
await refreshCookieAsync()
if (typeof onSaveFromProps === 'function') {
onSaveFromProps()
}
}, [onSaveFromProps, refreshCookieAsync])
const onSave = useCallback(async () => {
await refreshCookieAsync()
if (typeof onSaveFromProps === 'function') {
onSaveFromProps({})
}
}, [onSaveFromProps, refreshCookieAsync])
const classes = [baseClass].filter(Boolean).join(' ')
const classes = [baseClass].filter(Boolean).join(' ')
return (
<React.Fragment>
<LoadingOverlayToggle name="account" show={isLoading} type="withoutNav" />
{!isLoading && (
<div className={classes}>
<OperationContext.Provider value="update">
<Form
action={action}
className={`${baseClass}__form`}
disabled={!hasSavePermission}
initialState={initialState}
method="patch"
onSuccess={onSave}
>
<DocumentHeader apiURL={apiURL} collection={collection} data={data} />
<DocumentControls
apiURL={apiURL}
collection={collection}
data={data}
hasSavePermission={hasSavePermission}
permissions={permissions}
/>
<div className={`${baseClass}__main`}>
<Meta
description={t('accountOfCurrentUser')}
keywords={t('account')}
title={t('account')}
return (
<React.Fragment>
<LoadingOverlayToggle name="account" show={isLoading} type="withoutNav" />
{!isLoading && (
<div className={classes}>
<OperationContext.Provider value="update">
<Form
action={action}
className={`${baseClass}__form`}
disabled={!hasSavePermission}
initialState={initialState}
method="patch"
onSuccess={onSave}
>
<DocumentHeader apiURL={apiURL} collection={collection} data={data} />
<DocumentControls
apiURL={apiURL}
collection={collection}
data={data}
hasSavePermission={hasSavePermission}
permissions={permissions}
/>
{!(collection.versions?.drafts && collection.versions?.drafts?.autosave) && (
<LeaveWithoutSaving />
)}
<div className={`${baseClass}__edit`}>
<Gutter className={`${baseClass}__header`}>
<Auth
collection={collection}
email={data?.email}
operation="update"
readOnly={!hasSavePermission}
useAPIKey={auth.useAPIKey}
/>
<RenderFields
fieldSchema={fields}
fieldTypes={fieldTypes}
filter={(field) => field?.admin?.position !== 'sidebar'}
permissions={permissions.fields}
readOnly={!hasSavePermission}
/>
</Gutter>
<Gutter className={`${baseClass}__payload-settings`}>
<h3>{t('general:payloadSettings')}</h3>
<div className={`${baseClass}__language`}>
<Label htmlFor="language-select" label={t('general:language')} />
<ReactSelect
inputId="language-select"
onChange={({ value }) => i18n.changeLanguage(value)}
options={languageOptions}
value={languageOptions.find((language) => language.value === i18n.language)}
<div className={`${baseClass}__main`}>
<Meta
description={t('accountOfCurrentUser')}
keywords={t('account')}
title={t('account')}
/>
{!(collection.versions?.drafts && collection.versions?.drafts?.autosave) && (
<LeaveWithoutSaving />
)}
<div className={`${baseClass}__edit`}>
<Gutter className={`${baseClass}__header`}>
<Auth
collection={collection}
email={data?.email}
operation="update"
readOnly={!hasSavePermission}
useAPIKey={auth.useAPIKey}
/>
</div>
<ToggleTheme />
</Gutter>
</div>
</div>
<div className={`${baseClass}__sidebar-wrap`}>
<div className={`${baseClass}__sidebar`}>
<div className={`${baseClass}__sidebar-sticky-wrap`}>
<div className={`${baseClass}__sidebar-fields`}>
<RenderFields
fieldSchema={fields}
fieldTypes={fieldTypes}
filter={(field) => field?.admin?.position === 'sidebar'}
filter={(field) => field?.admin?.position !== 'sidebar'}
permissions={permissions.fields}
readOnly={!hasSavePermission}
/>
</Gutter>
<Gutter className={`${baseClass}__payload-settings`}>
<h3>{t('general:payloadSettings')}</h3>
<div className={`${baseClass}__language`}>
<Label htmlFor="language-select" label={t('general:language')} />
<ReactSelect
inputId="language-select"
onChange={({ value }) => i18n.changeLanguage(value)}
options={languageOptions}
value={languageOptions.find(
(language) => language.value === i18n.language,
)}
/>
</div>
<ToggleTheme />
</Gutter>
</div>
</div>
<div className={`${baseClass}__sidebar-wrap`}>
<div className={`${baseClass}__sidebar`}>
<div className={`${baseClass}__sidebar-sticky-wrap`}>
<div className={`${baseClass}__sidebar-fields`}>
<RenderFields
fieldSchema={fields}
fieldTypes={fieldTypes}
filter={(field) => field?.admin?.position === 'sidebar'}
permissions={permissions.fields}
readOnly={!hasSavePermission}
/>
</div>
</div>
</div>
</div>
</div>
</Form>
</OperationContext.Provider>
</div>
)}
</React.Fragment>
)
</Form>
</OperationContext.Provider>
</div>
)}
</React.Fragment>
)
}
return null
}
export default DefaultAccount

View File

@@ -1,8 +1,6 @@
import React from 'react'
import { useTranslation } from 'react-i18next'
import type { Props } from './types'
import { getTranslation } from '../../../../utilities/getTranslation'
import { DocumentHeader } from '../../elements/DocumentHeader'
import { FormLoadingOverlayToggle } from '../../elements/Loading'
@@ -11,58 +9,67 @@ import { OperationContext } from '../../utilities/OperationProvider'
import { GlobalRoutes } from './Routes'
import { CustomGlobalComponent } from './Routes/CustomComponent'
import './index.scss'
import { EditViewProps } from '../types'
const baseClass = 'global-edit'
const DefaultGlobalView: React.FC<Props> = (props) => {
const {
action,
apiURL,
data,
disableRoutes,
global,
initialState,
isLoading,
onSave,
permissions,
} = props
const DefaultGlobalView: React.FC<
EditViewProps & {
disableRoutes?: boolean
}
> = (props) => {
if ('global' in props) {
const {
action,
apiURL,
data,
disableRoutes,
global,
initialState,
isLoading,
onSave,
permissions,
} = props
const { i18n } = useTranslation('general')
const { i18n } = useTranslation('general')
const { label } = global
const { label } = global
const hasSavePermission = permissions?.update?.permission
const hasSavePermission = permissions?.update?.permission
return (
<div className={baseClass}>
<OperationContext.Provider value="update">
<Form
action={action}
className={`${baseClass}__form`}
disabled={!hasSavePermission}
initialState={initialState}
method="post"
onSuccess={onSave}
>
<FormLoadingOverlayToggle
action="update"
loadingSuffix={getTranslation(label, i18n)}
name={`global-edit--${typeof label === 'string' ? label : label?.en}`}
/>
{!isLoading && (
<React.Fragment>
<DocumentHeader apiURL={apiURL} data={data} global={global} />
{disableRoutes ? (
<CustomGlobalComponent view="Default" {...props} />
) : (
<GlobalRoutes {...props} />
)}
</React.Fragment>
)}
</Form>
</OperationContext.Provider>
</div>
)
return (
<div className={baseClass}>
<OperationContext.Provider value="update">
<Form
action={action}
className={`${baseClass}__form`}
disabled={!hasSavePermission}
initialState={initialState}
method="post"
onSuccess={onSave}
>
<FormLoadingOverlayToggle
action="update"
loadingSuffix={getTranslation(label, i18n)}
name={`global-edit--${typeof label === 'string' ? label : label?.en}`}
/>
{!isLoading && (
<React.Fragment>
<DocumentHeader apiURL={apiURL} data={data} global={global} />
{disableRoutes ? (
<CustomGlobalComponent view="Default" {...props} />
) : (
<GlobalRoutes {...props} />
)}
</React.Fragment>
)}
</Form>
</OperationContext.Provider>
</div>
)
}
return null
}
export default DefaultGlobalView

View File

@@ -1,8 +1,6 @@
import React from 'react'
import { useTranslation } from 'react-i18next'
import type { Props } from '../types'
import { getTranslation } from '../../../../../utilities/getTranslation'
import { DocumentControls } from '../../../elements/DocumentControls'
import { Gutter } from '../../../elements/Gutter'
@@ -13,91 +11,96 @@ import { fieldTypes } from '../../../forms/field-types'
import LeaveWithoutSaving from '../../../modals/LeaveWithoutSaving'
import Meta from '../../../utilities/Meta'
import './index.scss'
import { EditViewProps } from '../../types'
const baseClass = 'global-edit'
export const DefaultGlobalEdit: React.FC<Props> = (props) => {
const { apiURL, data, global, permissions } = props
export const DefaultGlobalEdit: React.FC<EditViewProps> = (props) => {
if ('global' in props) {
const { apiURL, data, global, permissions } = props
const { i18n } = useTranslation('general')
const { i18n } = useTranslation('general')
const { admin: { description } = {}, fields, label } = global
const { admin: { description } = {}, fields, label } = global
const hasSavePermission = permissions?.update?.permission
const hasSavePermission = permissions?.update?.permission
const sidebarFields = filterFields({
fieldSchema: fields,
fieldTypes,
filter: (field) => field?.admin?.position === 'sidebar',
permissions: permissions.fields,
readOnly: !hasSavePermission,
})
const sidebarFields = filterFields({
fieldSchema: fields,
fieldTypes,
filter: (field) => field?.admin?.position === 'sidebar',
permissions: permissions.fields,
readOnly: !hasSavePermission,
})
const hasSidebar = sidebarFields && sidebarFields.length > 0
const hasSidebar = sidebarFields && sidebarFields.length > 0
return (
<React.Fragment>
{/* <SetStepNav collection={collection} id={id} isEditing={isEditing} /> */}
<DocumentControls
apiURL={apiURL}
data={data}
global={global}
hasSavePermission={hasSavePermission}
isEditing
permissions={permissions}
/>
<div
className={[`${baseClass}__wrapper`, hasSidebar && `${baseClass}__wrapper--has-sidebar`]
.filter(Boolean)
.join(' ')}
>
<div className={`${baseClass}__main`}>
<Meta
description={getTranslation(label, i18n)}
keywords={`${getTranslation(label, i18n)}, Payload, CMS`}
title={getTranslation(label, i18n)}
/>
{!(global.versions?.drafts && global.versions?.drafts?.autosave) && (
<LeaveWithoutSaving />
)}
<Gutter className={`${baseClass}__edit`}>
<header className={`${baseClass}__header`}>
{description && (
<div className={`${baseClass}__sub-header`}>
<ViewDescription description={description} />
</div>
)}
</header>
<RenderFields
fieldSchema={fields}
fieldTypes={fieldTypes}
filter={(field) =>
!field.admin.position ||
(field.admin.position && field.admin.position !== 'sidebar')
}
permissions={permissions.fields}
readOnly={!hasSavePermission}
return (
<React.Fragment>
{/* <SetStepNav collection={collection} id={id} isEditing={isEditing} /> */}
<DocumentControls
apiURL={apiURL}
data={data}
global={global}
hasSavePermission={hasSavePermission}
isEditing
permissions={permissions}
/>
<div
className={[`${baseClass}__wrapper`, hasSidebar && `${baseClass}__wrapper--has-sidebar`]
.filter(Boolean)
.join(' ')}
>
<div className={`${baseClass}__main`}>
<Meta
description={getTranslation(label, i18n)}
keywords={`${getTranslation(label, i18n)}, Payload, CMS`}
title={getTranslation(label, i18n)}
/>
</Gutter>
</div>
{hasSidebar && (
<div className={`${baseClass}__sidebar-wrap`}>
<div className={`${baseClass}__sidebar`}>
<div className={`${baseClass}__sidebar-sticky-wrap`}>
<div className={`${baseClass}__sidebar-fields`}>
<RenderFields
fieldSchema={fields}
fieldTypes={fieldTypes}
filter={(field) => field.admin.position === 'sidebar'}
permissions={permissions.fields}
readOnly={!hasSavePermission}
/>
{!(global.versions?.drafts && global.versions?.drafts?.autosave) && (
<LeaveWithoutSaving />
)}
<Gutter className={`${baseClass}__edit`}>
<header className={`${baseClass}__header`}>
{description && (
<div className={`${baseClass}__sub-header`}>
<ViewDescription description={description} />
</div>
)}
</header>
<RenderFields
fieldSchema={fields}
fieldTypes={fieldTypes}
filter={(field) =>
!field.admin.position ||
(field.admin.position && field.admin.position !== 'sidebar')
}
permissions={permissions.fields}
readOnly={!hasSavePermission}
/>
</Gutter>
</div>
{hasSidebar && (
<div className={`${baseClass}__sidebar-wrap`}>
<div className={`${baseClass}__sidebar`}>
<div className={`${baseClass}__sidebar-sticky-wrap`}>
<div className={`${baseClass}__sidebar-fields`}>
<RenderFields
fieldSchema={fields}
fieldTypes={fieldTypes}
filter={(field) => field.admin.position === 'sidebar'}
permissions={permissions.fields}
readOnly={!hasSavePermission}
/>
</div>
</div>
</div>
</div>
</div>
)}
</div>
</React.Fragment>
)
)}
</div>
</React.Fragment>
)
}
return null
}

View File

@@ -1,10 +1,9 @@
import React from 'react'
import type { Props } from '../types'
import VersionView from '../../Version/Version'
import VersionsView from '../../Versions'
import { DefaultGlobalEdit } from '../Default/index'
import { EditViewProps } from '../../types'
export type globalViewType =
| 'API'
@@ -28,30 +27,36 @@ export const defaultGlobalViews: {
}
export const CustomGlobalComponent = (
args: Props & {
args: EditViewProps & {
view: globalViewType
},
) => {
const { global, view } = args
if ('global' in args) {
const { global, view } = args
const { admin: { components: { views: { Edit } = {} } = {} } = {} } = global
const { admin: { components: { views: { Edit } = {} } = {} } = {} } = global
// Overriding components may come from multiple places in the config
// Need to cascade through the hierarchy to find the correct component to render
// For example, the Edit view:
// 1. Edit?.Default
// 2. Edit?.Default?.Component
const Component =
typeof Edit === 'object' && typeof Edit[view] === 'function'
? Edit[view]
: typeof Edit === 'object' &&
typeof Edit?.[view] === 'object' &&
typeof Edit[view].Component === 'function'
? Edit[view].Component
: defaultGlobalViews[view]
// Overriding components may come from multiple places in the config
// Need to cascade through the hierarchy to find the correct component to render
// For example, the Edit view:
// 1. Edit?.Default
// 2. Edit?.Default?.Component
// TODO: Remove the `@ts-ignore` when a Typescript wizard arrives
// For some reason `Component` does not exist on type `Edit[view]` no matter how narrow the type is
const Component =
typeof Edit === 'object' && typeof Edit[view] === 'function'
? Edit[view]
: typeof Edit === 'object' &&
typeof Edit?.[view] === 'object' &&
// @ts-ignore
typeof Edit[view].Component === 'function'
? // @ts-ignore
Edit[view].Component
: defaultGlobalViews[view]
if (Component) {
return <Component {...args} />
if (Component) {
return <Component {...args} />
}
}
return null

View File

@@ -5,60 +5,64 @@ import { Route, Switch, useRouteMatch } from 'react-router-dom'
import { useAuth } from '../../../utilities/Auth'
import { useConfig } from '../../../utilities/Config'
import NotFound from '../../NotFound'
import { type Props } from '../types'
import { CustomGlobalComponent } from './CustomComponent'
import { globalCustomRoutes } from './custom'
import { EditViewProps } from '../../types'
// @ts-expect-error Just TypeScript being broken // TODO: Open TypeScript issue
const Unauthorized = lazy(() => import('../../Unauthorized'))
export const GlobalRoutes: React.FC<Props> = (props) => {
const { global, permissions } = props
export const GlobalRoutes: React.FC<EditViewProps> = (props) => {
if ('global' in props) {
const { global, permissions } = props
const match = useRouteMatch()
const match = useRouteMatch()
const {
routes: { admin: adminRoute },
} = useConfig()
const {
routes: { admin: adminRoute },
} = useConfig()
const { user } = useAuth()
const { user } = useAuth()
return (
<Switch>
<Route
exact
key={`${global.slug}-versions`}
path={`${adminRoute}/globals/${global.slug}/versions`}
>
{permissions?.readVersions?.permission ? (
<CustomGlobalComponent view="Versions" {...props} />
) : (
<Unauthorized />
)}
</Route>
<Route
exact
key={`${global.slug}-view-version`}
path={`${adminRoute}/globals/${global.slug}/versions/:versionID`}
>
{permissions?.readVersions?.permission ? (
<CustomGlobalComponent view="Version" {...props} />
) : (
<Unauthorized />
)}
</Route>
{globalCustomRoutes({
global,
match,
permissions,
user,
})}
<Route exact key={`${global.slug}-view`} path={`${adminRoute}/globals/${global.slug}`}>
<CustomGlobalComponent view="Default" {...props} />
</Route>
<Route path={`${match.url}*`}>
<NotFound marginTop="large" />
</Route>
</Switch>
)
return (
<Switch>
<Route
exact
key={`${global.slug}-versions`}
path={`${adminRoute}/globals/${global.slug}/versions`}
>
{permissions?.readVersions?.permission ? (
<CustomGlobalComponent view="Versions" {...props} />
) : (
<Unauthorized />
)}
</Route>
<Route
exact
key={`${global.slug}-view-version`}
path={`${adminRoute}/globals/${global.slug}/versions/:versionID`}
>
{permissions?.readVersions?.permission ? (
<CustomGlobalComponent view="Version" {...props} />
) : (
<Unauthorized />
)}
</Route>
{globalCustomRoutes({
global,
match,
permissions,
user,
})}
<Route exact key={`${global.slug}-view`} path={`${adminRoute}/globals/${global.slug}`}>
<CustomGlobalComponent view="Default" {...props} />
</Route>
<Route path={`${match.url}*`}>
<NotFound marginTop="large" />
</Route>
</Switch>
)
}
return null
}

View File

@@ -16,8 +16,11 @@ import { usePreferences } from '../../utilities/Preferences'
import RenderCustomComponent from '../../utilities/RenderCustomComponent'
import DefaultGlobalView from './Default'
import { EditDepthContext } from '../../utilities/EditDepth'
import { EditViewProps } from '../types'
const GlobalView: React.FC<IndexProps> = (props) => {
const { global } = props
const { state: locationState } = useLocation<{ data?: Record<string, unknown> }>()
const { code: locale } = useLocale()
const { setStepNav } = useStepNav()
@@ -34,8 +37,6 @@ const GlobalView: React.FC<IndexProps> = (props) => {
serverURL,
} = useConfig()
const { global } = props
const {
admin: { components: { views: { Edit: Edit } = {} } = {} } = {},
fields,
@@ -101,26 +102,28 @@ const GlobalView: React.FC<IndexProps> = (props) => {
const isLoading = !initialState || !docPermissions || isLoadingData
const componentProps: EditViewProps = {
action: `${serverURL}${api}/globals/${slug}?locale=${locale}&fallback-locale=null`,
apiURL: `${serverURL}${api}/globals/${slug}?locale=${locale}${
global.versions?.drafts ? '&draft=true' : ''
}`,
canAccessAdmin: permissions?.canAccessAdmin,
data: dataToRender,
global,
initialState,
isLoading,
onSave,
permissions: docPermissions,
updatedAt: updatedAt || dataToRender?.updatedAt,
user,
}
return (
<EditDepthContext.Provider value={1}>
<RenderCustomComponent
CustomComponent={typeof Edit === 'function' ? Edit : undefined}
DefaultComponent={DefaultGlobalView}
componentProps={{
action: `${serverURL}${api}/globals/${slug}?locale=${locale}&fallback-locale=null`,
apiURL: `${serverURL}${api}/globals/${slug}?locale=${locale}${
global.versions?.drafts ? '&draft=true' : ''
}`,
canAccessAdmin: permissions?.canAccessAdmin,
data: dataToRender,
global,
initialState,
isLoading,
onSave,
permissions: docPermissions,
updatedAt: updatedAt || dataToRender?.updatedAt,
user,
}}
componentProps={componentProps}
/>
</EditDepthContext.Provider>
)

View File

@@ -1,22 +1,5 @@
import type { GlobalPermission } from '../../../../auth/types'
import type { SanitizedGlobalConfig } from '../../../../globals/config/types'
import type { Document } from '../../../../types'
import type { Fields } from '../../forms/Form/types'
export type IndexProps = {
global: SanitizedGlobalConfig
}
export type Props = {
action: string
apiURL: string
autosaveEnabled: boolean
data: Document
disableRoutes?: boolean
global: SanitizedGlobalConfig
initialState: Fields
isLoading: boolean
onSave: () => void
permissions: GlobalPermission
updatedAt: string
}

View File

@@ -1,8 +1,6 @@
import React, { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import type { Props } from './types'
import { getTranslation } from '../../../../../utilities/getTranslation'
import { DocumentHeader } from '../../../elements/DocumentHeader'
import { FormLoadingOverlayToggle } from '../../../elements/Loading'
@@ -12,95 +10,105 @@ import { OperationContext } from '../../../utilities/OperationProvider'
import { CollectionRoutes } from './Routes'
import { CustomCollectionComponent } from './Routes/CustomComponent'
import './index.scss'
import { EditViewProps } from '../../types'
const baseClass = 'collection-edit'
const DefaultEditView: React.FC<Props> = (props) => {
const DefaultEditView: React.FC<
EditViewProps & {
disableRoutes?: boolean
customHeader?: React.ReactNode
}
> = (props) => {
const { i18n } = useTranslation('general')
const { refreshCookieAsync, user } = useAuth()
const {
id,
action,
apiURL,
collection,
customHeader,
data,
disableRoutes,
hasSavePermission,
internalState,
isEditing,
isLoading,
onSave: onSaveFromProps,
} = props
if ('collection' in props) {
const {
id,
action,
apiURL,
collection,
customHeader,
data,
disableRoutes,
hasSavePermission,
internalState,
isEditing,
isLoading,
onSave: onSaveFromProps,
} = props
const { auth } = collection
const { auth } = collection
const classes = [baseClass, isEditing && `${baseClass}--is-editing`].filter(Boolean).join(' ')
const classes = [baseClass, isEditing && `${baseClass}--is-editing`].filter(Boolean).join(' ')
const onSave = useCallback(
async (json) => {
if (auth && id === user.id) {
await refreshCookieAsync()
}
const onSave = useCallback(
async (json) => {
if (auth && id === user.id) {
await refreshCookieAsync()
}
if (typeof onSaveFromProps === 'function') {
onSaveFromProps({
...json,
operation: id ? 'update' : 'create',
})
}
},
[id, onSaveFromProps, auth, user, refreshCookieAsync],
)
if (typeof onSaveFromProps === 'function') {
onSaveFromProps({
...json,
operation: id ? 'update' : 'create',
})
}
},
[id, onSaveFromProps, auth, user, refreshCookieAsync],
)
const operation = isEditing ? 'update' : 'create'
const operation = isEditing ? 'update' : 'create'
return (
<React.Fragment>
<div className={classes}>
<OperationContext.Provider value={operation}>
<Form
action={action}
className={`${baseClass}__form`}
disabled={!hasSavePermission}
initialState={internalState}
method={id ? 'patch' : 'post'}
onSuccess={onSave}
>
<FormLoadingOverlayToggle
action={isLoading ? 'loading' : operation}
formIsLoading={isLoading}
loadingSuffix={getTranslation(collection.labels.singular, i18n)}
name={`collection-edit--${
typeof collection?.labels?.singular === 'string'
? collection.labels.singular
: 'document'
}`}
type="withoutNav"
/>
{!isLoading && (
<React.Fragment>
<DocumentHeader
apiURL={apiURL}
collection={collection}
customHeader={customHeader}
data={data}
id={id}
isEditing={isEditing}
/>
{disableRoutes ? (
<CustomCollectionComponent view="Default" {...props} />
) : (
<CollectionRoutes {...props} />
)}
</React.Fragment>
)}
</Form>
</OperationContext.Provider>
</div>
</React.Fragment>
)
return (
<React.Fragment>
<div className={classes}>
<OperationContext.Provider value={operation}>
<Form
action={action}
className={`${baseClass}__form`}
disabled={!hasSavePermission}
initialState={internalState}
method={id ? 'patch' : 'post'}
onSuccess={onSave}
>
<FormLoadingOverlayToggle
action={isLoading ? 'loading' : operation}
formIsLoading={isLoading}
loadingSuffix={getTranslation(collection.labels.singular, i18n)}
name={`collection-edit--${
typeof collection?.labels?.singular === 'string'
? collection.labels.singular
: 'document'
}`}
type="withoutNav"
/>
{!isLoading && (
<React.Fragment>
<DocumentHeader
apiURL={apiURL}
collection={collection}
customHeader={customHeader}
data={data}
id={id}
isEditing={isEditing}
/>
{disableRoutes ? (
<CustomCollectionComponent view="Default" {...props} />
) : (
<CollectionRoutes {...props} />
)}
</React.Fragment>
)}
</Form>
</OperationContext.Provider>
</div>
</React.Fragment>
)
}
return null
}
export default DefaultEditView

View File

@@ -1,8 +1,6 @@
import React, { Fragment } from 'react'
import { useTranslation } from 'react-i18next'
import type { Props } from '../types'
import { getTranslation } from '../../../../../../utilities/getTranslation'
import { DocumentControls } from '../../../../elements/DocumentControls'
import { Gutter } from '../../../../elements/Gutter'
@@ -15,105 +13,117 @@ import Auth from '../Auth'
import { SetStepNav } from '../SetStepNav'
import Upload from '../Upload'
import './index.scss'
import { EditViewProps } from '../../../types'
const baseClass = 'collection-edit'
export const DefaultCollectionEdit: React.FC<Props> = (props) => {
export const DefaultCollectionEdit: React.FC<
EditViewProps & {
disableLeaveWithoutSaving?: boolean
disableActions?: boolean
}
> = (props) => {
const { i18n, t } = useTranslation('general')
const {
id,
apiURL,
collection,
data,
disableActions,
disableLeaveWithoutSaving,
hasSavePermission,
internalState,
isEditing,
permissions,
} = props
if ('collection' in props) {
const {
id,
apiURL,
collection,
data,
disableActions,
disableLeaveWithoutSaving,
hasSavePermission,
internalState,
isEditing,
permissions,
} = props
const { auth, fields, upload } = collection
const { auth, fields, upload } = collection
const operation = isEditing ? 'update' : 'create'
const operation = isEditing ? 'update' : 'create'
const sidebarFields = filterFields({
fieldSchema: fields,
fieldTypes,
filter: (field) => field?.admin?.position === 'sidebar',
permissions: permissions.fields,
readOnly: !hasSavePermission,
})
const sidebarFields = filterFields({
fieldSchema: fields,
fieldTypes,
filter: (field) => field?.admin?.position === 'sidebar',
permissions: permissions.fields,
readOnly: !hasSavePermission,
})
const hasSidebar = sidebarFields && sidebarFields.length > 0
const hasSidebar = sidebarFields && sidebarFields.length > 0
return (
<Fragment>
<SetStepNav collection={collection} id={id} isEditing={isEditing} />
<DocumentControls
apiURL={apiURL}
collection={collection}
data={data}
disableActions={disableActions}
hasSavePermission={hasSavePermission}
id={id}
isEditing={isEditing}
permissions={permissions}
/>
<div
className={[`${baseClass}__wrapper`, hasSidebar && `${baseClass}__wrapper--has-sidebar`]
.filter(Boolean)
.join(' ')}
>
<div className={`${baseClass}__main`}>
<Meta
description={`${isEditing ? t('editing') : t('creating')} - ${getTranslation(
collection.labels.singular,
i18n,
)}`}
keywords={`${getTranslation(collection.labels.singular, i18n)}, Payload, CMS`}
title={`${isEditing ? t('editing') : t('creating')} - ${getTranslation(
collection.labels.singular,
i18n,
)}`}
/>
{!(collection.versions?.drafts && collection.versions?.drafts?.autosave) &&
!disableLeaveWithoutSaving && <LeaveWithoutSaving />}
<Gutter className={`${baseClass}__edit`}>
{auth && (
<Auth
collection={collection}
email={data?.email}
operation={operation}
readOnly={!hasSavePermission}
requirePassword={!isEditing}
useAPIKey={auth.useAPIKey}
verify={auth.verify}
/>
)}
{upload && <Upload collection={collection} data={data} internalState={internalState} />}
<RenderFields
fieldSchema={fields}
fieldTypes={fieldTypes}
filter={(field) => !field?.admin?.position || field?.admin?.position !== 'sidebar'}
permissions={permissions.fields}
readOnly={!hasSavePermission}
return (
<Fragment>
<SetStepNav collection={collection} id={id} isEditing={isEditing} />
<DocumentControls
apiURL={apiURL}
collection={collection}
data={data}
disableActions={disableActions}
hasSavePermission={hasSavePermission}
id={id}
isEditing={isEditing}
permissions={permissions}
/>
<div
className={[`${baseClass}__wrapper`, hasSidebar && `${baseClass}__wrapper--has-sidebar`]
.filter(Boolean)
.join(' ')}
>
<div className={`${baseClass}__main`}>
<Meta
description={`${isEditing ? t('editing') : t('creating')} - ${getTranslation(
collection.labels.singular,
i18n,
)}`}
keywords={`${getTranslation(collection.labels.singular, i18n)}, Payload, CMS`}
title={`${isEditing ? t('editing') : t('creating')} - ${getTranslation(
collection.labels.singular,
i18n,
)}`}
/>
</Gutter>
</div>
{hasSidebar && (
<div className={`${baseClass}__sidebar-wrap`}>
<div className={`${baseClass}__sidebar`}>
<div className={`${baseClass}__sidebar-sticky-wrap`}>
<div className={`${baseClass}__sidebar-fields`}>
<RenderFields fieldTypes={fieldTypes} fields={sidebarFields} />
{!(collection.versions?.drafts && collection.versions?.drafts?.autosave) &&
!disableLeaveWithoutSaving && <LeaveWithoutSaving />}
<Gutter className={`${baseClass}__edit`}>
{auth && (
<Auth
collection={collection}
email={data?.email}
operation={operation}
readOnly={!hasSavePermission}
requirePassword={!isEditing}
useAPIKey={auth.useAPIKey}
verify={auth.verify}
/>
)}
{upload && (
<Upload collection={collection} data={data} internalState={internalState} />
)}
<RenderFields
fieldSchema={fields}
fieldTypes={fieldTypes}
filter={(field) => !field?.admin?.position || field?.admin?.position !== 'sidebar'}
permissions={permissions.fields}
readOnly={!hasSavePermission}
/>
</Gutter>
</div>
{hasSidebar && (
<div className={`${baseClass}__sidebar-wrap`}>
<div className={`${baseClass}__sidebar`}>
<div className={`${baseClass}__sidebar-sticky-wrap`}>
<div className={`${baseClass}__sidebar-fields`}>
<RenderFields fieldTypes={fieldTypes} fields={sidebarFields} />
</div>
</div>
</div>
</div>
</div>
)}
</div>
</Fragment>
)
)}
</div>
</Fragment>
)
}
return null
}

View File

@@ -1,10 +1,9 @@
import React from 'react'
import type { Props } from '../types'
import VersionView from '../../../Version/Version'
import VersionsView from '../../../Versions'
import { DefaultCollectionEdit } from '../Default/index'
import { EditViewProps } from '../../../types'
export type collectionViewType =
| 'API'
@@ -28,30 +27,36 @@ export const defaultCollectionViews: {
}
export const CustomCollectionComponent = (
args: Props & {
args: EditViewProps & {
view: collectionViewType
},
) => {
const { collection, view } = args
if ('collection' in args) {
const { collection, view } = args
const { admin: { components: { views: { Edit } = {} } = {} } = {} } = collection
const { admin: { components: { views: { Edit } = {} } = {} } = {} } = collection
// Overriding components may come from multiple places in the config
// Need to cascade through the hierarchy to find the correct component to render
// For example, the Edit view:
// 1. Edit?.Default
// 2. Edit?.Default?.Component
const Component =
typeof Edit === 'object' && typeof Edit[view] === 'function'
? Edit[view]
: typeof Edit === 'object' &&
typeof Edit?.[view] === 'object' &&
typeof Edit[view].Component === 'function'
? Edit[view].Component
: defaultCollectionViews[view]
// Overriding components may come from multiple places in the config
// Need to cascade through the hierarchy to find the correct component to render
// For example, the Edit view:
// 1. Edit?.Default
// 2. Edit?.Default?.Component
// TODO: Remove the `@ts-ignore` when a Typescript wizard arrives
// For some reason `Component` does not exist on type `Edit[view]` no matter how narrow the type is
const Component =
typeof Edit === 'object' && typeof Edit[view] === 'function'
? Edit[view]
: typeof Edit === 'object' &&
typeof Edit?.[view] === 'object' &&
// @ts-ignore
typeof Edit[view].Component === 'function'
? // @ts-ignore
Edit[view].Component
: defaultCollectionViews[view]
if (Component) {
return <Component {...args} />
if (Component) {
return <Component {...args} />
}
}
return null

View File

@@ -5,64 +5,66 @@ import { Route, Switch, useRouteMatch } from 'react-router-dom'
import { useAuth } from '../../../../utilities/Auth'
import { useConfig } from '../../../../utilities/Config'
import NotFound from '../../../NotFound'
import { type Props } from '../types'
import { CustomCollectionComponent } from './CustomComponent'
import { collectionCustomRoutes } from './custom'
import { EditViewProps } from '../../../types'
// @ts-expect-error Just TypeScript being broken // TODO: Open TypeScript issue
const Unauthorized = lazy(() => import('../../../Unauthorized'))
export const CollectionRoutes: React.FC<Props> = (props) => {
const { collection, permissions } = props
export const CollectionRoutes: React.FC<EditViewProps> = (props) => {
if ('collection' in props) {
const { collection, permissions } = props
const match = useRouteMatch()
const match = useRouteMatch()
const {
routes: { admin: adminRoute },
} = useConfig()
const {
routes: { admin: adminRoute },
} = useConfig()
const { user } = useAuth()
const { user } = useAuth()
return (
<Switch>
<Route
exact
key={`${collection.slug}-versions`}
path={`${adminRoute}/collections/${collection.slug}/:id/versions`}
>
{permissions?.readVersions?.permission ? (
<CustomCollectionComponent view="Versions" {...props} />
) : (
<Unauthorized />
)}
</Route>
<Route
exact
key={`${collection.slug}-view-version`}
path={`${adminRoute}/collections/${collection.slug}/:id/versions/:versionID`}
>
{permissions?.readVersions?.permission ? (
<CustomCollectionComponent view="Version" {...props} />
) : (
<Unauthorized />
)}
</Route>
{collectionCustomRoutes({
collection,
match,
permissions,
user,
})}
<Route
exact
key={`${collection.slug}-view`}
path={`${adminRoute}/collections/${collection.slug}/:id`}
>
<CustomCollectionComponent view="Default" {...props} />
</Route>
<Route path={`${match.url}*`}>
<NotFound marginTop="large" />
</Route>
</Switch>
)
return (
<Switch>
<Route
exact
key={`${collection.slug}-versions`}
path={`${adminRoute}/collections/${collection.slug}/:id/versions`}
>
{permissions?.readVersions?.permission ? (
<CustomCollectionComponent view="Versions" {...props} />
) : (
<Unauthorized />
)}
</Route>
<Route
exact
key={`${collection.slug}-view-version`}
path={`${adminRoute}/collections/${collection.slug}/:id/versions/:versionID`}
>
{permissions?.readVersions?.permission ? (
<CustomCollectionComponent view="Version" {...props} />
) : (
<Unauthorized />
)}
</Route>
{collectionCustomRoutes({
collection,
match,
permissions,
user,
})}
<Route
exact
key={`${collection.slug}-view`}
path={`${adminRoute}/collections/${collection.slug}/:id`}
>
<CustomCollectionComponent view="Default" {...props} />
</Route>
<Route path={`${match.url}*`}>
<NotFound marginTop="large" />
</Route>
</Switch>
)
}
}

View File

@@ -17,6 +17,7 @@ import RenderCustomComponent from '../../../utilities/RenderCustomComponent'
import NotFound from '../../NotFound'
import DefaultEdit from './Default'
import formatFields from './formatFields'
import { EditViewProps } from '../../types'
const EditView: React.FC<IndexProps> = (props) => {
const { collection: incomingCollection, isEditing } = props
@@ -124,27 +125,29 @@ const EditView: React.FC<IndexProps> = (props) => {
const isLoading = !internalState || !docPermissions || isLoadingData
const componentProps: EditViewProps = {
id,
action,
apiURL,
canAccessAdmin: permissions?.canAccessAdmin,
collection,
data,
hasSavePermission,
internalState,
isEditing,
isLoading,
onSave,
permissions: docPermissions as CollectionPermission,
updatedAt: updatedAt || data?.updatedAt,
user,
}
return (
<EditDepthContext.Provider value={1}>
<RenderCustomComponent
CustomComponent={typeof Edit === 'function' ? Edit : undefined}
DefaultComponent={DefaultEdit}
componentProps={{
id,
action,
apiURL,
canAccessAdmin: permissions?.canAccessAdmin,
collection,
data,
hasSavePermission,
internalState,
isEditing,
isLoading,
onSave,
permissions: docPermissions,
updatedAt: updatedAt || data?.updatedAt,
user,
}}
componentProps={componentProps}
/>
</EditDepthContext.Provider>
)

View File

@@ -1,36 +1,6 @@
import type React from 'react'
import type { CollectionPermission } from '../../../../../auth/types'
import type { SanitizedCollectionConfig } from '../../../../../collections/config/types'
import type { Document } from '../../../../../types'
import type { Fields } from '../../../forms/Form/types'
export type IndexProps = {
collection: SanitizedCollectionConfig
isEditing?: boolean
}
export type Props = IndexProps & {
action: string
apiURL: string
autosaveEnabled: boolean
customHeader?: React.ReactNode
data: Document
disableActions?: boolean
disableLeaveWithoutSaving?: boolean
disableRoutes?: boolean
hasSavePermission: boolean
id?: string
internalState?: Fields
isLoading: boolean
onSave?: (
json: Record<string, unknown> & {
collectionConfig: SanitizedCollectionConfig
doc: Record<string, any>
message: string
operation: 'create' | 'update'
},
) => void
permissions: CollectionPermission
updatedAt?: string
}

View File

@@ -0,0 +1,28 @@
import { CollectionPermission, GlobalPermission, User } from '../../../auth'
import { Fields, SanitizedCollectionConfig, SanitizedGlobalConfig } from '../../../exports/types'
export type EditViewProps = (
| {
id: string
collection: SanitizedCollectionConfig
hasSavePermission: boolean
isEditing: boolean
internalState: Fields
initialState?: Fields
permissions: CollectionPermission
}
| {
global: SanitizedGlobalConfig
initialState: Fields
permissions: GlobalPermission
}
) & {
isLoading: boolean
onSave: (json: any) => void
updatedAt: string
data: any
user: User
canAccessAdmin: boolean
action: string
apiURL: string
}

View File

@@ -4,22 +4,27 @@ import type { GraphQLInputObjectType, GraphQLNonNull, GraphQLObjectType } from '
import type { Config as GeneratedTypes } from 'payload/generated-types'
import type { DeepRequired } from 'ts-essentials'
import type { DocumentTab } from '../../admin/components/elements/DocumentHeader/Tabs/types'
import type {
CustomPreviewButtonProps,
CustomPublishButtonProps,
CustomSaveButtonProps,
CustomSaveDraftButtonProps,
} from '../../admin/components/elements/types'
import type { Props as EditProps } from '../../admin/components/views/collections/Edit/types'
import type { Props as ListProps } from '../../admin/components/views/collections/List/types'
import type { Auth, IncomingAuthType, User } from '../../auth/types'
import type { Access, Endpoint, EntityDescription, GeneratePreviewURL } from '../../config/types'
import type {
Access,
EditViewComponent,
Endpoint,
EntityDescription,
GeneratePreviewURL,
} from '../../config/types'
import type { PayloadRequest, RequestContext } from '../../express/types'
import type { Field } from '../../fields/config/types'
import type { IncomingUploadType, Upload } from '../../uploads/types'
import type { IncomingCollectionVersions, SanitizedCollectionVersions } from '../../versions/types'
import type { AfterOperationArg, AfterOperationMap } from '../operations/utils'
import type { EditView } from '../../config/types'
export type HookOperationType =
| 'autosave'
@@ -165,18 +170,6 @@ type BeforeDuplicateArgs<T> = {
export type BeforeDuplicate<T = any> = (args: BeforeDuplicateArgs<T>) => Promise<T> | T
export type CollectionEditViewConfig = {
/**
* The component to render for this view
* + Replaces the default component
*/
Component: React.ComponentType<EditProps>
Tab: DocumentTab
path: string
}
export type CollectionEditView = CollectionEditViewConfig | React.ComponentType<EditProps>
export type CollectionAdminOptions = {
/**
* Custom admin components
@@ -213,32 +206,33 @@ export type CollectionAdminOptions = {
}
views?: {
/**
* Replaces the "Edit" view entirely
* Set to a React component to replace the entire "Edit" view, including all nested routes.
* Set to an object to replace or modify individual nested routes, or to add new ones.
*/
Edit?:
| {
/**
* Replaces or adds nested views within the "Edit" view
* Replace or modify individual nested routes, or add new ones:
* + `Default` - `/admin/collections/:collection/:id`
* + `API` - `/admin/collections/:collection/:id/api`
* + `Preview` - `/admin/collections/:collection/:id/preview`
* + `LivePreview` - `/admin/collections/:collection/:id/preview`
* + `References` - `/admin/collections/:collection/:id/references`
* + `Relationships` - `/admin/collections/:collection/:id/relationships`
* + `Versions` - `/admin/collections/:collection/:id/versions`
* + `Version` - `/admin/collections/:collection/:id/versions/:version`
* + `:path` - `/admin/collections/:collection/:id/:path`
*/
Default: CollectionEditView
Versions?: CollectionEditView
Default?: EditView
Versions?: EditView
[key: string]: EditView
// TODO: uncomment these as they are built
// [key: string]: CollectionEditView
// API?: CollectionEditView
// Preview?: CollectionEditView
// References?: CollectionEditView
// Relationships?: CollectionEditView
// Version: CollectionEditView
// API?: EditView
// LivePreview?: EditView
// References?: EditView
// Relationships?: EditView
// Version: EditView
}
| React.ComponentType<EditProps>
| EditViewComponent
List?: React.ComponentType<ListProps>
}
}

View File

@@ -22,6 +22,8 @@ import type { PayloadRequest } from '../express/types'
import type { GlobalConfig, SanitizedGlobalConfig } from '../globals/config/types'
import type { Payload } from '../payload'
import type { Where } from '../types'
import { DocumentTab } from '../admin/components/elements/DocumentHeader/Tabs/types'
import { EditViewProps } from '../admin/components/views/types'
type Prettify<T> = {
[K in keyof T]: T[K]
@@ -201,6 +203,20 @@ export type CustomAdminView = React.ComponentType<{
user: User
}>
export type EditViewConfig = {
/**
* The component to render for this view
* + Replaces the default component
*/
Component: EditViewComponent
Tab: DocumentTab
path: string
}
export type EditViewComponent = React.ComponentType<EditViewProps>
export type EditView = EditViewConfig | EditViewComponent
export type AdminRoute = {
Component: CustomAdminView
/** Whether the path should be matched exactly or as a prefix */

View File

@@ -1,2 +1 @@
export { default as Edit } from '../../../admin/components/views/collections/Edit/Default'
export type { Props } from '../../../admin/components/views/collections/Edit/types'

View File

@@ -1,8 +1,6 @@
import type { GraphQLNonNull, GraphQLObjectType } from 'graphql'
import type React from 'react'
import type { DeepRequired } from 'ts-essentials'
import type { DocumentTab } from '../../admin/components/elements/DocumentHeader/Tabs/types'
import type {
CustomPreviewButtonProps,
CustomPublishButtonProps,
@@ -10,10 +8,18 @@ import type {
CustomSaveDraftButtonProps,
} from '../../admin/components/elements/types'
import type { User } from '../../auth/types'
import type { Access, Endpoint, EntityDescription, GeneratePreviewURL } from '../../config/types'
import type {
Access,
EditView,
EditViewComponent,
Endpoint,
EntityDescription,
GeneratePreviewURL,
} from '../../config/types'
import type { PayloadRequest } from '../../express/types'
import type { Field } from '../../fields/config/types'
import type { Where } from '../../types'
import type { IncomingGlobalVersions, SanitizedGlobalVersions } from '../../versions/types'
export type TypeWithID = {
@@ -39,18 +45,6 @@ export type AfterReadHook = (args: {
req: PayloadRequest
}) => any
export type GlobalEditViewConfig = {
/**
* The component to render for this view
* + Replaces the default component
*/
Component: React.ComponentType<any>
Tab?: DocumentTab
path: string
}
export type GlobalEditView = GlobalEditViewConfig | React.ComponentType<any>
export type GlobalAdminOptions = {
/**
* Custom admin components
@@ -80,32 +74,33 @@ export type GlobalAdminOptions = {
}
views?: {
/**
* Replaces the "Edit" view
* Set to a React component to replace the entire "Edit" view, including all nested routes.
* Set to an object to replace or modify individual nested routes, or to add new ones.
*/
Edit?:
| {
/**
* Replaces or adds nested routes within the "Edit" view
* Replace or modify individual nested routes, or add new ones:
* + `Default` - `/admin/globals/:slug`
* + `API` - `/admin/globals/:id/api`
* + `Preview` - `/admin/globals/:id/preview`
* + `LivePreview` - `/admin/globals/:id/preview`
* + `References` - `/admin/globals/:id/references`
* + `Relationships` - `/admin/globals/:id/relationships`
* + `Versions` - `/admin/globals/:id/versions`
* + `Version` - `/admin/globals/:id/versions/:version`
* + `:path` - `/admin/globals/:id/:path`
*/
Default: GlobalEditView
Versions?: GlobalEditView
Default?: EditView
Versions?: EditView
[name: string]: EditView
// TODO: uncomment these as they are built
// [name: string]: GlobalEditView
// API?: GlobalEditView
// Preview?: GlobalEditView
// References?: GlobalEditView
// Relationships?: GlobalEditView
// Version?: GlobalEditView
// API?: EditView
// LivePreview?: EditView
// References?: EditView
// Relationships?: EditView
// Version?: EditView
}
| React.ComponentType<any>
| EditViewComponent
}
}
/**

View File

@@ -3,9 +3,9 @@ import { Redirect } from 'react-router-dom'
import { useStepNav } from '../../../../../packages/payload/src/admin/components/elements/StepNav'
import { useConfig } from '../../../../../packages/payload/src/admin/components/utilities/Config'
import { type CustomAdminView } from '../../../../../packages/payload/src/config/types'
import { EditViewComponent } from '../../../../../packages/payload/src/config/types'
const CustomDefaultView: CustomAdminView = ({
const CustomDefaultView: EditViewComponent = ({
canAccessAdmin,
// collection,
// global,

View File

@@ -3,9 +3,9 @@ import { Redirect } from 'react-router-dom'
import { useStepNav } from '../../../../../packages/payload/src/admin/components/elements/StepNav'
import { useConfig } from '../../../../../packages/payload/src/admin/components/utilities/Config'
import { type CustomAdminView } from '../../../../../packages/payload/src/config/types'
import { EditViewComponent } from '../../../../../packages/payload/src/config/types'
const CustomEditView: CustomAdminView = ({
const CustomEditView: EditViewComponent = ({
canAccessAdmin,
// collection,
// global,

View File

@@ -3,9 +3,9 @@ import { Redirect } from 'react-router-dom'
import { useStepNav } from '../../../../../packages/payload/src/admin/components/elements/StepNav'
import { useConfig } from '../../../../../packages/payload/src/admin/components/utilities/Config'
import { type CustomAdminView } from '../../../../../packages/payload/src/config/types'
import { EditViewComponent } from '../../../../../packages/payload/src/config/types'
const CustomVersionsView: CustomAdminView = ({
const CustomVersionsView: EditViewComponent = ({
canAccessAdmin,
// collection,
// global,

View File

@@ -1,9 +1,9 @@
import React, { Fragment, useEffect } from 'react'
import { useStepNav } from '../../../../../packages/payload/src/admin/components/elements/StepNav'
import { type CustomAdminView } from '../../../../../packages/payload/src/config/types'
import { type EditViewComponent } from '../../../../../packages/payload/src/config/types'
const CustomView: CustomAdminView = () => {
const CustomView: EditViewComponent = () => {
const { setStepNav } = useStepNav()
// This effect will only run one time and will allow us

View File

@@ -64,6 +64,7 @@ export default buildConfigWithDefaults({
},
},
localization: {
defaultLocale: 'en',
locales: ['en', 'es'],
},
collections: [