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:
@@ -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: {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -158,6 +158,7 @@ export const renderListHandler = async (args: {
|
||||
redirectAfterDelete,
|
||||
redirectAfterDuplicate,
|
||||
searchParams: {},
|
||||
viewType: 'list',
|
||||
})
|
||||
|
||||
return {
|
||||
|
||||
@@ -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,
|
||||
|
||||
47
packages/next/src/views/Root/attachViewActions.ts
Normal file
47
packages/next/src/views/Root/attachViewActions.ts
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
39
packages/next/src/views/Root/getDocumentViewInfo.ts
Normal file
39
packages/next/src/views/Root/getDocumentViewInfo.ts
Normal 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',
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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[]
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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',
|
||||
})
|
||||
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"references": [{ "path": "../payload" }, { "path": "../ui" }]
|
||||
"references": [{ "path": "../payload" }, { "path": "../ui"}]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user