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
76 changes: 59 additions & 17 deletions docs/admin/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -257,23 +257,65 @@ const config = buildConfig({

The following options are available:

| Option | Default route | Description |
| ----------------- | -------------------- | ----------------------------------------- |
| `account` | `/account` | The user's account page. |
| `createFirstUser` | `/create-first-user` | The page to create the first user. |
| `forgot` | `/forgot` | The password reset page. |
| `inactivity` | `/logout-inactivity` | The page to redirect to after inactivity. |
| `login` | `/login` | The login page. |
| `logout` | `/logout` | The logout page. |
| `reset` | `/reset` | The password reset page. |
| `unauthorized` | `/unauthorized` | The unauthorized page. |
| Option | Default route | Description |
| ----------------- | -------------------- | ----------------------------------------------------------------------------------------- |
| `account` | `/account` | The user's account page. |
| `createFirstUser` | `/create-first-user` | The page to create the first user. |
| `forgot` | `/forgot` | The password reset page. |
| `inactivity` | `/logout-inactivity` | The page to redirect to after inactivity. |
| `login` | `/login` | The login page. |
| `logout` | `/logout` | The logout page. |
| `reset` | `/reset` | The password reset page. |
| `systemInfo` | `/system-info` | The system information page. Set to `false` to disable. [More details](#system-info-page) |
| `unauthorized` | `/unauthorized` | The unauthorized page. |

<Banner type="success">
**Note:** You can also swap out entire _views_ out for your own, using the
`admin.views` property of the Payload Config. See [Custom
Views](../custom-components/custom-views) for more information.
</Banner>

### System Info Page

The System Info page displays important technical information about your Payload installation and runtime environment. This page is accessible from the settings menu (gear icon) in the bottom left corner of the navigation, above the logout button.

The following information is displayed on the System Info page:

**Version Information:**

- Payload version
- Next.js version
- Node.js version

**Configuration:**

- Environment (NODE_ENV)
- Server URL
- Database Adapter (MongoDB, PostgreSQL, SQLite, or Cloudflare D1 SQLite)

**Current Session:**

- Logged-in user information

#### Disabling the System Info Page

If you want to disable the System Info page entirely, you can set `admin.routes.systemInfo` to `false`:

```ts
import { buildConfig } from 'payload'

const config = buildConfig({
// ...
admin: {
routes: {
systemInfo: false, // highlight-line
},
},
})
```

When disabled, the System Info page will not be accessible and will not appear in the settings menu.

## I18n

The Payload Admin Panel is translated in over [30 languages and counting](https://github.com/payloadcms/payload/tree/main/packages/translations). Languages are automatically detected based on the user's browser and used by the Admin Panel to display all text in that language. If no language was detected, or if the user's language is not yet supported, English will be chosen. Users can easily specify their language by selecting one from their account page. See [I18n](../configuration/i18n) for more information.
Expand Down Expand Up @@ -312,19 +354,19 @@ const config = buildConfig({
timezones: {
supportedTimezones: [
{
label: "Europe/Dublin",
value: "Europe/Dublin",
label: 'Europe/Dublin',
value: 'Europe/Dublin',
},
{
label: "Europe/Amsterdam",
value: "Europe/Amsterdam",
label: 'Europe/Amsterdam',
value: 'Europe/Amsterdam',
},
{
label: "Europe/Bucharest",
value: "Europe/Bucharest",
label: 'Europe/Bucharest',
value: 'Europe/Bucharest',
},
],
defaultTimezone: "Europe/Amsterdam",
defaultTimezone: 'Europe/Amsterdam',
},
},
})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
'use client'
import { PopupList, useConfig } from '@payloadcms/ui'
import React from 'react'

export const DefaultSystemInfoItem: React.FC = () => {
const {
config: {
admin: {
routes: { systemInfo },
},
routes: { admin: adminRoute },
},
} = useConfig()

return (
<PopupList.ButtonGroup>
<PopupList.Button href={`${adminRoute}${systemInfo}`}>System Info</PopupList.Button>
</PopupList.ButtonGroup>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use client'
import { PopupList } from '@payloadcms/ui'
import React from 'react'

export const SettingsDivider: React.FC = () => {
return <PopupList.Divider />
}
22 changes: 21 additions & 1 deletion packages/next/src/elements/Nav/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import React from 'react'

import { NavHamburger } from './NavHamburger/index.js'
import { NavWrapper } from './NavWrapper/index.js'
import { DefaultSystemInfoItem } from './SettingsMenuButton/DefaultSystemInfoItem.js'
import { SettingsMenuButton } from './SettingsMenuButton/index.js'
import { SettingsDivider } from './SettingsMenuButton/SettingsDivider.js'
import './index.scss'

const baseClass = 'nav'
Expand Down Expand Up @@ -93,7 +95,13 @@ export const DefaultNav: React.FC<NavProps> = async (props) => {
},
})

const renderedSettingsMenu =
// Include the default System Info menu item only if not disabled
const defaultSystemInfoItem =
payload.config.admin.routes.systemInfo !== false ? (
<DefaultSystemInfoItem key="default-system-info-item" />
) : null

const customSettingsMenuItems =
settingsMenu && Array.isArray(settingsMenu)
? settingsMenu.map((item, index) =>
RenderServerComponent({
Expand All @@ -117,6 +125,18 @@ export const DefaultNav: React.FC<NavProps> = async (props) => {
)
: []

// Reorganize settings menu: user-defined items first, then divider (if custom items exist), then System Info
const settingsDivider =
customSettingsMenuItems.length > 0 && defaultSystemInfoItem ? (
<SettingsDivider key="settings-divider" />
) : null

const renderedSettingsMenu = [
...customSettingsMenuItems,
settingsDivider,
defaultSystemInfoItem,
].filter(Boolean)

return (
<NavWrapper baseClass={baseClass}>
<nav className={`${baseClass}__wrap`}>
Expand Down
15 changes: 14 additions & 1 deletion packages/next/src/views/Root/getRouteData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { ListView } from '../List/index.js'
import { loginBaseClass, LoginView } from '../Login/index.js'
import { LogoutInactivity, LogoutView } from '../Logout/index.js'
import { ResetPassword, resetPasswordBaseClass } from '../ResetPassword/index.js'
import { SystemInfoView } from '../SystemInfo/index.js'
import { UnauthorizedView } from '../Unauthorized/index.js'
import { Verify, verifyBaseClass } from '../Verify/index.js'
import { getSubViewActions, getViewActions } from './attachViewActions.js'
Expand All @@ -42,6 +43,7 @@ const baseClasses = {
forgot: forgotPasswordBaseClass,
login: loginBaseClass,
reset: resetPasswordBaseClass,
systemInfo: 'system-info',
verify: verifyBaseClass,
}

Expand All @@ -62,6 +64,7 @@ const oneSegmentViews: OneSegmentViews = {
inactivity: LogoutInactivity,
login: LoginView,
logout: LogoutView,
systemInfo: SystemInfoView,
unauthorized: UnauthorizedView,
}

Expand Down Expand Up @@ -157,6 +160,10 @@ export const getRouteData = ({

if (config.admin.routes) {
const matchedRoute = Object.entries(config.admin.routes).find(([, route]) => {
// Skip routes that are explicitly disabled (set to false)
if (route === false) {
return false
}
return isPathMatchingRoute({
currentRoute,
exact: true,
Expand Down Expand Up @@ -205,6 +212,12 @@ export const getRouteData = ({
// --> /logout-inactivity
// --> /unauthorized

// Check if the System Info page is explicitly disabled
if (viewKey === 'systemInfo' && config.admin.routes.systemInfo === false) {
// System Info page is disabled, don't render it
break
}

ViewToRender = {
Component: oneSegmentViews[viewKey],
}
Expand All @@ -214,7 +227,7 @@ export const getRouteData = ({
templateClassName = baseClasses[viewKey]
templateType = 'minimal'

if (viewKey === 'account') {
if (viewKey === 'systemInfo' || viewKey === 'account') {
templateType = 'default'
}

Expand Down
89 changes: 89 additions & 0 deletions packages/next/src/views/SystemInfo/index.client.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
'use client'
import { Gutter, useStepNav } from '@payloadcms/ui'
import React, { useEffect } from 'react'

import type { SystemInfoViewProps } from './index.js'

import './index.scss'

const baseClass = 'system-info-view'

export const SystemInfoClient: React.FC<SystemInfoViewProps> = (props) => {
const {
databaseAdapter,
nextVersion,
nodeEnv,
nodeVersion,
payloadVersion,
serverURL,
userName,
} = props

const { setStepNav } = useStepNav()

useEffect(() => {
setStepNav([
{
label: 'System Info',
},
])
}, [setStepNav])

return (
<div className={baseClass}>
<Gutter className={`${baseClass}__wrap`}>
<div className={`${baseClass}__content`}>
<h1>System Info</h1>

<section className={`${baseClass}__section`}>
<h2>Version Information</h2>
<dl className={`${baseClass}__info-list`}>
<div className={`${baseClass}__info-item`}>
<dt>Payload Version</dt>
<dd>{payloadVersion}</dd>
</div>
<div className={`${baseClass}__info-item`}>
<dt>Next.js Version</dt>
<dd>{nextVersion}</dd>
</div>
<div className={`${baseClass}__info-item`}>
<dt>Node.js Version</dt>
<dd>{nodeVersion}</dd>
</div>
</dl>
</section>

<section className={`${baseClass}__section`}>
<h2>Configuration</h2>
<dl className={`${baseClass}__info-list`}>
<div className={`${baseClass}__info-item`}>
<dt>Environment (NODE_ENV)</dt>
<dd>{nodeEnv}</dd>
</div>
<div className={`${baseClass}__info-item`}>
<dt>Server URL</dt>
<dd>{serverURL}</dd>
</div>
<div className={`${baseClass}__info-item`}>
<dt>Database Adapter</dt>
<dd>{databaseAdapter}</dd>
</div>
</dl>
</section>

{userName && (
<section className={`${baseClass}__section`}>
<h2>Current Session</h2>
<dl className={`${baseClass}__info-list`}>
<div className={`${baseClass}__info-item`}>
<dt>Logged in as</dt>
<dd>{userName}</dd>
</div>
</dl>
</section>
)}
</div>
</Gutter>
</div>
)
}
74 changes: 74 additions & 0 deletions packages/next/src/views/SystemInfo/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
@import '~@payloadcms/ui/scss';

@layer payload-default {
.system-info-view {
&__wrap {
padding-block: var(--gutter-v);
}

&__content {
max-width: 800px;

h1 {
margin-bottom: var(--base);
}
}

&__section {
margin-top: var(--base);
padding-top: var(--base);
border-top: 1px solid var(--theme-elevation-150);

&:first-child {
margin-top: 0;
padding-top: 0;
border-top: none;
}

h2 {
font-size: 1.25rem;
margin-bottom: calc(var(--base) / 2);
font-weight: 600;
}
}

&__info-list {
display: grid;
gap: calc(var(--base) / 2);
margin: 0;
}

&__info-item {
display: grid;
grid-template-columns: 200px 1fr;
gap: var(--base);
padding: calc(var(--base) / 4) 0;

dt {
font-weight: 600;
color: var(--theme-text);
}

dd {
margin: 0;
color: var(--theme-elevation-800);
word-break: break-word;
}
}

&__plugin-list {
list-style: none;
margin: 0;
padding: 0;
display: grid;
gap: calc(var(--base) / 4);
}

&__plugin-item {
padding: calc(var(--base) / 4) calc(var(--base) / 2);
background-color: var(--theme-elevation-50);
border-radius: 4px;
color: var(--theme-elevation-800);
}
}
}
Loading