Scaffold webu na Payload CMS 3 + Next.js nad SQLite
CI / test (push) Failing after 1m42s
CI / build-and-push (push) Has been skipped

Kostra prezentace s vlastní administrací pro Martina Reinera.

Obsah:
- kolekce Products (obrázky, volné parametry, cena, dostupnost, skrytí),
  Media se zmenšeninami, Pages, Users bez veřejné registrace
- veřejná část: výpis nabídky, detail položky, statické stránky
- /api/health pro Docker HEALTHCHECK

Provoz:
- Dockerfile, docker-compose.yml (vývoj) a docker-compose.prod.yml
  (nasazení z Gitea registry)
- CI v Gitea Actions: lint -> testy -> build -> push image
- úvodní migrace databáze

Stránky jsou force-dynamic, aby se úprava položky projevila hned;
schéma se za běhu nedomýšlí (push: false), migrace se commitují.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-30 13:13:23 +02:00
co-authored by Claude Opus 5
parent fedccbee93
commit c8e435139e
49 changed files with 13152 additions and 49 deletions
+42
View File
@@ -0,0 +1,42 @@
import { notFound } from 'next/navigation'
import { getPayload } from 'payload'
import React from 'react'
import { RichText } from '@payloadcms/richtext-lexical/react'
import config from '@/payload.config'
import type { Page } from '@/payload-types'
import '../styles.css'
export const dynamic = 'force-dynamic'
type Props = { params: Promise<{ slug: string }> }
export async function generateMetadata({ params }: Props) {
const stranka = await najdiStranku((await params).slug)
return { title: stranka ? `${stranka.nazev} — Reiner zbraně` : 'Stránka nenalezena' }
}
/** Statické stránky z kolekce Pages — /kontakt, /o-nas a podobně. */
export default async function StatickaStranka({ params }: Props) {
const stranka = await najdiStranku((await params).slug)
if (!stranka) notFound()
return (
<article className="stranka">
<h1>{stranka.nazev}</h1>
{stranka.obsah && <RichText data={stranka.obsah} />}
</article>
)
}
async function najdiStranku(slug: string): Promise<Page | null> {
const payload = await getPayload({ config: await config })
const { docs } = await payload.find({
collection: 'pages',
where: { slug: { equals: slug } },
limit: 1,
})
return docs[0] ?? null
}
+13
View File
@@ -0,0 +1,13 @@
/**
* Endpoint pro Docker HEALTHCHECK.
*
* Záměrně nesahá do databáze — odpovídá na otázku „stojí server a obsluhuje
* požadavky?“. Výpadek databáze pozná až monitoring obsahu, ne healthcheck;
* kdyby ho shazoval, restartoval by Docker aplikaci kvůli něčemu, co restart
* neopraví.
*/
export const dynamic = 'force-dynamic'
export function GET() {
return Response.json({ status: 'ok' })
}
+38
View File
@@ -0,0 +1,38 @@
import React from 'react'
import Link from 'next/link'
import './styles.css'
export const metadata = {
description: 'Prodej zbraní, střeliva a doplňků — Martin Reiner.',
title: 'Reiner — zbraně a střelivo',
}
export default async function RootLayout(props: { children: React.ReactNode }) {
const { children } = props
return (
<html lang="cs">
<body>
<header className="hlavicka">
<Link className="znacka" href="/">
Reiner <span>zbraně a střelivo</span>
</Link>
<nav>
<Link href="/">Nabídka</Link>
<Link href="/kontakt">Kontakt</Link>
</nav>
</header>
<main>{children}</main>
<footer className="paticka">
<p>© {new Date().getFullYear()} Martin Reiner</p>
<p>
Prodej zbraní a střeliva pouze osobám s platným zbrojním průkazem podle zákona
č.&nbsp;14/2021&nbsp;Sb.
</p>
</footer>
</body>
</html>
)
}
+104
View File
@@ -0,0 +1,104 @@
import Image from 'next/image'
import Link from 'next/link'
import { notFound } from 'next/navigation'
import { getPayload } from 'payload'
import React from 'react'
import config from '@/payload.config'
import type { Media, Product } from '@/payload-types'
import { formatujCenu, popisStavu } from '@/lib/format'
import '../../styles.css'
export const dynamic = 'force-dynamic'
type Props = { params: Promise<{ slug: string }> }
export async function generateMetadata({ params }: Props) {
const polozka = await najdiPolozku((await params).slug)
if (!polozka) return { title: 'Položka nenalezena' }
return {
title: `${polozka.nazev} — Reiner zbraně`,
description: polozka.popis ?? undefined,
}
}
export default async function DetailPolozky({ params }: Props) {
const polozka = await najdiPolozku((await params).slug)
if (!polozka) notFound()
const obrazky = (polozka.obrazky ?? [])
.map((radek) => radek.obrazek)
.filter((o): o is Media => typeof o === 'object' && o !== null)
return (
<article className="stranka detail">
<p className="zpet">
<Link href="/"> Zpět na nabídku</Link>
</p>
<h1>{polozka.nazev}</h1>
<p className="detail__cena">
{formatujCenu(polozka.cena)}
<span className={`znacka-stavu znacka-stavu--${polozka.stav}`}>
{popisStavu(polozka.stav)}
</span>
</p>
{obrazky.length > 0 && (
<div className="galerie">
{obrazky.map((obrazek) => {
const varianta = obrazek.sizes?.detail
const url = varianta?.url ?? obrazek.url
const width = varianta?.width ?? obrazek.width
const height = varianta?.height ?? obrazek.height
if (!url || !width || !height) return null
return (
<Image
key={obrazek.id}
alt={obrazek.alt ?? polozka.nazev}
src={url}
width={width}
height={height}
sizes="(max-width: 900px) 100vw, 800px"
/>
)
})}
</div>
)}
{polozka.parametry && polozka.parametry.length > 0 && (
<table className="parametry">
<tbody>
{polozka.parametry.map((parametr) => (
<tr key={parametr.id}>
<th scope="row">{parametr.nazev}</th>
<td>{parametr.hodnota}</td>
</tr>
))}
</tbody>
</table>
)}
{polozka.popis && <div className="popis">{polozka.popis}</div>}
</article>
)
}
async function najdiPolozku(slug: string): Promise<Product | null> {
const payload = await getPayload({ config: await config })
const { docs } = await payload.find({
collection: 'products',
where: {
slug: { equals: slug },
skryto: { not_equals: true },
},
depth: 1,
limit: 1,
})
return docs[0] ?? null
}
+90
View File
@@ -0,0 +1,90 @@
import Image from 'next/image'
import Link from 'next/link'
import { getPayload } from 'payload'
import React from 'react'
import config from '@/payload.config'
import type { Media, Product } from '@/payload-types'
import { formatujCenu, popisStavu } from '@/lib/format'
import './styles.css'
// Obsah se čte z CMS při každém požadavku — jinak by se úprava položky
// projevila až po přestavění webu.
export const dynamic = 'force-dynamic'
/** Výpis nabídky. Skryté položky se nezobrazují, prodané se řadí nakonec. */
export default async function HomePage() {
const payload = await getPayload({ config: await config })
const { docs: polozky } = await payload.find({
collection: 'products',
where: { skryto: { not_equals: true } },
sort: ['stav', '-updatedAt'],
depth: 1,
limit: 100,
})
return (
<div className="stranka">
<h1>Nabídka</h1>
{polozky.length === 0 ? (
<p className="prazdno">
Zatím tu nic není. Položky se přidávají v <Link href="/admin">administraci</Link>.
</p>
) : (
<ul className="mrizka">
{polozky.map((polozka) => (
<KartaPolozky key={polozka.id} polozka={polozka} />
))}
</ul>
)}
</div>
)
}
function KartaPolozky({ polozka }: { polozka: Product }) {
const nahled = prvniObrazek(polozka)
return (
<li className={`karta${polozka.stav === 'prodano' ? ' karta--prodano' : ''}`}>
<Link href={`/nabidka/${polozka.slug}`}>
<div className="karta__obrazek">
{nahled ? (
<Image
alt={nahled.alt}
src={nahled.url}
width={nahled.width}
height={nahled.height}
sizes="(max-width: 700px) 100vw, 320px"
/>
) : (
<div className="karta__bezobrazku">bez fotky</div>
)}
</div>
<h2>{polozka.nazev}</h2>
<p className="karta__cena">{formatujCenu(polozka.cena)}</p>
<p className="karta__stav">{popisStavu(polozka.stav)}</p>
</Link>
</li>
)
}
/**
* Vytáhne z položky první obrázek v podobě použitelné pro `next/image`.
* Vrací `null`, pokud položka fotku nemá nebo se nepodařilo načíst rozměry.
*/
function prvniObrazek(polozka: Product) {
const prvni = polozka.obrazky?.[0]?.obrazek
if (!prvni || typeof prvni === 'number') return null
const media = prvni as Media
const varianta = media.sizes?.nahled
const url = varianta?.url ?? media.url
const width = varianta?.width ?? media.width
const height = varianta?.height ?? media.height
if (!url || !width || !height) return null
return { url, width, height, alt: media.alt ?? polozka.nazev }
}
+263
View File
@@ -0,0 +1,263 @@
/*
* Styly veřejné části. Záměrně jeden soubor bez frameworku — web má tři
* typy stránek a Tailwind by sem přinesl build krok navíc bez užitku.
*/
:root {
--barva-text: #16181c;
--barva-tlumena: #6b7280;
--barva-pozadi: #ffffff;
--barva-plocha: #f5f5f4;
--barva-linka: #e2e0dd;
--barva-akcent: #7a5c3e;
--sirka: 1100px;
--radius: 6px;
}
@media (prefers-color-scheme: dark) {
:root {
--barva-text: #ececec;
--barva-tlumena: #a1a1aa;
--barva-pozadi: #141414;
--barva-plocha: #1e1e1e;
--barva-linka: #2e2e2e;
--barva-akcent: #c9a227;
}
}
* {
box-sizing: border-box;
}
html {
-webkit-text-size-adjust: 100%;
}
body {
margin: 0;
font-family: system-ui, -apple-system, 'Segoe UI', sans-serif;
font-size: 17px;
line-height: 1.6;
color: var(--barva-text);
background: var(--barva-pozadi);
-webkit-font-smoothing: antialiased;
}
img {
max-width: 100%;
height: auto;
}
a {
color: inherit;
}
/* ---------- hlavička a patička ---------- */
.hlavicka {
display: flex;
flex-wrap: wrap;
gap: 1rem;
align-items: baseline;
justify-content: space-between;
max-width: var(--sirka);
margin: 0 auto;
padding: 1.5rem 1.25rem;
border-bottom: 1px solid var(--barva-linka);
}
.znacka {
font-size: 1.35rem;
font-weight: 700;
letter-spacing: 0.01em;
text-decoration: none;
}
.znacka span {
display: block;
font-size: 0.8rem;
font-weight: 400;
text-transform: uppercase;
letter-spacing: 0.12em;
color: var(--barva-tlumena);
}
.hlavicka nav {
display: flex;
gap: 1.5rem;
}
.hlavicka nav a {
text-decoration: none;
color: var(--barva-tlumena);
}
.hlavicka nav a:hover {
color: var(--barva-text);
}
.paticka {
max-width: var(--sirka);
margin: 4rem auto 0;
padding: 1.5rem 1.25rem 3rem;
border-top: 1px solid var(--barva-linka);
font-size: 0.85rem;
color: var(--barva-tlumena);
}
.paticka p {
margin: 0.25rem 0;
}
/* ---------- obecná stránka ---------- */
.stranka {
max-width: var(--sirka);
margin: 0 auto;
padding: 2rem 1.25rem 0;
}
.stranka h1 {
margin: 0 0 1.5rem;
font-size: clamp(1.6rem, 4vw, 2.2rem);
line-height: 1.2;
}
.prazdno {
color: var(--barva-tlumena);
}
/* ---------- výpis nabídky ---------- */
.mrizka {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr));
gap: 1.75rem;
margin: 0;
padding: 0;
list-style: none;
}
.karta a {
display: block;
text-decoration: none;
color: inherit;
}
.karta__obrazek {
aspect-ratio: 4 / 3;
overflow: hidden;
background: var(--barva-plocha);
border-radius: var(--radius);
}
.karta__obrazek img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.25s ease;
}
.karta a:hover .karta__obrazek img {
transform: scale(1.03);
}
.karta__bezobrazku {
display: grid;
place-items: center;
height: 100%;
font-size: 0.85rem;
color: var(--barva-tlumena);
}
.karta h2 {
margin: 0.75rem 0 0.25rem;
font-size: 1.05rem;
line-height: 1.35;
}
.karta__cena {
margin: 0;
font-weight: 600;
color: var(--barva-akcent);
}
.karta__stav {
margin: 0.1rem 0 0;
font-size: 0.85rem;
color: var(--barva-tlumena);
}
/* Prodané položky zůstávají ve výpisu, ale nekřičí. */
.karta--prodano .karta__obrazek img {
filter: grayscale(1);
opacity: 0.65;
}
/* ---------- detail položky ---------- */
.zpet {
margin: 0 0 1rem;
font-size: 0.9rem;
}
.detail__cena {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
align-items: center;
margin: -0.75rem 0 1.75rem;
font-size: 1.4rem;
font-weight: 600;
color: var(--barva-akcent);
}
.znacka-stavu {
padding: 0.15rem 0.6rem;
border: 1px solid var(--barva-linka);
border-radius: 999px;
font-size: 0.8rem;
font-weight: 500;
color: var(--barva-tlumena);
}
.znacka-stavu--prodano {
border-color: transparent;
background: var(--barva-plocha);
}
.galerie {
display: grid;
gap: 1rem;
margin-bottom: 2rem;
}
.galerie img {
width: 100%;
border-radius: var(--radius);
}
.parametry {
width: 100%;
border-collapse: collapse;
margin-bottom: 2rem;
}
.parametry th,
.parametry td {
padding: 0.6rem 0;
text-align: left;
vertical-align: top;
border-bottom: 1px solid var(--barva-linka);
}
.parametry th {
width: 40%;
font-weight: 500;
color: var(--barva-tlumena);
}
.popis {
max-width: 65ch;
white-space: pre-line;
}
@@ -0,0 +1,24 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import type { Metadata } from 'next'
import config from '@payload-config'
import { NotFoundPage, generatePageMetadata } from '@payloadcms/next/views'
import { importMap } from '../importMap'
type Args = {
params: Promise<{
segments: string[]
}>
searchParams: Promise<{
[key: string]: string | string[]
}>
}
export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>
generatePageMetadata({ config, params, searchParams })
const NotFound = ({ params, searchParams }: Args) =>
NotFoundPage({ config, params, searchParams, importMap })
export default NotFound
@@ -0,0 +1,24 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import type { Metadata } from 'next'
import config from '@payload-config'
import { RootPage, generatePageMetadata } from '@payloadcms/next/views'
import { importMap } from '../importMap'
type Args = {
params: Promise<{
segments: string[]
}>
searchParams: Promise<{
[key: string]: string | string[]
}>
}
export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>
generatePageMetadata({ config, params, searchParams })
const Page = ({ params, searchParams }: Args) =>
RootPage({ config, params, searchParams, importMap })
export default Page
+52
View File
@@ -0,0 +1,52 @@
import { RscEntryLexicalCell as RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
import { RscEntryLexicalField as RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
import { LexicalDiffComponent as LexicalDiffComponent_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
import { InlineToolbarFeatureClient as InlineToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { HorizontalRuleFeatureClient as HorizontalRuleFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { UploadFeatureClient as UploadFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { BlockquoteFeatureClient as BlockquoteFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { RelationshipFeatureClient as RelationshipFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { LinkFeatureClient as LinkFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { ChecklistFeatureClient as ChecklistFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { OrderedListFeatureClient as OrderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { UnorderedListFeatureClient as UnorderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { IndentFeatureClient as IndentFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { AlignFeatureClient as AlignFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { HeadingFeatureClient as HeadingFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { ParagraphFeatureClient as ParagraphFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { InlineCodeFeatureClient as InlineCodeFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { SuperscriptFeatureClient as SuperscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { SubscriptFeatureClient as SubscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { StrikethroughFeatureClient as StrikethroughFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
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 { CollectionCards as CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1 } from '@payloadcms/next/rsc'
/** @type import('payload').ImportMap */
export const importMap = {
"@payloadcms/richtext-lexical/rsc#RscEntryLexicalCell": RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e,
"@payloadcms/richtext-lexical/rsc#RscEntryLexicalField": RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e,
"@payloadcms/richtext-lexical/rsc#LexicalDiffComponent": LexicalDiffComponent_44fe37237e0ebf4470c9990d8cb7b07e,
"@payloadcms/richtext-lexical/client#InlineToolbarFeatureClient": InlineToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#HorizontalRuleFeatureClient": HorizontalRuleFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#UploadFeatureClient": UploadFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#BlockquoteFeatureClient": BlockquoteFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#RelationshipFeatureClient": RelationshipFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#LinkFeatureClient": LinkFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#ChecklistFeatureClient": ChecklistFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#OrderedListFeatureClient": OrderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#UnorderedListFeatureClient": UnorderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#IndentFeatureClient": IndentFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#AlignFeatureClient": AlignFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#HeadingFeatureClient": HeadingFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#ParagraphFeatureClient": ParagraphFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#InlineCodeFeatureClient": InlineCodeFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#SuperscriptFeatureClient": SuperscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#SubscriptFeatureClient": SubscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@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/next/rsc#CollectionCards": CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1
}
+19
View File
@@ -0,0 +1,19 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import '@payloadcms/next/css'
import {
REST_DELETE,
REST_GET,
REST_OPTIONS,
REST_PATCH,
REST_POST,
REST_PUT,
} from '@payloadcms/next/routes'
export const GET = REST_GET(config)
export const POST = REST_POST(config)
export const DELETE = REST_DELETE(config)
export const PATCH = REST_PATCH(config)
export const PUT = REST_PUT(config)
export const OPTIONS = REST_OPTIONS(config)
@@ -0,0 +1,7 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import '@payloadcms/next/css'
import { GRAPHQL_PLAYGROUND_GET } from '@payloadcms/next/routes'
export const GET = GRAPHQL_PLAYGROUND_GET(config)
+8
View File
@@ -0,0 +1,8 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import { GRAPHQL_POST, REST_OPTIONS } from '@payloadcms/next/routes'
export const POST = GRAPHQL_POST(config)
export const OPTIONS = REST_OPTIONS(config)
View File
+31
View File
@@ -0,0 +1,31 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import '@payloadcms/next/css'
import type { ServerFunctionClient } from 'payload'
import { handleServerFunctions, RootLayout } from '@payloadcms/next/layouts'
import React from 'react'
import { importMap } from './admin/importMap.js'
import './custom.scss'
type Args = {
children: React.ReactNode
}
const serverFunction: ServerFunctionClient = async function (args) {
'use server'
return handleServerFunctions({
...args,
config,
importMap,
})
}
const Layout = ({ children }: Args) => (
<RootLayout config={config} importMap={importMap} serverFunction={serverFunction}>
{children}
</RootLayout>
)
export default Layout