added adminbar to layout
This commit is contained in:
6
bun.lock
6
bun.lock
@@ -3,6 +3,7 @@
|
||||
"workspaces": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"@payloadcms/admin-bar": "^3.59.1",
|
||||
"@payloadcms/db-postgres": "3.59.1",
|
||||
"@payloadcms/next": "3.59.1",
|
||||
"@payloadcms/payload-cloud": "3.59.1",
|
||||
@@ -16,6 +17,7 @@
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"sharp": "0.34.2",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "1.54.1",
|
||||
@@ -455,6 +457,8 @@
|
||||
|
||||
"@nolyfill/is-core-module": ["@nolyfill/is-core-module@1.0.39", "", {}, "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA=="],
|
||||
|
||||
"@payloadcms/admin-bar": ["@payloadcms/admin-bar@3.59.1", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-gaOP7fWypaNHUfxLhdgmKV9qfrKmRUOjy32deBE/75lOSTtrZfOgMbq6W5cVLYl0yjV7xlSFoINIWmawmkQCjA=="],
|
||||
|
||||
"@payloadcms/db-postgres": ["@payloadcms/db-postgres@3.59.1", "", { "dependencies": { "@payloadcms/drizzle": "3.59.1", "@types/pg": "8.10.2", "console-table-printer": "2.12.1", "drizzle-kit": "0.31.4", "drizzle-orm": "0.44.2", "pg": "8.16.3", "prompts": "2.4.2", "to-snake-case": "1.0.0", "uuid": "10.0.0" }, "peerDependencies": { "payload": "3.59.1" } }, "sha512-lTxItMJ8wDt9gQ19zFRV2XJBsuoMtQiEOtHNBxULu4SVdee0vdcuRFZmjQYB5tZ0VOUEp96sCx+aMUh7RwRmzw=="],
|
||||
|
||||
"@payloadcms/drizzle": ["@payloadcms/drizzle@3.59.1", "", { "dependencies": { "console-table-printer": "2.12.1", "dequal": "2.0.3", "drizzle-orm": "0.44.2", "prompts": "2.4.2", "to-snake-case": "1.0.0", "uuid": "9.0.0" }, "peerDependencies": { "payload": "3.59.1" } }, "sha512-2n25PQfbFJ7uIlQxLilf8lrdbIGJ9p56ZNtViHl57DCq8SYGcB7Rz+5MSNdGwMnK28oYMAObJ4bBI5JG9DhR/g=="],
|
||||
@@ -1707,6 +1711,8 @@
|
||||
|
||||
"tabbable": ["tabbable@6.2.0", "", {}, "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew=="],
|
||||
|
||||
"tailwind-merge": ["tailwind-merge@3.3.1", "", {}, "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g=="],
|
||||
|
||||
"thread-stream": ["thread-stream@3.1.0", "", { "dependencies": { "real-require": "^0.2.0" } }, "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A=="],
|
||||
|
||||
"tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="],
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
"test:int": "cross-env NODE_OPTIONS=--no-deprecation vitest run --config ./vitest.config.mts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@payloadcms/admin-bar": "^3.59.1",
|
||||
"@payloadcms/db-postgres": "3.59.1",
|
||||
"@payloadcms/next": "3.59.1",
|
||||
"@payloadcms/payload-cloud": "3.59.1",
|
||||
"@payloadcms/richtext-lexical": "3.59.1",
|
||||
@@ -30,7 +32,7 @@
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"sharp": "0.34.2",
|
||||
"@payloadcms/db-postgres": "3.59.1"
|
||||
"tailwind-merge": "^3.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "1.54.1",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react'
|
||||
import './styles.css'
|
||||
import { AdminBar } from '@/src/components/AdminBar'
|
||||
|
||||
export const metadata = {
|
||||
description: 'A blank template using Payload in a Next.js app.',
|
||||
@@ -12,6 +13,7 @@ export default async function RootLayout(props: { children: React.ReactNode }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>
|
||||
<AdminBar />
|
||||
<main>{children}</main>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,9 +3,8 @@ import Image from 'next/image'
|
||||
import { getPayload } from 'payload'
|
||||
import type { Payload } from 'payload'
|
||||
import React from 'react'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
import config from '@/payload.config'
|
||||
import config from '@/src/payload.config'
|
||||
import './styles.css'
|
||||
import { RichText } from '@payloadcms/richtext-lexical/react'
|
||||
|
||||
@@ -15,8 +14,6 @@ export default async function HomePage() {
|
||||
const payload = await getPayload({ config: payloadConfig })
|
||||
const { user } = await payload.auth({ headers })
|
||||
|
||||
const fileURL = `vscode://file/${fileURLToPath(import.meta.url)}`
|
||||
|
||||
const homePage = await payload.find({
|
||||
collection: 'pages',
|
||||
where: {
|
||||
@@ -28,8 +25,6 @@ export default async function HomePage() {
|
||||
|
||||
const page = homePage.docs?.[0]
|
||||
|
||||
console.log(page)
|
||||
|
||||
return (
|
||||
<div className="home">
|
||||
<div className="content">
|
||||
@@ -53,7 +48,7 @@ export default async function HomePage() {
|
||||
/>
|
||||
</picture>
|
||||
{!user && <h1>Welcome to your new project.</h1>}
|
||||
{user && <h1>Welcome back, {user.email}</h1>}
|
||||
{user && <h1>Welcome back, {user.name}</h1>}
|
||||
<div className="links">
|
||||
<a
|
||||
className="admin"
|
||||
@@ -75,9 +70,7 @@ export default async function HomePage() {
|
||||
</div>
|
||||
<div className="footer">
|
||||
<p>Update this page by editing</p>
|
||||
<a className="codeLink" href={fileURL}>
|
||||
<code>app/(frontend)/page.tsx</code>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -22,6 +22,7 @@ import { StrikethroughFeatureClient as StrikethroughFeatureClient_e70f5e05f09f93
|
||||
import { UnderlineFeatureClient as UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { BoldFeatureClient as BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { ItalicFeatureClient as ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { SlugField as SlugField_3817bf644402e67bfe6577f60ef982de } from '@payloadcms/ui'
|
||||
|
||||
export const importMap = {
|
||||
"@payloadcms/richtext-lexical/rsc#RscEntryLexicalCell": RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e,
|
||||
@@ -47,5 +48,6 @@ export const importMap = {
|
||||
"@payloadcms/richtext-lexical/client#StrikethroughFeatureClient": StrikethroughFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#UnderlineFeatureClient": UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#BoldFeatureClient": BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#ItalicFeatureClient": ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864
|
||||
"@payloadcms/richtext-lexical/client#ItalicFeatureClient": ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/ui#SlugField": SlugField_3817bf644402e67bfe6577f60ef982de
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { lexicalEditor } from '@payloadcms/richtext-lexical'
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { slugField } from 'payload'
|
||||
|
||||
export const Page: CollectionConfig = {
|
||||
slug: 'pages',
|
||||
@@ -7,9 +8,6 @@ export const Page: CollectionConfig = {
|
||||
title: true,
|
||||
slug: true,
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
admin: {
|
||||
defaultColumns: ['title', 'slug', 'updatedAt',],
|
||||
},
|
||||
@@ -22,6 +20,7 @@ export const Page: CollectionConfig = {
|
||||
{
|
||||
name: 'richText',
|
||||
type: 'richText'
|
||||
}
|
||||
},
|
||||
slugField(),
|
||||
],
|
||||
}
|
||||
|
||||
7
src/components/AdminBar/index.scss
Normal file
7
src/components/AdminBar/index.scss
Normal file
@@ -0,0 +1,7 @@
|
||||
@import '~@payloadcms/ui/scss';
|
||||
|
||||
.admin-bar {
|
||||
@include small-break {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
89
src/components/AdminBar/index.tsx
Normal file
89
src/components/AdminBar/index.tsx
Normal file
@@ -0,0 +1,89 @@
|
||||
'use client'
|
||||
|
||||
import type { PayloadAdminBarProps, PayloadMeUser } from '@payloadcms/admin-bar'
|
||||
|
||||
import { cn } from '@/utilities/ui'
|
||||
import { useSelectedLayoutSegments } from 'next/navigation'
|
||||
import { PayloadAdminBar } from '@payloadcms/admin-bar'
|
||||
import React, { useState } from 'react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
|
||||
import './index.scss'
|
||||
|
||||
import { getClientSideURL } from '@/utilities/getURL'
|
||||
|
||||
const baseClass = 'admin-bar'
|
||||
|
||||
const collectionLabels = {
|
||||
pages: {
|
||||
plural: 'Pages',
|
||||
singular: 'Page',
|
||||
},
|
||||
posts: {
|
||||
plural: 'Posts',
|
||||
singular: 'Post',
|
||||
},
|
||||
projects: {
|
||||
plural: 'Projects',
|
||||
singular: 'Project',
|
||||
},
|
||||
}
|
||||
|
||||
const Title: React.FC = () => <span>Dashboard</span>
|
||||
|
||||
export const AdminBar: React.FC<{
|
||||
adminBarProps?: PayloadAdminBarProps
|
||||
}> = (props) => {
|
||||
const { adminBarProps } = props || {}
|
||||
const segments = useSelectedLayoutSegments()
|
||||
const [show, setShow] = useState(false)
|
||||
const collection = (
|
||||
collectionLabels[segments?.[1] as keyof typeof collectionLabels] ? segments[1] : 'pages'
|
||||
) as keyof typeof collectionLabels
|
||||
const router = useRouter()
|
||||
|
||||
const onAuthChange = React.useCallback((user: PayloadMeUser) => {
|
||||
setShow(Boolean(user?.id))
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(baseClass, 'py-2 bg-black text-white', {
|
||||
block: show,
|
||||
hidden: !show,
|
||||
})}
|
||||
>
|
||||
<div className="container">
|
||||
<PayloadAdminBar
|
||||
{...adminBarProps}
|
||||
className="py-2 text-white"
|
||||
classNames={{
|
||||
controls: 'font-medium text-white',
|
||||
logo: 'text-white',
|
||||
user: 'text-white',
|
||||
}}
|
||||
cmsURL={getClientSideURL()}
|
||||
collectionSlug={collection}
|
||||
collectionLabels={{
|
||||
plural: collectionLabels[collection]?.plural || 'Pages',
|
||||
singular: collectionLabels[collection]?.singular || 'Page',
|
||||
}}
|
||||
logo={<Title />}
|
||||
onAuthChange={onAuthChange}
|
||||
onPreviewExit={() => {
|
||||
fetch('/next/exit-preview').then(() => {
|
||||
router.push('/')
|
||||
router.refresh()
|
||||
})
|
||||
}}
|
||||
style={{
|
||||
backgroundColor: 'transparent',
|
||||
padding: 0,
|
||||
position: 'relative',
|
||||
zIndex: 'unset',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -182,6 +182,11 @@ export interface Page {
|
||||
};
|
||||
[k: string]: unknown;
|
||||
} | null;
|
||||
/**
|
||||
* When enabled, the slug will auto-generate from the title field on save and autosave.
|
||||
*/
|
||||
generateSlug?: boolean | null;
|
||||
slug: string;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
@@ -295,6 +300,8 @@ export interface MediaSelect<T extends boolean = true> {
|
||||
export interface PagesSelect<T extends boolean = true> {
|
||||
title?: T;
|
||||
richText?: T;
|
||||
generateSlug?: T;
|
||||
slug?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
],
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
"./*"
|
||||
],
|
||||
"@payload-config": [
|
||||
"./src/payload.config.ts"
|
||||
|
||||
1
utilities/canUseDOM.ts
Normal file
1
utilities/canUseDOM.ts
Normal file
@@ -0,0 +1 @@
|
||||
export default !!(typeof window !== 'undefined' && window.document && window.document.createElement)
|
||||
26
utilities/getURL.ts
Normal file
26
utilities/getURL.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import canUseDOM from './canUseDOM'
|
||||
|
||||
export const getServerSideURL = () => {
|
||||
return (
|
||||
process.env.NEXT_PUBLIC_SERVER_URL ||
|
||||
(process.env.VERCEL_PROJECT_PRODUCTION_URL
|
||||
? `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}`
|
||||
: 'http://localhost:3000')
|
||||
)
|
||||
}
|
||||
|
||||
export const getClientSideURL = () => {
|
||||
if (canUseDOM) {
|
||||
const protocol = window.location.protocol
|
||||
const domain = window.location.hostname
|
||||
const port = window.location.port
|
||||
|
||||
return `${protocol}//${domain}${port ? `:${port}` : ''}`
|
||||
}
|
||||
|
||||
if (process.env.VERCEL_PROJECT_PRODUCTION_URL) {
|
||||
return `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}`
|
||||
}
|
||||
|
||||
return process.env.NEXT_PUBLIC_SERVER_URL || ''
|
||||
}
|
||||
6
utilities/ui.ts
Normal file
6
utilities/ui.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { type ClassValue, clsx } from 'clsx'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
Reference in New Issue
Block a user