feat(next): export views, pass all props to custom dashboard view (#14094)
- Exports additional modules from `@payloadcms/next` like `DashboardView`, which are useful for when you provide your own view but want to render parts of the default view - Passes all props to custom dashboard views, giving you access to things like `req` - Makes use of `select` API in `payload.find` call of the dashboard view for improved performance
This commit is contained in:
@@ -64,6 +64,16 @@
|
||||
"import": "./src/exports/views.ts",
|
||||
"types": "./src/exports/views.ts",
|
||||
"default": "./src/exports/views.ts"
|
||||
},
|
||||
"./client": {
|
||||
"import": "./src/exports/client.ts",
|
||||
"types": "./src/exports/client.ts",
|
||||
"default": "./src/exports/client.ts"
|
||||
},
|
||||
"./rsc": {
|
||||
"import": "./src/exports/rsc.ts",
|
||||
"types": "./src/exports/rsc.ts",
|
||||
"default": "./src/exports/rsc.ts"
|
||||
}
|
||||
},
|
||||
"main": "./src/index.js",
|
||||
@@ -177,6 +187,16 @@
|
||||
"import": "./dist/exports/views.js",
|
||||
"types": "./dist/exports/views.d.ts",
|
||||
"default": "./dist/exports/views.js"
|
||||
},
|
||||
"./client": {
|
||||
"import": "./dist/exports/client.js",
|
||||
"types": "./dist/exports/client.d.ts",
|
||||
"default": "./dist/exports/client.js"
|
||||
},
|
||||
"./rsc": {
|
||||
"import": "./dist/exports/rsc.js",
|
||||
"types": "./dist/exports/rsc.d.ts",
|
||||
"default": "./dist/exports/rsc.js"
|
||||
}
|
||||
},
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@@ -13,6 +13,9 @@ import './index.scss'
|
||||
|
||||
const baseClass = `doc-header`
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const DocumentHeader: React.FC<{
|
||||
AfterHeader?: React.ReactNode
|
||||
collectionConfig?: SanitizedCollectionConfig
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
import { Hamburger, useNav } from '@payloadcms/ui'
|
||||
import React from 'react'
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const NavHamburger: React.FC<{
|
||||
baseClass?: string
|
||||
}> = ({ baseClass }) => {
|
||||
|
||||
@@ -4,6 +4,9 @@ import React from 'react'
|
||||
|
||||
import './index.scss'
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const NavWrapper: React.FC<{
|
||||
baseClass?: string
|
||||
children: React.ReactNode
|
||||
|
||||
@@ -12,6 +12,9 @@ import React, { Fragment } from 'react'
|
||||
|
||||
const baseClass = 'nav'
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const DefaultNavClient: React.FC<{
|
||||
groups: ReturnType<typeof groupNavItems>
|
||||
navPreferences: NavPreferences
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export { RootLayout } from './layouts/Root/index.js'
|
||||
export { Dashboard as DashboardPage } from './views/Dashboard/index.js'
|
||||
export { DashboardView } from './views/Dashboard/index.js'
|
||||
export { LoginView } from './views/Login/index.js'
|
||||
export { RootPage } from './views/Root/index.js'
|
||||
|
||||
5
packages/next/src/exports/client.ts
Normal file
5
packages/next/src/exports/client.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
'use client'
|
||||
|
||||
export { DefaultNavClient } from '../elements/Nav/index.client.js'
|
||||
export { NavHamburger } from '../elements/Nav/NavHamburger/index.js'
|
||||
export { NavWrapper } from '../elements/Nav/NavWrapper/index.js'
|
||||
3
packages/next/src/exports/rsc.ts
Normal file
3
packages/next/src/exports/rsc.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export { DocumentHeader } from '../elements/DocumentHeader/index.js'
|
||||
export { Logo } from '../elements/Logo/index.js'
|
||||
export { DefaultNav } from '../elements/Nav/index.js'
|
||||
@@ -1,3 +1,15 @@
|
||||
export { AccountView } from '../views/Account/index.js'
|
||||
export {
|
||||
type DashboardViewClientProps,
|
||||
type DashboardViewServerProps,
|
||||
type DashboardViewServerPropsOnly,
|
||||
DefaultDashboard,
|
||||
} from '../views/Dashboard/Default/index.js'
|
||||
export { DashboardView } from '../views/Dashboard/index.js'
|
||||
|
||||
export { ListView, renderListView, type RenderListViewArgs } from '../views/List/index.js'
|
||||
export { LoginView } from '../views/Login/index.js'
|
||||
export { NotFoundPage } from '../views/NotFound/index.js'
|
||||
|
||||
export { type GenerateViewMetadata, RootPage } from '../views/Root/index.js'
|
||||
export { generatePageMetadata } from '../views/Root/metadata.js'
|
||||
|
||||
@@ -16,7 +16,7 @@ import { EditView } from '../Edit/index.js'
|
||||
import { AccountClient } from './index.client.js'
|
||||
import { Settings } from './Settings/index.js'
|
||||
|
||||
export async function Account({ initPageResult, params, searchParams }: AdminViewServerProps) {
|
||||
export async function AccountView({ initPageResult, params, searchParams }: AdminViewServerProps) {
|
||||
const {
|
||||
languageOptions,
|
||||
locale,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { groupNavItems } from '@payloadcms/ui/shared'
|
||||
import type { ClientUser, Locale, ServerProps } from 'payload'
|
||||
import type { AdminViewServerPropsOnly, ClientUser, Locale, ServerProps } from 'payload'
|
||||
|
||||
import { getTranslation } from '@payloadcms/translations'
|
||||
import { Button, Card, Gutter, Locked } from '@payloadcms/ui'
|
||||
@@ -29,7 +29,7 @@ export type DashboardViewServerPropsOnly = {
|
||||
*/
|
||||
Link?: React.ComponentType
|
||||
navGroups?: ReturnType<typeof groupNavItems>
|
||||
} & ServerProps
|
||||
} & AdminViewServerPropsOnly
|
||||
|
||||
export type DashboardViewServerProps = DashboardViewClientProps & DashboardViewServerPropsOnly
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { EntityToGroup } from '@payloadcms/ui/shared'
|
||||
import type { AdminViewServerProps } from 'payload'
|
||||
import type { AdminViewServerProps, TypedUser } from 'payload'
|
||||
|
||||
import { HydrateAuthProvider, SetStepNav } from '@payloadcms/ui'
|
||||
import { RenderServerComponent } from '@payloadcms/ui/elements/RenderServerComponent'
|
||||
@@ -10,7 +10,9 @@ import type { DashboardViewClientProps, DashboardViewServerPropsOnly } from './D
|
||||
|
||||
import { DefaultDashboard } from './Default/index.js'
|
||||
|
||||
export async function Dashboard({ initPageResult, params, searchParams }: AdminViewServerProps) {
|
||||
const globalLockDurationDefault = 300
|
||||
|
||||
export async function DashboardView(props: AdminViewServerProps) {
|
||||
const {
|
||||
locale,
|
||||
permissions,
|
||||
@@ -22,8 +24,7 @@ export async function Dashboard({ initPageResult, params, searchParams }: AdminV
|
||||
},
|
||||
req,
|
||||
visibleEntities,
|
||||
} = initPageResult
|
||||
|
||||
} = props.initPageResult
|
||||
const collections = config.collections.filter(
|
||||
(collection) =>
|
||||
permissions?.collections?.[collection.slug]?.read &&
|
||||
@@ -36,7 +37,7 @@ export async function Dashboard({ initPageResult, params, searchParams }: AdminV
|
||||
)
|
||||
|
||||
// Query locked global documents only if there are globals in the config
|
||||
let globalData = []
|
||||
let globalData: DashboardViewServerPropsOnly['globalData'] = []
|
||||
|
||||
if (config.globals.length > 0) {
|
||||
const lockedDocuments = await payload.find({
|
||||
@@ -45,6 +46,11 @@ export async function Dashboard({ initPageResult, params, searchParams }: AdminV
|
||||
overrideAccess: false,
|
||||
pagination: false,
|
||||
req,
|
||||
select: {
|
||||
globalSlug: true,
|
||||
updatedAt: true,
|
||||
user: true,
|
||||
},
|
||||
where: {
|
||||
globalSlug: {
|
||||
exists: true,
|
||||
@@ -54,11 +60,10 @@ export async function Dashboard({ initPageResult, params, searchParams }: AdminV
|
||||
|
||||
// Map over globals to include `lockDuration` and lock data for each global slug
|
||||
globalData = config.globals.map((global) => {
|
||||
const lockDurationDefault = 300
|
||||
const lockDuration =
|
||||
typeof global.lockDocuments === 'object'
|
||||
? global.lockDocuments.duration
|
||||
: lockDurationDefault
|
||||
: globalLockDurationDefault
|
||||
|
||||
const lockedDoc = lockedDocuments.docs.find((doc) => doc.globalSlug === global.slug)
|
||||
|
||||
@@ -66,8 +71,8 @@ export async function Dashboard({ initPageResult, params, searchParams }: AdminV
|
||||
slug: global.slug,
|
||||
data: {
|
||||
_isLocked: !!lockedDoc,
|
||||
_lastEditedAt: lockedDoc?.updatedAt ?? null,
|
||||
_userEditing: lockedDoc?.user?.value ?? null,
|
||||
_lastEditedAt: (lockedDoc?.updatedAt as string) ?? null,
|
||||
_userEditing: (lockedDoc?.user as { value?: TypedUser })?.value ?? null,
|
||||
},
|
||||
lockDuration,
|
||||
}
|
||||
@@ -109,14 +114,13 @@ export async function Dashboard({ initPageResult, params, searchParams }: AdminV
|
||||
Fallback: DefaultDashboard,
|
||||
importMap: payload.importMap,
|
||||
serverProps: {
|
||||
...props,
|
||||
globalData,
|
||||
i18n,
|
||||
locale,
|
||||
navGroups,
|
||||
params,
|
||||
payload,
|
||||
permissions,
|
||||
searchParams,
|
||||
user,
|
||||
visibleEntities,
|
||||
} satisfies DashboardViewServerPropsOnly,
|
||||
|
||||
@@ -422,7 +422,7 @@ export const renderDocument = async ({
|
||||
}
|
||||
}
|
||||
|
||||
export async function Document(props: AdminViewServerProps) {
|
||||
export async function DocumentView(props: AdminViewServerProps) {
|
||||
try {
|
||||
const { Document: RenderedDocument } = await renderDocument(props)
|
||||
return RenderedDocument
|
||||
|
||||
@@ -7,6 +7,7 @@ import type {
|
||||
ListViewClientProps,
|
||||
ListViewServerPropsOnly,
|
||||
PaginatedDocs,
|
||||
PayloadComponent,
|
||||
QueryPreset,
|
||||
SanitizedCollectionPermission,
|
||||
} from 'payload'
|
||||
@@ -32,7 +33,17 @@ import { renderListViewSlots } from './renderListViewSlots.js'
|
||||
import { resolveAllFilterOptions } from './resolveAllFilterOptions.js'
|
||||
import { transformColumnsToSelect } from './transformColumnsToSelect.js'
|
||||
|
||||
type RenderListViewArgs = {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type RenderListViewArgs = {
|
||||
/**
|
||||
* Allows providing your own list view component. This will override the default list view component and
|
||||
* the collection's configured list view component (if any).
|
||||
*/
|
||||
ComponentOverride?:
|
||||
| PayloadComponent
|
||||
| React.ComponentType<ListViewClientProps | (ListViewClientProps & ListViewServerPropsOnly)>
|
||||
customCellProps?: Record<string, any>
|
||||
disableBulkDelete?: boolean
|
||||
disableBulkEdit?: boolean
|
||||
@@ -40,7 +51,10 @@ type RenderListViewArgs = {
|
||||
drawerSlug?: string
|
||||
enableRowSelections: boolean
|
||||
overrideEntityVisibility?: boolean
|
||||
query: ListQuery
|
||||
/**
|
||||
* If not ListQuery is provided, `req.query` will be used.
|
||||
*/
|
||||
query?: ListQuery
|
||||
redirectAfterDelete?: boolean
|
||||
redirectAfterDuplicate?: boolean
|
||||
/**
|
||||
@@ -54,6 +68,8 @@ type RenderListViewArgs = {
|
||||
* the list view on the server for both:
|
||||
* - default list view
|
||||
* - list view within drawers
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export const renderListView = async (
|
||||
args: RenderListViewArgs,
|
||||
@@ -62,6 +78,7 @@ export const renderListView = async (
|
||||
}> => {
|
||||
const {
|
||||
clientConfig,
|
||||
ComponentOverride,
|
||||
customCellProps,
|
||||
disableBulkDelete,
|
||||
disableBulkEdit,
|
||||
@@ -385,7 +402,8 @@ export const renderListView = async (
|
||||
Table,
|
||||
viewType,
|
||||
} satisfies ListViewClientProps,
|
||||
Component: collectionConfig?.admin?.components?.views?.list?.Component,
|
||||
Component:
|
||||
ComponentOverride ?? collectionConfig?.admin?.components?.views?.list?.Component,
|
||||
Fallback: DefaultListView,
|
||||
importMap: payload.importMap,
|
||||
serverProps,
|
||||
|
||||
@@ -16,13 +16,13 @@ import type React from 'react'
|
||||
import { parseDocumentID } from 'payload'
|
||||
import { formatAdminURL, isNumber } from 'payload/shared'
|
||||
|
||||
import { Account } from '../Account/index.js'
|
||||
import { AccountView } from '../Account/index.js'
|
||||
import { BrowseByFolder } from '../BrowseByFolder/index.js'
|
||||
import { CollectionFolderView } from '../CollectionFolders/index.js'
|
||||
import { TrashView } from '../CollectionTrash/index.js'
|
||||
import { CreateFirstUserView } from '../CreateFirstUser/index.js'
|
||||
import { Dashboard } from '../Dashboard/index.js'
|
||||
import { Document as DocumentView } from '../Document/index.js'
|
||||
import { DashboardView } from '../Dashboard/index.js'
|
||||
import { DocumentView } from '../Document/index.js'
|
||||
import { forgotPasswordBaseClass, ForgotPasswordView } from '../ForgotPassword/index.js'
|
||||
import { ListView } from '../List/index.js'
|
||||
import { loginBaseClass, LoginView } from '../Login/index.js'
|
||||
@@ -54,7 +54,7 @@ export type ViewFromConfig = {
|
||||
}
|
||||
|
||||
const oneSegmentViews: OneSegmentViews = {
|
||||
account: Account,
|
||||
account: AccountView,
|
||||
browseByFolder: BrowseByFolder,
|
||||
createFirstUser: CreateFirstUserView,
|
||||
forgot: ForgotPasswordView,
|
||||
@@ -141,7 +141,7 @@ export const getRouteData = ({
|
||||
case 0: {
|
||||
if (currentRoute === adminRoute) {
|
||||
ViewToRender = {
|
||||
Component: Dashboard,
|
||||
Component: DashboardView,
|
||||
}
|
||||
templateClassName = 'dashboard'
|
||||
templateType = 'default'
|
||||
|
||||
Reference in New Issue
Block a user