Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions packages/next/src/views/Dashboard/Default/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export function DefaultDashboard(props: DashboardViewServerProps) {
payload: {
config: {
admin: {
components: { afterDashboard, beforeDashboard },
components: { afterDashboard, beforeDashboard, emptyDashboard },
},
routes: { admin: adminRoute },
},
Expand Down Expand Up @@ -75,7 +75,23 @@ export function DefaultDashboard(props: DashboardViewServerProps) {

<Fragment>
{!navGroups || navGroups?.length === 0 ? (
<p>no nav groups....</p>
emptyDashboard ? (
RenderServerComponent({
Component: emptyDashboard,
importMap: payload.importMap,
serverProps: {
i18n,
locale,
params,
payload,
permissions,
searchParams,
user,
} satisfies ServerProps,
})
) : (
<p>no nav groups....</p>
)
) : (
navGroups.map(({ entities, label }, groupIndex) => {
return (
Expand Down
2 changes: 2 additions & 0 deletions packages/payload/src/bin/generateImportMap/iterateConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ export function iterateConfig({
addToImportMap(config.admin?.components?.beforeLogin)
addToImportMap(config.admin?.components?.beforeNavLinks)

addToImportMap(config.admin?.components?.emptyDashboard)

addToImportMap(config.admin?.components?.providers)

if (config.admin?.components?.views) {
Expand Down
4 changes: 4 additions & 0 deletions packages/payload/src/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,10 @@ export type Config = {
* Add custom components before the navigation links
*/
beforeNavLinks?: CustomComponent[]
/**
* Add custom components when dashboard has any nav groups to show
*/
emptyDashboard?: CustomComponent[]
/** Replace graphical components */
graphics?: {
/** Replace the icon in the navigation */
Expand Down
16 changes: 16 additions & 0 deletions test/empty-dashboard/components/EmptyDashboard/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { PayloadServerReactComponent, SanitizedConfig } from 'payload'

import React from 'react'

const baseClass = 'empty-dashboard'

export const EmptyDashboard: PayloadServerReactComponent<
SanitizedConfig['admin']['components']['emptyDashboard'][0]
> = () => {
return (
<div className={baseClass}>
<h4>Empty Dashboard</h4>
<p>This is a custom empty dashboard component.</p>
</div>
)
}
45 changes: 45 additions & 0 deletions test/empty-dashboard/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { fileURLToPath } from 'node:url'
import path from 'path'

import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
import { devUser } from '../credentials.js'

const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)

export default buildConfigWithDefaults({
admin: {
importMap: {
baseDir: path.resolve(dirname),
},
components: {
emptyDashboard: ['/components/EmptyDashboard/index.js#EmptyDashboard'],
},
autoLogin: {
email: devUser.email,
password: devUser.password,
},
},
collections: [
{
slug: 'users',
auth: true,
fields: [],
admin: {
group: false,
},
},
],
onInit: async (payload) => {
await payload.create({
collection: 'users',
data: {
email: devUser.email,
password: devUser.password,
},
})
},
typescript: {
outputFile: path.resolve(dirname, 'payload-types.ts'),
},
})
49 changes: 49 additions & 0 deletions test/empty-dashboard/e2e.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type { Page } from '@playwright/test'

import { expect, test } from '@playwright/test'
import path from 'path'
import { fileURLToPath } from 'url'

import { ensureCompilationIsDone, initPageConsoleErrorCatch } from '../helpers.js'
import { initPayloadE2ENoConfig } from '../helpers/initPayloadE2ENoConfig.js'
import { TEST_TIMEOUT_LONG } from '../playwright.config.js'

const { beforeAll, describe } = test
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)

describe('Empty Dashboard', () => {
let serverURL: string
let page: Page

beforeAll(async ({ browser }, testInfo) => {
testInfo.setTimeout(TEST_TIMEOUT_LONG)

// Initialize Payload
;({ serverURL } = await initPayloadE2ENoConfig({ dirname }))

// Set up browser context
const context = await browser.newContext()
page = await context.newPage()
initPageConsoleErrorCatch(page)

// Wait for compilation to complete
await ensureCompilationIsDone({ page, serverURL })
})

test('should display empty dashboard component', async () => {
// Navigate to admin dashboard
await page.goto(`${serverURL}/admin`)

// Wait for the empty dashboard to be visible
const emptyDashboard = page.locator('.empty-dashboard')
await expect(emptyDashboard).toBeVisible()

// Verify the content
const title = emptyDashboard.locator('h4')
await expect(title).toHaveText('Empty Dashboard')

const description = emptyDashboard.locator('p')
await expect(description).toContainText('This is a custom empty dashboard component.')
})
})
Loading