diff --git a/bun.lock b/bun.lock
index 49edca2..0a2bfef 100644
--- a/bun.lock
+++ b/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=="],
diff --git a/package.json b/package.json
index 95ab216..00d574e 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/src/app/(frontend)/layout.tsx b/src/app/(frontend)/layout.tsx
index e7681f7..41e2ce2 100644
--- a/src/app/(frontend)/layout.tsx
+++ b/src/app/(frontend)/layout.tsx
@@ -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 (
+
{children}
diff --git a/src/app/(frontend)/page.tsx b/src/app/(frontend)/page.tsx
index de615ae..6b10aa3 100644
--- a/src/app/(frontend)/page.tsx
+++ b/src/app/(frontend)/page.tsx
@@ -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 (
@@ -53,7 +48,7 @@ export default async function HomePage() {
/>
{!user &&
Welcome to your new project.
}
- {user &&
Welcome back, {user.email}
}
+ {user &&
Welcome back, {user.name}
}
)
diff --git a/src/app/(payload)/admin/importMap.js b/src/app/(payload)/admin/importMap.js
index b6bb80e..881803e 100644
--- a/src/app/(payload)/admin/importMap.js
+++ b/src/app/(payload)/admin/importMap.js
@@ -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
}
diff --git a/src/collections/Page.ts b/src/collections/Page.ts
index 4decf75..d404dd0 100644
--- a/src/collections/Page.ts
+++ b/src/collections/Page.ts
@@ -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(),
],
}
diff --git a/src/components/AdminBar/index.scss b/src/components/AdminBar/index.scss
new file mode 100644
index 0000000..e72eb34
--- /dev/null
+++ b/src/components/AdminBar/index.scss
@@ -0,0 +1,7 @@
+@import '~@payloadcms/ui/scss';
+
+.admin-bar {
+ @include small-break {
+ display: none;
+ }
+}
\ No newline at end of file
diff --git a/src/components/AdminBar/index.tsx b/src/components/AdminBar/index.tsx
new file mode 100644
index 0000000..54eb939
--- /dev/null
+++ b/src/components/AdminBar/index.tsx
@@ -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 = () =>
Dashboard
+
+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 (
+
+
+
}
+ onAuthChange={onAuthChange}
+ onPreviewExit={() => {
+ fetch('/next/exit-preview').then(() => {
+ router.push('/')
+ router.refresh()
+ })
+ }}
+ style={{
+ backgroundColor: 'transparent',
+ padding: 0,
+ position: 'relative',
+ zIndex: 'unset',
+ }}
+ />
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/payload-types.ts b/src/payload-types.ts
index 90c8353..5e16c46 100644
--- a/src/payload-types.ts
+++ b/src/payload-types.ts
@@ -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
{
export interface PagesSelect {
title?: T;
richText?: T;
+ generateSlug?: T;
+ slug?: T;
updatedAt?: T;
createdAt?: T;
}
diff --git a/tsconfig.json b/tsconfig.json
index 477c25a..dd246d9 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -24,7 +24,7 @@
],
"paths": {
"@/*": [
- "./src/*"
+ "./*"
],
"@payload-config": [
"./src/payload.config.ts"
diff --git a/utilities/canUseDOM.ts b/utilities/canUseDOM.ts
new file mode 100644
index 0000000..df6703f
--- /dev/null
+++ b/utilities/canUseDOM.ts
@@ -0,0 +1 @@
+export default !!(typeof window !== 'undefined' && window.document && window.document.createElement)
\ No newline at end of file
diff --git a/utilities/getURL.ts b/utilities/getURL.ts
new file mode 100644
index 0000000..6766950
--- /dev/null
+++ b/utilities/getURL.ts
@@ -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 || ''
+}
\ No newline at end of file
diff --git a/utilities/ui.ts b/utilities/ui.ts
new file mode 100644
index 0000000..312ce6a
--- /dev/null
+++ b/utilities/ui.ts
@@ -0,0 +1,6 @@
+import { type ClassValue, clsx } from 'clsx'
+import { twMerge } from 'tailwind-merge'
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs))
+}
\ No newline at end of file