feat(ui): exposes context of the view being rendered on the server (#10620)

### What?
Extends visibility into what view is being shown so custom components
have context as to where they are being rendered.

**This PR does not add React Context.**

### Why?
This was needed for the multi-tenant plugin where the selector is in the
navigation sidebar and has no way to know if it is being shown inside of
a document or the list view.

I assume other users may also want their server components to be aware
of where a component is rendering before hitting the client. An example
would be wanting to redirect on the server instead of on the client,
this is how multi-tenant redirects users from "global" enabled
collections to the document view.

### How?
Adds 2 new variables that are determined by the view being routed to.

`viewType` - which view is being rendered, ie `list`, `document`,
`version`, `account`, `verify`, `reset`
```ts
type ViewTypes =
  | 'account'
  | 'dashboard'
  | 'document'
  | 'list'
  | 'reset'
  | 'verify'
  | 'version'
```

`documentSubViewType` - which tells you what sub view you are on, ie
`api`, `livePreview`, `default`, `versions`
```ts
type DocumentSubViewTypes =
  | 'api' 
  | 'default' 
  | 'livePreview' 
  | 'version' 
  | 'versions'
```
This commit is contained in:
Jarrod Flesch
2025-01-16 15:44:09 -05:00
committed by GitHub
parent 5a9cf8979e
commit e80d67987e
17 changed files with 241 additions and 169 deletions

View File

@@ -18,7 +18,18 @@ import { DefaultNavClient } from './index.client.js'
export type NavProps = ServerProps
export const DefaultNav: React.FC<NavProps> = async (props) => {
const { i18n, locale, params, payload, permissions, searchParams, user, visibleEntities } = props
const {
documentSubViewType,
i18n,
locale,
params,
payload,
permissions,
searchParams,
user,
viewType,
visibleEntities,
} = props
if (!payload?.config) {
return null
@@ -60,6 +71,10 @@ export const DefaultNav: React.FC<NavProps> = async (props) => {
const navPreferences = await getNavPrefs({ payload, user })
const LogoutComponent = RenderServerComponent({
clientProps: {
documentSubViewType,
viewType,
},
Component: logout?.Button,
Fallback: Logout,
importMap: payload.importMap,
@@ -78,6 +93,10 @@ export const DefaultNav: React.FC<NavProps> = async (props) => {
<NavWrapper baseClass={baseClass}>
<nav className={`${baseClass}__wrap`}>
{RenderServerComponent({
clientProps: {
documentSubViewType,
viewType,
},
Component: beforeNavLinks,
importMap: payload.importMap,
serverProps: {
@@ -92,6 +111,10 @@ export const DefaultNav: React.FC<NavProps> = async (props) => {
})}
<DefaultNavClient groups={groups} navPreferences={navPreferences} />
{RenderServerComponent({
clientProps: {
documentSubViewType,
viewType,
},
Component: afterNavLinks,
importMap: payload.importMap,
serverProps: {

View File

@@ -1,4 +1,10 @@
import type { CustomComponent, ServerProps, VisibleEntities } from 'payload'
import type {
CustomComponent,
DocumentSubViewTypes,
ServerProps,
ViewTypes,
VisibleEntities,
} from 'payload'
import {
ActionsProvider,
@@ -8,10 +14,12 @@ import {
NavToggler,
} from '@payloadcms/ui'
import { RenderServerComponent } from '@payloadcms/ui/elements/RenderServerComponent'
import './index.scss'
import React from 'react'
import { DefaultNav } from '../../elements/Nav/index.js'
import './index.scss'
import { NavHamburger } from './NavHamburger/index.js'
import { Wrapper } from './Wrapper/index.js'
@@ -22,9 +30,10 @@ export type DefaultTemplateProps = {
className?: string
collectionSlug?: string
docID?: number | string
documentSubViewType?: DocumentSubViewTypes
globalSlug?: string
viewActions?: CustomComponent[]
viewType?: 'edit' | 'list'
viewType?: ViewTypes
visibleEntities: VisibleEntities
} & ServerProps
@@ -33,6 +42,7 @@ export const DefaultTemplate: React.FC<DefaultTemplateProps> = ({
className,
collectionSlug,
docID,
documentSubViewType,
globalSlug,
i18n,
locale,
@@ -56,6 +66,14 @@ export const DefaultTemplate: React.FC<DefaultTemplateProps> = ({
} = {},
} = payload.config || {}
const clientProps = React.useMemo(() => {
return {
documentSubViewType,
viewType,
visibleEntities,
}
}, [documentSubViewType, viewType, visibleEntities])
const serverProps = React.useMemo<ServerProps>(
() => ({
collectionSlug,
@@ -68,8 +86,6 @@ export const DefaultTemplate: React.FC<DefaultTemplateProps> = ({
permissions,
searchParams,
user,
viewType,
visibleEntities,
}),
[
i18n,
@@ -79,11 +95,9 @@ export const DefaultTemplate: React.FC<DefaultTemplateProps> = ({
permissions,
searchParams,
user,
visibleEntities,
globalSlug,
collectionSlug,
docID,
viewType,
],
)
@@ -96,12 +110,14 @@ export const DefaultTemplate: React.FC<DefaultTemplateProps> = ({
if (action) {
if (typeof action === 'object') {
acc[action.path] = RenderServerComponent({
clientProps,
Component: action,
importMap: payload.importMap,
serverProps,
})
} else {
acc[action] = RenderServerComponent({
clientProps,
Component: action,
importMap: payload.importMap,
serverProps,
@@ -113,10 +129,10 @@ export const DefaultTemplate: React.FC<DefaultTemplateProps> = ({
}, {})
: undefined,
}
}, [payload, serverProps, viewActions])
}, [payload, serverProps, viewActions, clientProps])
const NavComponent = RenderServerComponent({
clientProps: { clientProps: { visibleEntities } },
clientProps,
Component: CustomNav,
Fallback: DefaultNav,
importMap: payload.importMap,
@@ -128,7 +144,7 @@ export const DefaultTemplate: React.FC<DefaultTemplateProps> = ({
<BulkUploadProvider>
<ActionsProvider Actions={Actions}>
{RenderServerComponent({
clientProps: { clientProps: { visibleEntities } },
clientProps,
Component: CustomHeader,
importMap: payload.importMap,
serverProps,

View File

@@ -143,6 +143,7 @@ export const renderDocumentHandler = async (args: {
const { data, Document } = await renderDocument({
clientConfig,
disableActions,
documentSubViewType: 'default',
drawerSlug,
importMap: payload.importMap,
initialData,
@@ -167,6 +168,7 @@ export const renderDocumentHandler = async (args: {
redirectAfterDelete,
redirectAfterDuplicate,
searchParams: {},
viewType: 'document',
})
return {

View File

@@ -29,6 +29,7 @@ export const generateMetadata: GenerateEditViewMetadata = async (args) => getMet
// called on-demand from document drawers
export const renderDocument = async ({
disableActions,
documentSubViewType,
drawerSlug,
importMap,
initialData,
@@ -38,6 +39,7 @@ export const renderDocument = async ({
redirectAfterDelete,
redirectAfterDuplicate,
searchParams,
viewType,
}: {
overrideEntityVisibility?: boolean
} & AdminViewProps): Promise<{
@@ -316,7 +318,7 @@ export const renderDocument = async ({
req,
})
const clientProps = { formState, ...documentSlots }
const clientProps = { formState, ...documentSlots, documentSubViewType, viewType }
return {
data: doc,

View File

@@ -158,6 +158,7 @@ export const renderListHandler = async (args: {
redirectAfterDelete,
redirectAfterDuplicate,
searchParams: {},
viewType: 'list',
})
return {

View File

@@ -69,8 +69,6 @@ export const renderListView = async (
const query = queryFromArgs || queryFromReq
const preferenceKey = `${collectionSlug}-list`
const listPreferences = await upsertPreferences<ListPreferences>({
key: `${collectionSlug}-list`,
req,

View File

@@ -0,0 +1,47 @@
import type {
CustomComponent,
EditConfig,
SanitizedCollectionConfig,
SanitizedGlobalConfig,
ServerPropsFromView,
} from 'payload'
export function getViewActions({
editConfig,
viewKey,
}: {
editConfig: EditConfig
viewKey: keyof EditConfig
}): CustomComponent[] | undefined {
if (editConfig && viewKey in editConfig && 'actions' in editConfig[viewKey]) {
return editConfig[viewKey].actions
}
return undefined
}
export function attachViewActions({
collectionOrGlobal,
serverProps,
viewKeyArg,
}: {
collectionOrGlobal: SanitizedCollectionConfig | SanitizedGlobalConfig
serverProps: ServerPropsFromView
viewKeyArg?: keyof EditConfig
}) {
if (collectionOrGlobal?.admin?.components?.views?.edit) {
let viewKey = viewKeyArg || 'default'
if ('root' in collectionOrGlobal.admin.components.views.edit) {
viewKey = 'root'
}
const actions = getViewActions({
editConfig: collectionOrGlobal.admin?.components?.views?.edit,
viewKey,
})
if (actions) {
serverProps.viewActions = serverProps.viewActions.concat(actions)
}
}
}

View File

@@ -0,0 +1,39 @@
import type { DocumentSubViewTypes, ViewTypes } from 'payload'
export function getDocumentViewInfo(segments: string[]): {
documentSubViewType?: DocumentSubViewTypes
viewType: ViewTypes
} {
const [tabSegment, versionSegment] = segments
if (versionSegment) {
if (tabSegment === 'versions') {
return {
documentSubViewType: 'version',
viewType: 'version',
}
}
} else {
if (tabSegment === 'versions') {
return {
documentSubViewType: 'versions',
viewType: 'document',
}
} else if (tabSegment === 'preview') {
return {
documentSubViewType: 'livePreview',
viewType: 'document',
}
} else if (tabSegment === 'api') {
return {
documentSubViewType: 'api',
viewType: 'document',
}
}
}
return {
documentSubViewType: 'default',
viewType: 'document',
}
}

View File

@@ -1,10 +1,11 @@
import type {
AdminViewComponent,
AdminViewProps,
CustomComponent,
EditConfig,
DocumentSubViewTypes,
ImportMap,
SanitizedConfig,
ServerPropsFromView,
ViewTypes,
} from 'payload'
import type React from 'react'
@@ -23,7 +24,9 @@ import { LogoutInactivity, LogoutView } from '../Logout/index.js'
import { ResetPassword, resetPasswordBaseClass } from '../ResetPassword/index.js'
import { UnauthorizedView } from '../Unauthorized/index.js'
import { Verify, verifyBaseClass } from '../Verify/index.js'
import { attachViewActions, getViewActions } from './attachViewActions.js'
import { getCustomViewByRoute } from './getCustomViewByRoute.js'
import { getDocumentViewInfo } from './getDocumentViewInfo.js'
import { isPathMatchingRoute } from './isPathMatchingRoute.js'
const baseClasses = {
@@ -53,27 +56,6 @@ const oneSegmentViews: OneSegmentViews = {
unauthorized: UnauthorizedView,
}
function getViewActions({
editConfig,
viewKey,
}: {
editConfig: EditConfig
viewKey: keyof EditConfig
}): CustomComponent[] | undefined {
if (editConfig && viewKey in editConfig && 'actions' in editConfig[viewKey]) {
return editConfig[viewKey].actions
}
return undefined
}
type ServerPropsFromView = {
collectionConfig?: SanitizedConfig['collections'][number]
globalConfig?: SanitizedConfig['globals'][number]
viewActions: CustomComponent[]
viewType?: 'edit' | 'list'
}
type GetViewFromConfigArgs = {
adminRoute: string
config: SanitizedConfig
@@ -87,10 +69,12 @@ type GetViewFromConfigArgs = {
type GetViewFromConfigResult = {
DefaultView: ViewFromConfig
documentSubViewType?: DocumentSubViewTypes
initPageOptions: Parameters<typeof initPage>[0]
serverProps: ServerPropsFromView
templateClassName: string
templateType: 'default' | 'minimal'
viewType?: ViewTypes
}
export const getViewFromConfig = ({
@@ -104,6 +88,8 @@ export const getViewFromConfig = ({
let ViewToRender: ViewFromConfig = null
let templateClassName: string
let templateType: 'default' | 'minimal' | undefined
let documentSubViewType: DocumentSubViewTypes
let viewType: ViewTypes
const initPageOptions: Parameters<typeof initPage>[0] = {
config,
@@ -141,6 +127,7 @@ export const getViewFromConfig = ({
}
templateClassName = 'dashboard'
templateType = 'default'
viewType = 'dashboard'
}
break
}
@@ -181,6 +168,7 @@ export const getViewFromConfig = ({
if (viewKey === 'account') {
templateType = 'default'
viewType = 'account'
}
}
break
@@ -193,6 +181,7 @@ export const getViewFromConfig = ({
}
templateClassName = baseClasses[segmentTwo]
templateType = 'minimal'
viewType = 'reset'
}
if (isCollection && matchedCollection) {
@@ -204,7 +193,7 @@ export const getViewFromConfig = ({
templateClassName = `${segmentTwo}-list`
templateType = 'default'
serverProps.viewType = 'list'
viewType = 'list'
serverProps.viewActions = serverProps.viewActions.concat(
matchedCollection.admin.components?.views?.list?.actions,
)
@@ -217,6 +206,7 @@ export const getViewFromConfig = ({
templateClassName = 'global-edit'
templateType = 'default'
viewType = 'document'
// add default view actions
serverProps.viewActions = serverProps.viewActions.concat(
@@ -237,6 +227,7 @@ export const getViewFromConfig = ({
templateClassName = 'verify'
templateType = 'minimal'
viewType = 'verify'
} else if (isCollection && matchedCollection) {
// Custom Views
// --> /collections/:collectionSlug/:id
@@ -251,65 +242,16 @@ export const getViewFromConfig = ({
templateClassName = `collection-default-edit`
templateType = 'default'
serverProps.viewType = 'edit'
// Adds view actions to the current collection view
if (matchedCollection.admin?.components?.views?.edit) {
if ('root' in matchedCollection.admin.components.views.edit) {
serverProps.viewActions = serverProps.viewActions.concat(
getViewActions({
editConfig: matchedCollection.admin?.components?.views?.edit,
viewKey: 'root',
}),
)
} else {
if (segmentFive) {
if (segmentFour === 'versions') {
// add version view actions
serverProps.viewActions = serverProps.viewActions.concat(
getViewActions({
editConfig: matchedCollection.admin?.components?.views?.edit,
viewKey: 'version',
}),
)
}
} else if (segmentFour) {
if (segmentFour === 'versions') {
// add versions view actions
serverProps.viewActions = serverProps.viewActions.concat(
getViewActions({
editConfig: matchedCollection.admin?.components?.views.edit,
viewKey: 'versions',
}),
)
} else if (segmentFour === 'preview') {
// add livePreview view actions
serverProps.viewActions = serverProps.viewActions.concat(
getViewActions({
editConfig: matchedCollection.admin?.components?.views.edit,
viewKey: 'livePreview',
}),
)
} else if (segmentFour === 'api') {
// add api view actions
serverProps.viewActions = serverProps.viewActions.concat(
getViewActions({
editConfig: matchedCollection.admin?.components?.views.edit,
viewKey: 'api',
}),
)
}
} else if (segmentThree) {
// add default view actions
serverProps.viewActions = serverProps.viewActions.concat(
getViewActions({
editConfig: matchedCollection.admin?.components?.views.edit,
viewKey: 'default',
}),
)
}
}
}
const viewInfo = getDocumentViewInfo([segmentFour, segmentFive])
viewType = viewInfo.viewType
documentSubViewType = viewInfo.documentSubViewType
attachViewActions({
collectionOrGlobal: matchedCollection,
serverProps,
viewKeyArg: documentSubViewType,
})
} else if (isGlobal && matchedGlobal) {
// Custom Views
// --> /globals/:globalSlug/versions
@@ -324,55 +266,15 @@ export const getViewFromConfig = ({
templateClassName = `global-edit`
templateType = 'default'
// Adds view actions to the current global view
if (matchedGlobal.admin?.components?.views?.edit) {
if ('root' in matchedGlobal.admin.components.views.edit) {
serverProps.viewActions = serverProps.viewActions.concat(
getViewActions({
editConfig: matchedGlobal.admin.components?.views?.edit,
viewKey: 'root',
}),
)
} else {
if (segmentFour) {
if (segmentThree === 'versions') {
// add version view actions
serverProps.viewActions = serverProps.viewActions.concat(
getViewActions({
editConfig: matchedGlobal.admin?.components?.views?.edit,
viewKey: 'version',
}),
)
}
} else if (segmentThree) {
if (segmentThree === 'versions') {
// add versions view actions
serverProps.viewActions = serverProps.viewActions.concat(
getViewActions({
editConfig: matchedGlobal.admin?.components?.views?.edit,
viewKey: 'versions',
}),
)
} else if (segmentThree === 'preview') {
// add livePreview view actions
serverProps.viewActions = serverProps.viewActions.concat(
getViewActions({
editConfig: matchedGlobal.admin?.components?.views?.edit,
viewKey: 'livePreview',
}),
)
} else if (segmentThree === 'api') {
// add api view actions
serverProps.viewActions = serverProps.viewActions.concat(
getViewActions({
editConfig: matchedGlobal.admin?.components?.views?.edit,
viewKey: 'api',
}),
)
}
}
}
}
const viewInfo = getDocumentViewInfo([segmentThree, segmentFour])
viewType = viewInfo.viewType
documentSubViewType = viewInfo.documentSubViewType
attachViewActions({
collectionOrGlobal: matchedGlobal,
serverProps,
viewKeyArg: documentSubViewType,
})
}
break
}
@@ -385,9 +287,11 @@ export const getViewFromConfig = ({
return {
DefaultView: ViewToRender,
documentSubViewType,
initPageOptions,
serverProps,
templateClassName,
templateType,
viewType,
}
}

View File

@@ -58,15 +58,22 @@ export const RootPage = async ({
const searchParams = await searchParamsPromise
const { DefaultView, initPageOptions, serverProps, templateClassName, templateType } =
getViewFromConfig({
adminRoute,
config,
currentRoute,
importMap,
searchParams,
segments,
})
const {
DefaultView,
documentSubViewType,
initPageOptions,
serverProps,
templateClassName,
templateType,
viewType,
} = getViewFromConfig({
adminRoute,
config,
currentRoute,
importMap,
searchParams,
segments,
})
const initPageResult = await initPage(initPageOptions)
@@ -123,7 +130,7 @@ export const RootPage = async ({
})
const RenderedView = RenderServerComponent({
clientProps: { clientConfig },
clientProps: { clientConfig, documentSubViewType, viewType },
Component: DefaultView.payloadComponent,
Fallback: DefaultView.Component,
importMap,
@@ -150,6 +157,7 @@ export const RootPage = async ({
<DefaultTemplate
collectionSlug={initPageResult?.collectionConfig?.slug}
docID={initPageResult?.docID}
documentSubViewType={documentSubViewType}
globalSlug={initPageResult?.globalConfig?.slug}
i18n={initPageResult?.req.i18n}
locale={initPageResult?.locale}
@@ -159,7 +167,7 @@ export const RootPage = async ({
searchParams={searchParams}
user={initPageResult?.req.user}
viewActions={serverProps.viewActions}
viewType={serverProps.viewType}
viewType={viewType}
visibleEntities={{
// The reason we are not passing in initPageResult.visibleEntities directly is due to a "Cannot assign to read only property of object '#<Object>" error introduced in React 19
// which this caused as soon as initPageResult.visibleEntities is passed in

View File

@@ -4,7 +4,14 @@ import type { SanitizedPermissions } from '../../auth/index.js'
import type { ImportMap } from '../../bin/generateImportMap/index.js'
import type { SanitizedCollectionConfig } from '../../collections/config/types.js'
import type { ClientConfig } from '../../config/client.js'
import type { Locale, MetaConfig, PayloadComponent, ServerProps } from '../../config/types.js'
import type {
CustomComponent,
Locale,
MetaConfig,
PayloadComponent,
SanitizedConfig,
ServerProps,
} from '../../config/types.js'
import type { SanitizedGlobalConfig } from '../../globals/config/types.js'
import type { PayloadRequest } from '../../types/index.js'
import type { LanguageOptions } from '../LanguageOptions.js'
@@ -23,6 +30,7 @@ export type AdminViewConfig = {
export type AdminViewProps = {
readonly clientConfig: ClientConfig
readonly disableActions?: boolean
readonly documentSubViewType?: DocumentSubViewTypes
readonly drawerSlug?: string
readonly importMap: ImportMap
readonly initialData?: Data
@@ -31,6 +39,7 @@ export type AdminViewProps = {
readonly redirectAfterDelete?: boolean
readonly redirectAfterDuplicate?: boolean
readonly searchParams: { [key: string]: string | string[] | undefined }
readonly viewType: ViewTypes
}
export type AdminViewComponent = PayloadComponent<AdminViewProps>
@@ -67,3 +76,19 @@ export type ServerSideEditViewProps = {
ServerProps
export type ClientSideEditViewProps = {} & DocumentSlots
export type ViewTypes =
| 'account'
| 'dashboard'
| 'document'
| 'list'
| 'reset'
| 'verify'
| 'version'
export type DocumentSubViewTypes = 'api' | 'default' | 'livePreview' | 'version' | 'versions'
export type ServerPropsFromView = {
collectionConfig?: SanitizedConfig['collections'][number]
globalConfig?: SanitizedConfig['globals'][number]
viewActions: CustomComponent[]
}

View File

@@ -18,7 +18,10 @@ import type { RichTextAdapterProvider } from '../admin/RichText.js'
import type { DocumentTabConfig, RichTextAdapter } from '../admin/types.js'
import type {
AdminViewConfig,
DocumentSubViewTypes,
ServerPropsFromView,
ServerSideEditViewProps,
ViewTypes,
VisibleEntities,
} from '../admin/views/types.js'
import type { SanitizedPermissions } from '../auth/index.js'
@@ -400,6 +403,7 @@ type ClientProps = {
}
export type ServerProps = {
readonly documentSubViewType?: DocumentSubViewTypes
readonly i18n: I18nClient
readonly locale?: Locale
readonly params?: { [key: string]: string | string[] | undefined }
@@ -407,6 +411,7 @@ export type ServerProps = {
readonly permissions?: SanitizedPermissions
readonly searchParams?: { [key: string]: string | string[] | undefined }
readonly user?: TypedUser
readonly viewType?: ViewTypes
readonly visibleEntities?: VisibleEntities
} & ClientProps
@@ -1190,3 +1195,5 @@ export type EntityDescriptionFunction = ({ t }: { t: TFunction }) => string
export type EntityDescription = EntityDescriptionFunction | Record<string, string> | string
export type { EmailAdapter, SendEmailOptions }
export type { DocumentSubViewTypes, ServerPropsFromView, ViewTypes }

View File

@@ -1,4 +1,4 @@
import type { CollectionSlug, ServerProps } from 'payload'
import type { CollectionSlug, ServerProps, ViewTypes } from 'payload'
import { redirect } from 'next/navigation.js'
@@ -9,7 +9,7 @@ type Args = {
docID?: number | string
globalSlugs: string[]
tenantFieldName: string
viewType: 'edit' | 'list'
viewType: ViewTypes
} & ServerProps
export const GlobalViewRedirect = async (args: Args) => {

View File

@@ -1,14 +1,17 @@
'use client'
import type { ReactSelectOption } from '@payloadcms/ui'
import type { ViewTypes } from 'payload'
import { SelectInput } from '@payloadcms/ui'
import React from 'react'
import './index.scss'
import React from 'react'
import { SELECT_ALL } from '../../constants.js'
import { useTenantSelection } from '../../providers/TenantSelectionProvider/index.client.js'
export const TenantSelector = () => {
export const TenantSelector = ({ viewType }: { viewType?: ViewTypes }) => {
const { options, selectedTenantID, setTenant } = useTenantSelection()
const handleChange = React.useCallback(
@@ -29,6 +32,7 @@ export const TenantSelector = () => {
return (
<div className="tenant-selector">
<SelectInput
isClearable={viewType === 'list'}
label="Tenant"
name="setTenant"
onChange={handleChange}

View File

@@ -218,10 +218,6 @@ export const multiTenantPlugin =
* Add tenant selector to admin UI
*/
incomingConfig.admin.components.beforeNavLinks.push({
clientProps: {
tenantsCollectionSlug,
useAsTitle: tenantCollection.admin?.useAsTitle || 'id',
},
path: '@payloadcms/plugin-multi-tenant/client#TenantSelector',
})

View File

@@ -1,4 +1,4 @@
import type { Payload } from 'payload'
import type { Payload, ViewTypes } from 'payload'
import { headers as getHeaders } from 'next/headers.js'
@@ -10,7 +10,7 @@ type Args = {
payload: Payload
slug: string
tenantFieldName: string
view: 'edit' | 'list'
view: ViewTypes
}
export async function getGlobalViewRedirect({
slug,
@@ -41,7 +41,7 @@ export async function getGlobalViewRedirect({
const tenantDocID = docs?.[0]?.id
if (view === 'edit') {
if (view === 'document') {
if (docID && !tenantDocID) {
// viewing a document with an id but does not match the selected tenant, redirect to create route
redirectRoute = `${payload.config.routes.admin}/collections/${slug}/create`

View File

@@ -1,4 +1,4 @@
{
"extends": "../../tsconfig.base.json",
"references": [{ "path": "../payload" }, { "path": "../ui" }]
"references": [{ "path": "../payload" }, { "path": "../ui"}]
}