feat: manage generic page fallback copy via payload
This commit is contained in:
33
src/SiteSettings/config.ts
Normal file
33
src/SiteSettings/config.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import type { GlobalConfig } from 'payload'
|
||||||
|
|
||||||
|
import { defaultSiteSettings } from '@/spa/siteSettings'
|
||||||
|
|
||||||
|
export const SiteSettings: GlobalConfig = {
|
||||||
|
slug: 'site-settings',
|
||||||
|
access: {
|
||||||
|
read: () => true,
|
||||||
|
},
|
||||||
|
admin: {
|
||||||
|
group: 'Site Settings',
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'genericPageKicker',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: defaultSiteSettings.genericPageKicker,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'genericPageTitle',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: defaultSiteSettings.genericPageTitle,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'genericPageDescription',
|
||||||
|
type: 'textarea',
|
||||||
|
defaultValue: defaultSiteSettings.genericPageDescription,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import { notFound } from 'next/navigation'
|
|||||||
import NextApp from '@/spa/NextApp'
|
import NextApp from '@/spa/NextApp'
|
||||||
import { normalizeFooterData, normalizeHeaderData } from '@/spa/cmsNavigation'
|
import { normalizeFooterData, normalizeHeaderData } from '@/spa/cmsNavigation'
|
||||||
import type { CmsRouteDoc } from '@/spa/cmsRoute'
|
import type { CmsRouteDoc } from '@/spa/cmsRoute'
|
||||||
|
import { normalizeSiteSettings, type SpaSiteSettingsData } from '@/spa/siteSettings'
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic'
|
export const dynamic = 'force-dynamic'
|
||||||
|
|
||||||
@@ -73,9 +74,10 @@ export default async function Page({ params }: Args) {
|
|||||||
const pathname = `/${slug.join('/')}`.replace(/\/$/, '') || '/'
|
const pathname = `/${slug.join('/')}`.replace(/\/$/, '') || '/'
|
||||||
const payload = await getPayload({ config: configPromise })
|
const payload = await getPayload({ config: configPromise })
|
||||||
const { isEnabled: draft } = await draftMode()
|
const { isEnabled: draft } = await draftMode()
|
||||||
const [header, footer, route, preistraeger, events, posts] = await Promise.all([
|
const [header, footer, siteSettings, route, preistraeger, events, posts] = await Promise.all([
|
||||||
payload.findGlobal({ slug: 'header', depth: 1 }).catch(() => undefined),
|
payload.findGlobal({ slug: 'header', depth: 1 }).catch(() => undefined),
|
||||||
payload.findGlobal({ slug: 'footer', depth: 1 }).catch(() => undefined),
|
payload.findGlobal({ slug: 'footer', depth: 1 }).catch(() => undefined),
|
||||||
|
payload.findGlobal({ slug: 'site-settings', depth: 0 }).catch(() => undefined),
|
||||||
findRouteDoc(payload, pathname, draft),
|
findRouteDoc(payload, pathname, draft),
|
||||||
findList(payload, 'preistraeger', draft).catch(() => []),
|
findList(payload, 'preistraeger', draft).catch(() => []),
|
||||||
findList(payload, 'events', draft).catch(() => []),
|
findList(payload, 'events', draft).catch(() => []),
|
||||||
@@ -89,6 +91,7 @@ export default async function Page({ params }: Args) {
|
|||||||
pathname={pathname}
|
pathname={pathname}
|
||||||
header={normalizeHeaderData(header)}
|
header={normalizeHeaderData(header)}
|
||||||
footer={normalizeFooterData(footer)}
|
footer={normalizeFooterData(footer)}
|
||||||
|
siteSettings={normalizeSiteSettings(siteSettings as Partial<SpaSiteSettingsData> | undefined)}
|
||||||
cmsRoute={{
|
cmsRoute={{
|
||||||
collection: route.collection,
|
collection: route.collection,
|
||||||
doc: route.doc as unknown as CmsRouteDoc,
|
doc: route.doc as unknown as CmsRouteDoc,
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { post1 } from './post-1'
|
|||||||
import { post2 } from './post-2'
|
import { post2 } from './post-2'
|
||||||
import { post3 } from './post-3'
|
import { post3 } from './post-3'
|
||||||
import { defaultFooterData, defaultHeaderData } from '@/spa/cmsNavigation'
|
import { defaultFooterData, defaultHeaderData } from '@/spa/cmsNavigation'
|
||||||
|
import { defaultSiteSettings } from '@/spa/siteSettings'
|
||||||
|
|
||||||
const collections: CollectionSlug[] = [
|
const collections: CollectionSlug[] = [
|
||||||
'categories',
|
'categories',
|
||||||
@@ -21,7 +22,7 @@ const collections: CollectionSlug[] = [
|
|||||||
'search',
|
'search',
|
||||||
]
|
]
|
||||||
|
|
||||||
const globals: GlobalSlug[] = ['header', 'footer']
|
const globals: GlobalSlug[] = ['header', 'footer', 'site-settings']
|
||||||
|
|
||||||
const categories = ['Technology', 'News', 'Finance', 'Design', 'Software', 'Engineering']
|
const categories = ['Technology', 'News', 'Finance', 'Design', 'Software', 'Engineering']
|
||||||
|
|
||||||
@@ -228,6 +229,10 @@ export const seed = async ({
|
|||||||
slug: 'footer',
|
slug: 'footer',
|
||||||
data: footerSeedData as never,
|
data: footerSeedData as never,
|
||||||
}),
|
}),
|
||||||
|
payload.updateGlobal({
|
||||||
|
slug: 'site-settings',
|
||||||
|
data: defaultSiteSettings as never,
|
||||||
|
}),
|
||||||
])
|
])
|
||||||
|
|
||||||
payload.logger.info('Seeded database successfully!')
|
payload.logger.info('Seeded database successfully!')
|
||||||
|
|||||||
@@ -116,10 +116,12 @@ export interface Config {
|
|||||||
globals: {
|
globals: {
|
||||||
header: Header;
|
header: Header;
|
||||||
footer: Footer;
|
footer: Footer;
|
||||||
|
'site-settings': SiteSetting;
|
||||||
};
|
};
|
||||||
globalsSelect: {
|
globalsSelect: {
|
||||||
header: HeaderSelect<false> | HeaderSelect<true>;
|
header: HeaderSelect<false> | HeaderSelect<true>;
|
||||||
footer: FooterSelect<false> | FooterSelect<true>;
|
footer: FooterSelect<false> | FooterSelect<true>;
|
||||||
|
'site-settings': SiteSettingsSelect<false> | SiteSettingsSelect<true>;
|
||||||
};
|
};
|
||||||
locale: null;
|
locale: null;
|
||||||
widgets: {
|
widgets: {
|
||||||
@@ -5886,6 +5888,18 @@ export interface Footer {
|
|||||||
updatedAt?: string | null;
|
updatedAt?: string | null;
|
||||||
createdAt?: string | null;
|
createdAt?: string | null;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "site-settings".
|
||||||
|
*/
|
||||||
|
export interface SiteSetting {
|
||||||
|
id: number;
|
||||||
|
genericPageKicker: string;
|
||||||
|
genericPageTitle: string;
|
||||||
|
genericPageDescription: string;
|
||||||
|
updatedAt?: string | null;
|
||||||
|
createdAt?: string | null;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "header_select".
|
* via the `definition` "header_select".
|
||||||
@@ -6007,6 +6021,18 @@ export interface FooterSelect<T extends boolean = true> {
|
|||||||
createdAt?: T;
|
createdAt?: T;
|
||||||
globalType?: T;
|
globalType?: T;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "site-settings_select".
|
||||||
|
*/
|
||||||
|
export interface SiteSettingsSelect<T extends boolean = true> {
|
||||||
|
genericPageKicker?: T;
|
||||||
|
genericPageTitle?: T;
|
||||||
|
genericPageDescription?: T;
|
||||||
|
updatedAt?: T;
|
||||||
|
createdAt?: T;
|
||||||
|
globalType?: T;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "collections_widget".
|
* via the `definition` "collections_widget".
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { Users } from './collections/Users'
|
|||||||
import { Footer } from './Footer/config'
|
import { Footer } from './Footer/config'
|
||||||
import { Header } from './Header/config'
|
import { Header } from './Header/config'
|
||||||
import { plugins } from './plugins'
|
import { plugins } from './plugins'
|
||||||
|
import { SiteSettings } from './SiteSettings/config'
|
||||||
import { defaultLexical } from '@/fields/defaultLexical'
|
import { defaultLexical } from '@/fields/defaultLexical'
|
||||||
import { getServerSideURL } from './utilities/getURL'
|
import { getServerSideURL } from './utilities/getURL'
|
||||||
|
|
||||||
@@ -66,7 +67,7 @@ export default buildConfig({
|
|||||||
}),
|
}),
|
||||||
collections: [Pages, Posts, Events, Preistraeger, Media, Categories, Users],
|
collections: [Pages, Posts, Events, Preistraeger, Media, Categories, Users],
|
||||||
cors: [getServerSideURL()].filter(Boolean),
|
cors: [getServerSideURL()].filter(Boolean),
|
||||||
globals: [Header, Footer],
|
globals: [Header, Footer, SiteSettings],
|
||||||
plugins,
|
plugins,
|
||||||
secret: process.env.PAYLOAD_SECRET,
|
secret: process.env.PAYLOAD_SECRET,
|
||||||
sharp,
|
sharp,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import Layout from '@/spa/components/layout/Layout'
|
|||||||
import ScrollToTop from '@/spa/components/layout/ScrollToTop'
|
import ScrollToTop from '@/spa/components/layout/ScrollToTop'
|
||||||
import { defaultHeaderData, type SpaFooterData, type SpaHeaderData } from '@/spa/cmsNavigation'
|
import { defaultHeaderData, type SpaFooterData, type SpaHeaderData } from '@/spa/cmsNavigation'
|
||||||
import { CmsRouteProvider, type CmsRouteData } from '@/spa/cmsRoute'
|
import { CmsRouteProvider, type CmsRouteData } from '@/spa/cmsRoute'
|
||||||
|
import type { SpaSiteSettingsData } from '@/spa/siteSettings'
|
||||||
import About from '@/spa/pages/About'
|
import About from '@/spa/pages/About'
|
||||||
import BlogDetail from '@/spa/pages/BlogDetail'
|
import BlogDetail from '@/spa/pages/BlogDetail'
|
||||||
import Contact from '@/spa/pages/Contact'
|
import Contact from '@/spa/pages/Contact'
|
||||||
@@ -58,11 +59,13 @@ export default function NextApp({
|
|||||||
header,
|
header,
|
||||||
footer,
|
footer,
|
||||||
cmsRoute,
|
cmsRoute,
|
||||||
|
siteSettings,
|
||||||
}: {
|
}: {
|
||||||
pathname: string
|
pathname: string
|
||||||
header?: SpaHeaderData
|
header?: SpaHeaderData
|
||||||
footer?: SpaFooterData
|
footer?: SpaFooterData
|
||||||
cmsRoute?: CmsRouteData
|
cmsRoute?: CmsRouteData
|
||||||
|
siteSettings?: SpaSiteSettingsData
|
||||||
}) {
|
}) {
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
const headerData = header || defaultHeaderData
|
const headerData = header || defaultHeaderData
|
||||||
@@ -92,7 +95,7 @@ export default function NextApp({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<CmsRouteProvider value={cmsRoute}>
|
<CmsRouteProvider value={cmsRoute ? { ...cmsRoute, siteSettings } : undefined}>
|
||||||
<ScrollToTop />
|
<ScrollToTop />
|
||||||
<Layout header={header} footer={footer}>
|
<Layout header={header} footer={footer}>
|
||||||
{page}
|
{page}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
import React, { createContext, useContext } from 'react'
|
import React, { createContext, useContext } from 'react'
|
||||||
|
|
||||||
|
import type { SpaSiteSettingsData } from '@/spa/siteSettings'
|
||||||
|
|
||||||
export type CmsRouteCollection = 'pages' | 'posts' | 'events' | 'preistraeger'
|
export type CmsRouteCollection = 'pages' | 'posts' | 'events' | 'preistraeger'
|
||||||
|
|
||||||
export type CmsRouteDoc = {
|
export type CmsRouteDoc = {
|
||||||
@@ -21,6 +23,7 @@ export type CmsRouteData = {
|
|||||||
collection: CmsRouteCollection
|
collection: CmsRouteCollection
|
||||||
doc: CmsRouteDoc
|
doc: CmsRouteDoc
|
||||||
lists?: Partial<Record<CmsRouteCollection, CmsRouteDoc[]>>
|
lists?: Partial<Record<CmsRouteCollection, CmsRouteDoc[]>>
|
||||||
|
siteSettings?: SpaSiteSettingsData
|
||||||
}
|
}
|
||||||
|
|
||||||
const CmsRouteContext = createContext<CmsRouteData | undefined>(undefined)
|
const CmsRouteContext = createContext<CmsRouteData | undefined>(undefined)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { useCmsRoute } from '@/spa/cmsRoute'
|
import { useCmsRoute } from '@/spa/cmsRoute'
|
||||||
|
import { defaultSiteSettings } from '@/spa/siteSettings'
|
||||||
|
|
||||||
const NAVY = '#111D55'
|
const NAVY = '#111D55'
|
||||||
const GOLD = '#EFBF04'
|
const GOLD = '#EFBF04'
|
||||||
@@ -13,14 +14,15 @@ const FB = '"Inter", sans-serif'
|
|||||||
export default function CmsGenericPage() {
|
export default function CmsGenericPage() {
|
||||||
const route = useCmsRoute()
|
const route = useCmsRoute()
|
||||||
const doc = route?.doc
|
const doc = route?.doc
|
||||||
const title = doc?.title || doc?.meta?.title || 'Seite'
|
const siteSettings = route?.siteSettings || defaultSiteSettings
|
||||||
|
const title = doc?.title || doc?.meta?.title || siteSettings.genericPageTitle
|
||||||
const description = doc?.description || doc?.meta?.description
|
const description = doc?.description || doc?.meta?.description
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main style={{ minHeight: '70vh', background: CREAM, padding: '140px 24px 80px' }}>
|
<main style={{ minHeight: '70vh', background: CREAM, padding: '140px 24px 80px' }}>
|
||||||
<div style={{ maxWidth: 960, margin: '0 auto' }}>
|
<div style={{ maxWidth: 960, margin: '0 auto' }}>
|
||||||
<div style={{ color: GOLD, fontFamily: FF, fontWeight: 800, fontSize: 12, letterSpacing: '0.18em', textTransform: 'uppercase', marginBottom: 20 }}>
|
<div style={{ color: GOLD, fontFamily: FF, fontWeight: 800, fontSize: 12, letterSpacing: '0.18em', textTransform: 'uppercase', marginBottom: 20 }}>
|
||||||
{route?.collection || 'CMS'}
|
{route?.collection || siteSettings.genericPageKicker}
|
||||||
</div>
|
</div>
|
||||||
<h1 style={{ color: NAVY, fontFamily: FF, fontSize: 'clamp(2.25rem, 7vw, 5rem)', lineHeight: 1, fontWeight: 900, textTransform: 'uppercase', margin: 0 }}>
|
<h1 style={{ color: NAVY, fontFamily: FF, fontSize: 'clamp(2.25rem, 7vw, 5rem)', lineHeight: 1, fontWeight: 900, textTransform: 'uppercase', margin: 0 }}>
|
||||||
{title}
|
{title}
|
||||||
@@ -31,7 +33,7 @@ export default function CmsGenericPage() {
|
|||||||
</p>
|
</p>
|
||||||
) : (
|
) : (
|
||||||
<p style={{ color: 'rgba(58,58,58,0.65)', fontFamily: FB, fontSize: 18, lineHeight: 1.7, marginTop: 28, maxWidth: 760 }}>
|
<p style={{ color: 'rgba(58,58,58,0.65)', fontFamily: FB, fontSize: 18, lineHeight: 1.7, marginTop: 28, maxWidth: 760 }}>
|
||||||
Diese Route wird aus Payload CMS geladen. Ergänze Inhalte im entsprechenden Collection-Dokument.
|
{siteSettings.genericPageDescription}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
18
src/spa/siteSettings.ts
Normal file
18
src/spa/siteSettings.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
export type SpaSiteSettingsData = {
|
||||||
|
genericPageKicker: string
|
||||||
|
genericPageTitle: string
|
||||||
|
genericPageDescription: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultSiteSettings: SpaSiteSettingsData = {
|
||||||
|
genericPageKicker: 'CMS',
|
||||||
|
genericPageTitle: 'Seite',
|
||||||
|
genericPageDescription: 'Diese Route wird aus Payload CMS geladen. Ergänze Inhalte im entsprechenden Collection-Dokument.',
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeSiteSettings(settings?: Partial<SpaSiteSettingsData> | null): SpaSiteSettingsData {
|
||||||
|
return {
|
||||||
|
...defaultSiteSettings,
|
||||||
|
...settings,
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user