diff options
Diffstat (limited to 'src/app')
| -rw-r--r-- | src/app/(home)/_components/posts.tsx | 40 | ||||
| -rw-r--r-- | src/app/(home)/posts/[slug]/page.tsx | 145 | ||||
| -rw-r--r-- | src/app/(home)/posts/page.tsx | 133 | ||||
| -rw-r--r-- | src/app/(main)/(auth)/login/page.tsx (renamed from src/app/(auth)/login/page.tsx) | 2 | ||||
| -rw-r--r-- | src/app/(main)/(home)/(mdx)/about/page.mdx (renamed from src/app/(home)/(mdx)/about/page.mdx) | 0 | ||||
| -rw-r--r-- | src/app/(main)/(home)/_components/call-to-action.tsx (renamed from src/app/(home)/_components/call-to-action.tsx) | 0 | ||||
| -rw-r--r-- | src/app/(main)/(home)/_components/hero.tsx (renamed from src/app/(home)/_components/hero.tsx) | 4 | ||||
| -rw-r--r-- | src/app/(main)/(home)/_components/posts.tsx | 89 | ||||
| -rw-r--r-- | src/app/(main)/(home)/actions.ts (renamed from src/app/(home)/actions.ts) | 0 | ||||
| -rw-r--r-- | src/app/(main)/(home)/layout.tsx (renamed from src/app/(home)/layout.tsx) | 0 | ||||
| -rw-r--r-- | src/app/(main)/(home)/page.tsx (renamed from src/app/(home)/page.tsx) | 17 | ||||
| -rw-r--r-- | src/app/(main)/(home)/posts/[slug]/page.client.tsx (renamed from src/app/(home)/posts/[slug]/page.client.tsx) | 0 | ||||
| -rw-r--r-- | src/app/(main)/(home)/posts/[slug]/page.tsx | 266 | ||||
| -rw-r--r-- | src/app/(main)/(home)/posts/page.tsx | 203 | ||||
| -rw-r--r-- | src/app/(main)/(home)/tags/[...slug]/page.tsx (renamed from src/app/(home)/tags/[...slug]/page.tsx) | 2 | ||||
| -rw-r--r-- | src/app/(main)/(home)/tags/page.tsx (renamed from src/app/(home)/tags/page.tsx) | 2 | ||||
| -rw-r--r-- | src/app/(main)/api/auth/[...all]/route.ts (renamed from src/app/api/auth/[...all]/route.ts) | 0 | ||||
| -rw-r--r-- | src/app/(main)/api/comments/[...comment]/route.ts (renamed from src/app/api/comments/[...comment]/route.ts) | 0 | ||||
| -rw-r--r-- | src/app/(main)/api/search/route.ts (renamed from src/app/api/search/route.ts) | 0 | ||||
| -rw-r--r-- | src/app/(main)/banner.png/fonts/geist-regular-otf.json (renamed from src/app/banner.png/fonts/geist-regular-otf.json) | 0 | ||||
| -rw-r--r-- | src/app/(main)/banner.png/fonts/geist-semibold-otf.json (renamed from src/app/banner.png/fonts/geist-semibold-otf.json) | 0 | ||||
| -rw-r--r-- | src/app/(main)/banner.png/fonts/geistmono-regular-otf.json (renamed from src/app/banner.png/fonts/geistmono-regular-otf.json) | 0 | ||||
| -rw-r--r-- | src/app/(main)/banner.png/og.tsx (renamed from src/app/banner.png/og.tsx) | 0 | ||||
| -rw-r--r-- | src/app/(main)/banner.png/route.tsx (renamed from src/app/banner.png/route.tsx) | 2 | ||||
| -rw-r--r-- | src/app/(main)/layout.client.tsx (renamed from src/app/layout.client.tsx) | 0 | ||||
| -rw-r--r-- | src/app/(main)/layout.config.tsx (renamed from src/app/layout.config.tsx) | 0 | ||||
| -rw-r--r-- | src/app/(main)/layout.tsx (renamed from src/app/layout.tsx) | 6 | ||||
| -rw-r--r-- | src/app/(main)/not-found.tsx (renamed from src/app/not-found.tsx) | 0 | ||||
| -rw-r--r-- | src/app/(main)/og/[...slug]/fonts/geist-regular-otf.json (renamed from src/app/og/[...slug]/fonts/geist-regular-otf.json) | 0 | ||||
| -rw-r--r-- | src/app/(main)/og/[...slug]/fonts/geist-semibold-otf.json (renamed from src/app/og/[...slug]/fonts/geist-semibold-otf.json) | 0 | ||||
| -rw-r--r-- | src/app/(main)/og/[...slug]/fonts/geistmono-regular-otf.json (renamed from src/app/og/[...slug]/fonts/geistmono-regular-otf.json) | 0 | ||||
| -rw-r--r-- | src/app/(main)/og/[...slug]/og.tsx (renamed from src/app/og/[...slug]/og.tsx) | 0 | ||||
| -rw-r--r-- | src/app/(main)/og/[...slug]/route.tsx (renamed from src/app/og/[...slug]/route.tsx) | 2 | ||||
| -rw-r--r-- | src/app/(main)/provider.tsx (renamed from src/app/provider.tsx) | 0 | ||||
| -rw-r--r-- | src/app/(main)/rss.xml/route.ts (renamed from src/app/rss.xml/route.ts) | 4 | ||||
| -rw-r--r-- | src/app/(payload)/admin/[[...segments]]/not-found.tsx | 24 | ||||
| -rw-r--r-- | src/app/(payload)/admin/[[...segments]]/page.tsx | 24 | ||||
| -rw-r--r-- | src/app/(payload)/admin/importMap.js | 49 | ||||
| -rw-r--r-- | src/app/(payload)/api/[...slug]/route.ts | 20 | ||||
| -rw-r--r-- | src/app/(payload)/api/graphql-playground/route.ts | 6 | ||||
| -rw-r--r-- | src/app/(payload)/api/graphql/route.ts | 6 | ||||
| -rw-r--r-- | src/app/(payload)/custom.scss | 1 | ||||
| -rw-r--r-- | src/app/(payload)/layout.tsx | 30 | ||||
| -rw-r--r-- | src/app/icon.png | bin | 212793 -> 0 bytes |
44 files changed, 741 insertions, 336 deletions
diff --git a/src/app/(home)/_components/posts.tsx b/src/app/(home)/_components/posts.tsx deleted file mode 100644 index 8c8dc33..0000000 --- a/src/app/(home)/_components/posts.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { Icons } from '@/components/icons/icons'; -import { PostCard } from '@/components/posts/post-card'; -import { Section } from '@/components/section'; -import { buttonVariants } from '@/components/ui/button'; -import type { Page } from '@/lib/source'; -import Link from 'next/link'; - -export default function Posts({ posts }: { posts: Page[] }) { - return ( - <Section> - <div className='grid divide-y divide-dashed divide-border/70 text-left dark:divide-border'> - {posts.map((post) => { - const date = new Date(post.data.date).toDateString(); - return ( - <PostCard - title={post.data.title} - description={post.data.description ?? ''} - image={post.data.image} - url={post.url} - date={date} - key={post.url} - author={post.data.author} - tags={post.data.tags} - /> - ); - })} - <Link - href='/posts' - className={buttonVariants({ - variant: 'default', - className: 'group rounded-none py-4 sm:py-8', - })} - > - View More - <Icons.arrowUpRight className='group-hover:-rotate-12 ml-2 size-5 transition-transform' /> - </Link> - </div> - </Section> - ); -} diff --git a/src/app/(home)/posts/[slug]/page.tsx b/src/app/(home)/posts/[slug]/page.tsx deleted file mode 100644 index 15a6bfd..0000000 --- a/src/app/(home)/posts/[slug]/page.tsx +++ /dev/null @@ -1,145 +0,0 @@ -import { PostComments, Share } from '@/app/(home)/posts/[slug]/page.client'; -import { PostJsonLd } from '@/components/json-ld'; -import { Section } from '@/components/section'; -import { TagCard } from '@/components/tags/tag-card'; -import { createMetadata } from '@/lib/metadata'; -import { metadataImage } from '@/lib/metadata-image'; -import { type Page as MDXPage, getPost, getPosts } from '@/lib/source'; -import { cn } from '@/lib/utils'; -import { File, Files, Folder } from 'fumadocs-ui/components/files'; -import { InlineTOC } from 'fumadocs-ui/components/inline-toc'; -import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; -import defaultMdxComponents from 'fumadocs-ui/mdx'; -import type { Metadata } from 'next'; -import { notFound } from 'next/navigation'; -import Balancer from 'react-wrap-balancer'; -import { description as homeDescription } from 'src/app/layout.config'; - -function Header(props: { page: MDXPage; tags?: string[] }) { - const { page, tags } = props; - - return ( - <Section className='p-4 lg:p-6'> - <div - className={cn( - 'flex flex-col items-start justify-center gap-4 py-8 md:gap-6', - 'sm:items-center sm:rounded-lg sm:border sm:bg-muted/70 sm:px-8 sm:py-20 sm:shadow-xs sm:dark:bg-muted', - )} - > - <div className='flex flex-col gap-2 sm:text-center md:gap-4'> - <h1 className='max-w-4xl font-bold text-3xl leading-tight tracking-tight sm:text-4xl sm:leading-tight md:text-5xl md:leading-tight'> - <Balancer>{page.data.title}</Balancer> - </h1> - <p className='mx-auto max-w-4xl'> - <Balancer>{page.data.description}</Balancer> - </p> - </div> - <div className='flex flex-wrap gap-2'> - {tags?.map((tag) => ( - <TagCard name={tag} key={tag} className=' border border-border ' /> - ))} - </div> - </div> - </Section> - ); -} - -export default async function Page(props: { - params: Promise<{ slug: string }>; -}) { - const params = await props.params; - const page = getPost([params.slug]); - - if (!page) notFound(); - const { body: Mdx, toc, tags, lastModified } = page.data; - - const lastUpdate = lastModified ? new Date(lastModified) : undefined; - - return ( - <> - <Header page={page} tags={tags} /> - - <Section className='h-full' sectionClassName='flex flex-1'> - <article className='flex min-h-full flex-col lg:flex-row'> - <div className='flex flex-1 flex-col gap-4'> - <InlineTOC - items={toc} - className='rounded-none border-0 border-border/70 border-b border-dashed dark:border-border' - /> - <div className='prose min-w-0 flex-1 px-4'> - <Mdx - components={{ - ...defaultMdxComponents, - File, - Files, - Folder, - Tabs, - Tab, - }} - /> - </div> - <PostComments - slug={params.slug} - className='[&_form>div]:!rounded-none rounded-none border-0 border-border/70 border-t border-dashed dark:border-border' - /> - </div> - <div className='flex flex-col gap-4 p-4 text-sm lg:sticky lg:top-[4rem] lg:h-[calc(100vh-4rem)] lg:w-[250px] lg:self-start lg:overflow-y-auto lg:border-border/70 lg:border-l lg:border-dashed lg:dark:border-border'> - <div> - <p className='mb-1 text-fd-muted-foreground'>Written by</p> - <p className='font-medium'>{page.data.author}</p> - </div> - <div> - <p className='mb-1 text-fd-muted-foreground text-sm'> - Created At - </p> - <p className='font-medium'> - {new Date(page.data.date ?? page.file.name).toDateString()} - </p> - </div> - {lastUpdate && ( - <div> - <p className='mb-1 text-fd-muted-foreground text-sm'> - Updated At - </p> - <p className='font-medium'>{lastUpdate.toDateString()}</p> - </div> - )} - <Share url={page.url} /> - </div> - </article> - </Section> - <PostJsonLd page={page} /> - </> - ); -} - -export async function generateMetadata(props: { - params: Promise<{ slug: string }>; -}): Promise<Metadata> { - const params = await props.params; - const page = getPost([params.slug]); - - if (!page) notFound(); - - const title = page.data.title; - const description = page.data.description ?? homeDescription; - - return createMetadata( - metadataImage.withImage(page.slugs, { - title, - description, - openGraph: { - url: `/posts/${page.slugs.join('/')}`, - }, - alternates: { - canonical: page.url, - }, - }), - ); -} - -export function generateStaticParams(): { slug: string | undefined }[] { - return getPosts().map((page) => ({ - slug: page.slugs[0], - })); -} diff --git a/src/app/(home)/posts/page.tsx b/src/app/(home)/posts/page.tsx deleted file mode 100644 index fd0f912..0000000 --- a/src/app/(home)/posts/page.tsx +++ /dev/null @@ -1,133 +0,0 @@ -import { postsPerPage } from '@/app/layout.config'; -import { NumberedPagination } from '@/components/numbered-pagination'; -import { PostCard } from '@/components/posts/post-card'; -import { Section } from '@/components/section'; -import { createMetadata } from '@/lib/metadata'; -import { getSortedByDatePosts } from '@/lib/source'; -import type { Metadata, ResolvingMetadata } from 'next'; -import { notFound, redirect } from 'next/navigation'; - -export const dynamicParams = false; - -const totalPosts = getSortedByDatePosts().length; -const pageCount = Math.ceil(totalPosts / postsPerPage); - -const CurrentPostsCount = ({ - startIndex, - endIndex, -}: { - startIndex: number; - endIndex: number; -}) => { - const start = startIndex + 1; - const end = endIndex < totalPosts ? endIndex : totalPosts; - if (start === end) return <span>({start})</span>; - return ( - <span> - ({start}-{end}) - </span> - ); -}; - -const Pagination = ({ pageIndex }: { pageIndex: number }) => { - const handlePageChange = async (page: number) => { - 'use server'; - redirect(`/posts?page=${page}`); - }; - - return ( - <Section className='bg-dashed'> - <NumberedPagination - currentPage={pageIndex + 1} - totalPages={pageCount} - paginationItemsToDisplay={5} - onPageChange={handlePageChange} - /> - </Section> - ); -}; - -export default async function Page(props: { - searchParams: Promise<{ [key: string]: string | string[] | undefined }>; -}) { - const searchParams = await props.searchParams; - const pageIndex = searchParams.page - ? Number.parseInt(searchParams.page[0] ?? '', 10) - 1 - : 0; - if (pageIndex < 0 || pageIndex >= pageCount) notFound(); - - const startIndex = pageIndex * postsPerPage; - const endIndex = startIndex + postsPerPage; - const posts = getSortedByDatePosts().slice(startIndex, endIndex); - - return ( - <> - <Section className='p-4 lg:p-6'> - <h1 className='font-bold text-3xl leading-tight tracking-tighter md:text-4xl'> - All {totalPosts} Posts{' '} - <CurrentPostsCount startIndex={startIndex} endIndex={endIndex} /> - </h1> - </Section> - <Section className='h-full' sectionClassName='flex flex-1'> - <div className='grid divide-y divide-dashed divide-border/70 text-left dark:divide-border'> - {posts.map((post) => { - const date = new Date(post.data.date).toDateString(); - return ( - <PostCard - title={post.data.title} - description={post.data.description ?? ''} - image={post.data.image} - url={post.url} - date={date} - key={post.url} - author={post.data.author} - tags={post.data.tags} - /> - ); - })} - </div> - </Section> - {pageCount > 1 && <Pagination pageIndex={pageIndex} />} - </> - ); -} - -export const generateStaticParams = () => { - const slugs = Array.from({ length: pageCount }, (_, index) => ({ - slug: [(index + 1).toString()], - })); - - return [{ slug: [] }, ...slugs]; -}; - -type Props = { - params: Promise<{ slug: string[] }>; - searchParams: Promise<{ [key: string]: string | string[] | undefined }>; -}; - -export async function generateMetadata( - props: Props, - parent: ResolvingMetadata, -): Promise<Metadata> { - const params = await props.params; - const searchParams = await props.searchParams; - - const pageIndex = searchParams.page - ? Number.parseInt(searchParams.page as string, 10) - : 1; - - const isFirstPage = pageIndex === 1 || !searchParams.page; - const pageTitle = isFirstPage ? 'Posts' : `Posts - Page ${pageIndex}`; - const canonicalUrl = isFirstPage ? '/posts' : `/posts?page=${pageIndex}`; - - return createMetadata({ - title: pageTitle, - description: `Posts${!isFirstPage ? ` - Page ${pageIndex}` : ''}`, - openGraph: { - url: canonicalUrl, - }, - alternates: { - canonical: canonicalUrl, - }, - }); -} diff --git a/src/app/(auth)/login/page.tsx b/src/app/(main)/(auth)/login/page.tsx index 2469097..3ff59d3 100644 --- a/src/app/(auth)/login/page.tsx +++ b/src/app/(main)/(auth)/login/page.tsx @@ -1,6 +1,6 @@ 'use client'; -import { baseOptions, linkItems } from '@/app/layout.config'; +import { baseOptions, linkItems } from '@/app/(main)/layout.config'; import { Icons } from '@/components/icons/icons'; import { Header } from '@/components/sections/header'; import { Button } from '@/components/ui/button'; diff --git a/src/app/(home)/(mdx)/about/page.mdx b/src/app/(main)/(home)/(mdx)/about/page.mdx index 675f3a8..675f3a8 100644 --- a/src/app/(home)/(mdx)/about/page.mdx +++ b/src/app/(main)/(home)/(mdx)/about/page.mdx diff --git a/src/app/(home)/_components/call-to-action.tsx b/src/app/(main)/(home)/_components/call-to-action.tsx index b75298e..b75298e 100644 --- a/src/app/(home)/_components/call-to-action.tsx +++ b/src/app/(main)/(home)/_components/call-to-action.tsx diff --git a/src/app/(home)/_components/hero.tsx b/src/app/(main)/(home)/_components/hero.tsx index 8ac251b..04371ca 100644 --- a/src/app/(home)/_components/hero.tsx +++ b/src/app/(main)/(home)/_components/hero.tsx @@ -1,4 +1,4 @@ -import { baseOptions, linkItems } from '@/app/layout.config'; +import { baseOptions, linkItems } from '@/app/(main)/layout.config'; import { Icons } from '@/components/icons/icons'; import { Section } from '@/components/section'; import { buttonVariants } from '@/components/ui/button'; @@ -8,7 +8,7 @@ import * as motion from 'motion/react-client'; import Image from 'next/image'; import Link from 'next/link'; import Balancer from 'react-wrap-balancer'; -import heroImage from '../../../../public/images/gradient-noise-purple-azure-light.png'; +import heroImage from '../../../../../public/images/gradient-noise-purple-azure-light.png'; const Hero = () => { const links = getLinks(linkItems, baseOptions.githubUrl); diff --git a/src/app/(main)/(home)/_components/posts.tsx b/src/app/(main)/(home)/_components/posts.tsx new file mode 100644 index 0000000..00ada0c --- /dev/null +++ b/src/app/(main)/(home)/_components/posts.tsx @@ -0,0 +1,89 @@ +import { Icons } from '@/components/icons/icons'; +import { PostCard } from '@/components/posts/post-card'; +import { Section } from '@/components/section'; +import { buttonVariants } from '@/components/ui/button'; +import type { Page } from '@/lib/source'; +import type { BlogPost } from '@/lib/payload-posts'; +import Link from 'next/link'; + +// 统一的文章数据格式 +interface UnifiedPost { + title: string; + description: string; + image?: string | null; + url: string; + date: string; + author: string; + tags?: string[]; +} + +// 将 MDX Page 转换为统一格式 +function transformMdxPost(post: Page): UnifiedPost { + return { + title: post.data.title, + description: post.data.description ?? '', + image: post.data.image, + url: post.url, + date: new Date(post.data.date).toDateString(), + author: post.data.author, + tags: post.data.tags, + }; +} + +// 将 Payload BlogPost 转换为统一格式 +function transformPayloadPost(post: BlogPost): UnifiedPost { + return { + title: post.title, + description: post.description, + image: post.image, + url: post.url, + date: post.date.toDateString(), + author: post.author, + tags: post.tags, + }; +} + +interface PostsProps { + mdxPosts?: Page[]; + payloadPosts?: BlogPost[]; +} + +export default function Posts({ mdxPosts = [], payloadPosts = [] }: PostsProps) { + // 转换并合并所有文章 + const allPosts: UnifiedPost[] = [ + ...mdxPosts.map(transformMdxPost), + ...payloadPosts.map(transformPayloadPost), + ]; + + // 按日期排序 + allPosts.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); + + return ( + <Section> + <div className='grid divide-y divide-dashed divide-border/70 text-left dark:divide-border'> + {allPosts.map((post) => ( + <PostCard + title={post.title} + description={post.description} + image={post.image} + url={post.url} + date={post.date} + key={post.url} + author={post.author} + tags={post.tags} + /> + ))} + <Link + href='/posts' + className={buttonVariants({ + variant: 'default', + className: 'group rounded-none py-4 sm:py-8', + })} + > + View More + <Icons.arrowUpRight className='group-hover:-rotate-12 ml-2 size-5 transition-transform' /> + </Link> + </div> + </Section> + ); +} diff --git a/src/app/(home)/actions.ts b/src/app/(main)/(home)/actions.ts index fdb16ca..fdb16ca 100644 --- a/src/app/(home)/actions.ts +++ b/src/app/(main)/(home)/actions.ts diff --git a/src/app/(home)/layout.tsx b/src/app/(main)/(home)/layout.tsx index bd641df..bd641df 100644 --- a/src/app/(home)/layout.tsx +++ b/src/app/(main)/(home)/layout.tsx diff --git a/src/app/(home)/page.tsx b/src/app/(main)/(home)/page.tsx index da7da0f..a94193c 100644 --- a/src/app/(home)/page.tsx +++ b/src/app/(main)/(home)/page.tsx @@ -1,13 +1,18 @@ -import Hero from '@/app/(home)/_components/hero'; -import Posts from '@/app/(home)/_components/posts'; +import Hero from '@/app/(main)/(home)/_components/hero'; +import Posts from '@/app/(main)/(home)/_components/posts'; import { Icons } from '@/components/icons/icons'; import { Section } from '@/components/section'; import Separator from '@/components/separator'; import { getSortedByDatePosts } from '@/lib/source'; +import { getPublishedPosts } from '@/lib/payload-posts'; import { CTA } from './_components/call-to-action'; -export default function Home() { - const posts = getSortedByDatePosts().slice(0, 3); +export default async function Home() { + // 获取 MDX 文章(保留原有功能) + // const mdxPosts = getSortedByDatePosts().slice(0, 3); + + // 获取 Payload CMS 文章 + const { posts: payloadPosts } = await getPublishedPosts({ limit: 3 }); return ( <> @@ -15,13 +20,13 @@ export default function Home() { <Section className='py-8 sm:py-16'> <h2 className='text-center font-semibold text-2xl sm:text-3xl md:text-4xl lg:text-5xl'> <span className='inline-flex items-center gap-3'> - Posts{' '} + Posts <Icons.posts className='size-10 fill-fd-primary/30 text-fd-primary transition-transform hover:rotate-12 hover:scale-125' /> </span> </h2> </Section> <Separator /> - <Posts posts={posts} /> + <Posts mdxPosts={[]} payloadPosts={payloadPosts} /> <Separator /> <CTA /> </> diff --git a/src/app/(home)/posts/[slug]/page.client.tsx b/src/app/(main)/(home)/posts/[slug]/page.client.tsx index 7a97f56..7a97f56 100644 --- a/src/app/(home)/posts/[slug]/page.client.tsx +++ b/src/app/(main)/(home)/posts/[slug]/page.client.tsx diff --git a/src/app/(main)/(home)/posts/[slug]/page.tsx b/src/app/(main)/(home)/posts/[slug]/page.tsx new file mode 100644 index 0000000..fa096b6 --- /dev/null +++ b/src/app/(main)/(home)/posts/[slug]/page.tsx @@ -0,0 +1,266 @@ +import { + PostComments, + Share, +} from '@/app/(main)/(home)/posts/[slug]/page.client'; +import { PostJsonLd } from '@/components/json-ld'; +import { RichText } from '@/components/rich-text'; +import { Section } from '@/components/section'; +import { TagCard } from '@/components/tags/tag-card'; +import { createMetadata } from '@/lib/metadata'; +import { metadataImage } from '@/lib/metadata-image'; +import { + getPostBySlug, + getAllPostSlugs, + type BlogPost, +} from '@/lib/payload-posts'; +import { type Page as MDXPage, getPost, getPosts } from '@/lib/source'; +import { cn } from '@/lib/utils'; +import { File, Files, Folder } from 'fumadocs-ui/components/files'; +import { InlineTOC } from 'fumadocs-ui/components/inline-toc'; +import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; +import defaultMdxComponents from 'fumadocs-ui/mdx'; +import type { Metadata } from 'next'; +import { notFound } from 'next/navigation'; +import Balancer from 'react-wrap-balancer'; +import { description as homeDescription } from '@/app/(main)/layout.config'; + +// MDX 文章 Header +function MdxHeader(props: { page: MDXPage; tags?: string[] }) { + const { page, tags } = props; + + return ( + <Section className="p-4 lg:p-6"> + <div + className={cn( + 'flex flex-col items-start justify-center gap-4 py-8 md:gap-6', + 'sm:items-center sm:rounded-lg sm:border sm:bg-muted/70 sm:px-8 sm:py-20 sm:shadow-xs sm:dark:bg-muted' + )} + > + <div className="flex flex-col gap-2 sm:text-center md:gap-4"> + <h1 className="max-w-4xl font-bold text-3xl leading-tight tracking-tight sm:text-4xl sm:leading-tight md:text-5xl md:leading-tight"> + <Balancer>{page.data.title}</Balancer> + </h1> + <p className="mx-auto max-w-4xl"> + <Balancer>{page.data.description}</Balancer> + </p> + </div> + <div className="flex flex-wrap gap-2"> + {tags?.map((tag) => ( + <TagCard name={tag} key={tag} className=" border border-border " /> + ))} + </div> + </div> + </Section> + ); +} + +// Payload 文章 Header +function PayloadHeader(props: { post: BlogPost }) { + const { post } = props; + + return ( + <Section className="p-4 lg:p-6"> + <div + className={cn( + 'flex flex-col items-start justify-center gap-4 py-8 md:gap-6', + 'sm:items-center sm:rounded-lg sm:border sm:bg-muted/70 sm:px-8 sm:py-20 sm:shadow-xs sm:dark:bg-muted' + )} + > + <div className="flex flex-col gap-2 sm:text-center md:gap-4"> + <h1 className="max-w-4xl font-bold text-3xl leading-tight tracking-tight sm:text-4xl sm:leading-tight md:text-5xl md:leading-tight"> + <Balancer>{post.title}</Balancer> + </h1> + <p className="mx-auto max-w-4xl"> + <Balancer>{post.description}</Balancer> + </p> + </div> + <div className="flex flex-wrap gap-2"> + {post.tags?.map((tag) => ( + <TagCard name={tag} key={tag} className=" border border-border " /> + ))} + </div> + </div> + </Section> + ); +} + +// MDX 文章内容 +function MdxContent({ page }: { page: MDXPage }) { + const { body: Mdx, toc, tags, lastModified } = page.data; + const lastUpdate = lastModified ? new Date(lastModified) : undefined; + + return ( + <> + <MdxHeader page={page} tags={tags} /> + + <Section className="h-full" sectionClassName="flex flex-1"> + <article className="flex min-h-full flex-col lg:flex-row"> + <div className="flex flex-1 flex-col gap-4"> + <InlineTOC + items={toc} + className="rounded-none border-0 border-border/70 border-b border-dashed dark:border-border" + /> + <div className="prose min-w-0 flex-1 px-4"> + <Mdx + components={{ + ...defaultMdxComponents, + File, + Files, + Folder, + Tabs, + Tab, + }} + /> + </div> + <PostComments + slug={page.slugs[0] ?? ''} + className="[&_form>div]:!rounded-none rounded-none border-0 border-border/70 border-t border-dashed dark:border-border" + /> + </div> + <div className="flex flex-col gap-4 p-4 text-sm lg:sticky lg:top-[4rem] lg:h-[calc(100vh-4rem)] lg:w-[250px] lg:self-start lg:overflow-y-auto lg:border-border/70 lg:border-l lg:border-dashed lg:dark:border-border"> + <div> + <p className="mb-1 text-fd-muted-foreground">Written by</p> + <p className="font-medium">{page.data.author}</p> + </div> + <div> + <p className="mb-1 text-fd-muted-foreground text-sm">Created At</p> + <p className="font-medium"> + {new Date(page.data.date ?? page.file.name).toDateString()} + </p> + </div> + {lastUpdate && ( + <div> + <p className="mb-1 text-fd-muted-foreground text-sm">Updated At</p> + <p className="font-medium">{lastUpdate.toDateString()}</p> + </div> + )} + <Share url={page.url} /> + </div> + </article> + </Section> + <PostJsonLd page={page} /> + </> + ); +} + +// Payload 文章内容 +function PayloadContent({ post }: { post: BlogPost }) { + return ( + <> + <PayloadHeader post={post} /> + + <Section className="h-full" sectionClassName="flex flex-1"> + <article className="flex min-h-full flex-col lg:flex-row"> + <div className="flex flex-1 flex-col gap-4"> + <RichText + content={post.content as Record<string, unknown>} + className="flex-1 px-4" + enableProse={true} + /> + <PostComments + slug={post.slug} + className="[&_form>div]:!rounded-none rounded-none border-0 border-border/70 border-t border-dashed dark:border-border" + /> + </div> + <div className="flex flex-col gap-4 p-4 text-sm lg:sticky lg:top-[4rem] lg:h-[calc(100vh-4rem)] lg:w-[250px] lg:self-start lg:overflow-y-auto lg:border-border/70 lg:border-l lg:border-dashed lg:dark:border-border"> + <div> + <p className="mb-1 text-fd-muted-foreground">Written by</p> + <p className="font-medium">{post.author}</p> + </div> + <div> + <p className="mb-1 text-fd-muted-foreground text-sm">Created At</p> + <p className="font-medium">{post.date.toDateString()}</p> + </div> + <div> + <p className="mb-1 text-fd-muted-foreground text-sm">Updated At</p> + <p className="font-medium">{post.updatedAt.toDateString()}</p> + </div> + <Share url={post.url} /> + </div> + </article> + </Section> + </> + ); +} + +export default async function Page(props: { + params: Promise<{ slug: string }>; +}) { + const params = await props.params; + + // 先尝试获取 MDX 文章 + const mdxPage = getPost([params.slug]); + + if (mdxPage) { + return <MdxContent page={mdxPage} />; + } + + // 再尝试获取 Payload 文章 + const payloadPost = await getPostBySlug(params.slug); + + if (payloadPost) { + return <PayloadContent post={payloadPost} />; + } + + // 都找不到则 404 + notFound(); +} + +export async function generateMetadata(props: { + params: Promise<{ slug: string }>; +}): Promise<Metadata> { + const params = await props.params; + + // 先尝试 MDX 文章 + const mdxPage = getPost([params.slug]); + + if (mdxPage) { + const title = mdxPage.data.title; + const description = mdxPage.data.description ?? homeDescription; + + return createMetadata( + metadataImage.withImage(mdxPage.slugs, { + title, + description, + openGraph: { + url: `/posts/${mdxPage.slugs.join('/')}`, + }, + alternates: { + canonical: mdxPage.url, + }, + }) + ); + } + + // 再尝试 Payload 文章 + const payloadPost = await getPostBySlug(params.slug); + + if (payloadPost) { + return createMetadata({ + title: payloadPost.title, + description: payloadPost.description || homeDescription, + openGraph: { + url: `/posts/${payloadPost.slug}`, + }, + alternates: { + canonical: `/posts/${payloadPost.slug}`, + }, + }); + } + + return {}; +} + +export async function generateStaticParams(): Promise<{ slug: string }[]> { + // MDX 文章的 slugs + const mdxSlugs = getPosts() + .map((page) => page.slugs[0]) + .filter((slug): slug is string => !!slug) + .map((slug) => ({ slug })); + + // Payload 文章的 slugs + const payloadSlugs = await getAllPostSlugs(); + const payloadParams = payloadSlugs.map((slug) => ({ slug })); + + return [...mdxSlugs, ...payloadParams]; +} diff --git a/src/app/(main)/(home)/posts/page.tsx b/src/app/(main)/(home)/posts/page.tsx new file mode 100644 index 0000000..00e51c4 --- /dev/null +++ b/src/app/(main)/(home)/posts/page.tsx @@ -0,0 +1,203 @@ +import { postsPerPage } from '@/app/(main)/layout.config'; +import { NumberedPagination } from '@/components/numbered-pagination'; +import { PostCard } from '@/components/posts/post-card'; +import { Section } from '@/components/section'; +import { createMetadata } from '@/lib/metadata'; +import { getPublishedPosts, type BlogPost } from '@/lib/payload-posts'; +import { getSortedByDatePosts, type Page } from '@/lib/source'; +import type { Metadata, ResolvingMetadata } from 'next'; +import { notFound, redirect } from 'next/navigation'; + +// 统一的文章类型 +interface UnifiedPost { + id: string; + title: string; + description: string; + image?: string; + url: string; + date: Date; + author: string; + tags?: string[]; + source: 'mdx' | 'payload'; +} + +// 将 MDX 文章转换为统一格式 +function transformMdxPost(post: Page): UnifiedPost { + return { + id: `mdx-${post.url}`, + title: post.data.title, + description: post.data.description ?? '', + image: post.data.image, + url: post.url, + date: new Date(post.data.date), + author: post.data.author, + tags: post.data.tags, + source: 'mdx', + }; +} + +// 将 Payload 文章转换为统一格式 +function transformPayloadPost(post: BlogPost): UnifiedPost { + return { + id: `payload-${post.id}`, + title: post.title, + description: post.description, + image: post.image, + url: post.url, + date: post.date, + author: post.author, + tags: post.tags, + source: 'payload', + }; +} + +// 获取所有文章(合并 MDX 和 Payload) +async function getAllPosts(): Promise<UnifiedPost[]> { + // 获取 MDX 文章 + const mdxPosts = getSortedByDatePosts().map(transformMdxPost); + + // 获取 Payload 文章(获取全部,用于分页计算) + const { posts: payloadPosts } = await getPublishedPosts({ limit: 1000 }); + const transformedPayloadPosts = payloadPosts.map(transformPayloadPost); + + // 合并并按日期排序 + const allPosts = [...mdxPosts, ...transformedPayloadPosts]; + allPosts.sort((a, b) => b.date.getTime() - a.date.getTime()); + + return allPosts; +} + +const CurrentPostsCount = ({ + startIndex, + endIndex, + totalPosts, +}: { + startIndex: number; + endIndex: number; + totalPosts: number; +}) => { + const start = startIndex + 1; + const end = endIndex < totalPosts ? endIndex : totalPosts; + if (start === end) return <span>({start})</span>; + return ( + <span> + ({start}-{end}) + </span> + ); +}; + +const Pagination = ({ + pageIndex, + pageCount, +}: { + pageIndex: number; + pageCount: number; +}) => { + const handlePageChange = async (page: number) => { + 'use server'; + redirect(`/posts?page=${page}`); + }; + + return ( + <Section className='bg-dashed'> + <NumberedPagination + currentPage={pageIndex + 1} + totalPages={pageCount} + paginationItemsToDisplay={5} + onPageChange={handlePageChange} + /> + </Section> + ); +}; + +export default async function Page(props: { + searchParams: Promise<{ [key: string]: string | string[] | undefined }>; +}) { + const searchParams = await props.searchParams; + + // 获取所有文章 + const allPosts = await getAllPosts(); + const totalPosts = allPosts.length; + const pageCount = Math.ceil(totalPosts / postsPerPage); + + const pageIndex = searchParams.page + ? Number.parseInt( + Array.isArray(searchParams.page) + ? searchParams.page[0] ?? '' + : searchParams.page, + 10 + ) - 1 + : 0; + + if (pageIndex < 0 || (pageCount > 0 && pageIndex >= pageCount)) notFound(); + + const startIndex = pageIndex * postsPerPage; + const endIndex = startIndex + postsPerPage; + const posts = allPosts.slice(startIndex, endIndex); + + return ( + <> + <Section className='p-4 lg:p-6'> + <h1 className='font-bold text-3xl leading-tight tracking-tighter md:text-4xl'> + All {totalPosts} Posts{' '} + <CurrentPostsCount + startIndex={startIndex} + endIndex={endIndex} + totalPosts={totalPosts} + /> + </h1> + </Section> + <Section className='h-full' sectionClassName='flex flex-1'> + <div className='grid divide-y divide-dashed divide-border/70 text-left dark:divide-border'> + {posts.map((post) => { + const date = post.date.toDateString(); + return ( + <PostCard + title={post.title} + description={post.description} + image={post.image} + url={post.url} + date={date} + key={post.id} + author={post.author} + tags={post.tags} + /> + ); + })} + </div> + </Section> + {pageCount > 1 && <Pagination pageIndex={pageIndex} pageCount={pageCount} />} + </> + ); +} + +type Props = { + params: Promise<{ slug: string[] }>; + searchParams: Promise<{ [key: string]: string | string[] | undefined }>; +}; + +export async function generateMetadata( + props: Props, + parent: ResolvingMetadata +): Promise<Metadata> { + const searchParams = await props.searchParams; + + const pageIndex = searchParams.page + ? Number.parseInt(searchParams.page as string, 10) + : 1; + + const isFirstPage = pageIndex === 1 || !searchParams.page; + const pageTitle = isFirstPage ? 'Posts' : `Posts - Page ${pageIndex}`; + const canonicalUrl = isFirstPage ? '/posts' : `/posts?page=${pageIndex}`; + + return createMetadata({ + title: pageTitle, + description: `Posts${!isFirstPage ? ` - Page ${pageIndex}` : ''}`, + openGraph: { + url: canonicalUrl, + }, + alternates: { + canonical: canonicalUrl, + }, + }); +} diff --git a/src/app/(home)/tags/[...slug]/page.tsx b/src/app/(main)/(home)/tags/[...slug]/page.tsx index 9479705..c1ce96b 100644 --- a/src/app/(home)/tags/[...slug]/page.tsx +++ b/src/app/(main)/(home)/tags/[...slug]/page.tsx @@ -1,4 +1,4 @@ -import { postsPerPage } from '@/app/layout.config'; +import { postsPerPage } from '@/app/(main)/layout.config'; import { Icons } from '@/components/icons/icons'; import { TagJsonLd } from '@/components/json-ld'; import { NumberedPagination } from '@/components/numbered-pagination'; diff --git a/src/app/(home)/tags/page.tsx b/src/app/(main)/(home)/tags/page.tsx index 54fb423..9138fde 100644 --- a/src/app/(home)/tags/page.tsx +++ b/src/app/(main)/(home)/tags/page.tsx @@ -1,4 +1,4 @@ -import { title as homeTitle } from '@/app/layout.config'; +import { title as homeTitle } from '@/app/(main)/layout.config'; import { Section } from '@/components/section'; import { TagCard } from '@/components/tags/tag-card'; import { createMetadata } from '@/lib/metadata'; diff --git a/src/app/api/auth/[...all]/route.ts b/src/app/(main)/api/auth/[...all]/route.ts index 677b24c..677b24c 100644 --- a/src/app/api/auth/[...all]/route.ts +++ b/src/app/(main)/api/auth/[...all]/route.ts diff --git a/src/app/api/comments/[...comment]/route.ts b/src/app/(main)/api/comments/[...comment]/route.ts index 1da87db..1da87db 100644 --- a/src/app/api/comments/[...comment]/route.ts +++ b/src/app/(main)/api/comments/[...comment]/route.ts diff --git a/src/app/api/search/route.ts b/src/app/(main)/api/search/route.ts index 3c99f5c..3c99f5c 100644 --- a/src/app/api/search/route.ts +++ b/src/app/(main)/api/search/route.ts diff --git a/src/app/banner.png/fonts/geist-regular-otf.json b/src/app/(main)/banner.png/fonts/geist-regular-otf.json index f220c87..f220c87 100644 --- a/src/app/banner.png/fonts/geist-regular-otf.json +++ b/src/app/(main)/banner.png/fonts/geist-regular-otf.json diff --git a/src/app/banner.png/fonts/geist-semibold-otf.json b/src/app/(main)/banner.png/fonts/geist-semibold-otf.json index c119360..c119360 100644 --- a/src/app/banner.png/fonts/geist-semibold-otf.json +++ b/src/app/(main)/banner.png/fonts/geist-semibold-otf.json diff --git a/src/app/banner.png/fonts/geistmono-regular-otf.json b/src/app/(main)/banner.png/fonts/geistmono-regular-otf.json index f4200df..f4200df 100644 --- a/src/app/banner.png/fonts/geistmono-regular-otf.json +++ b/src/app/(main)/banner.png/fonts/geistmono-regular-otf.json diff --git a/src/app/banner.png/og.tsx b/src/app/(main)/banner.png/og.tsx index 1a520c0..1a520c0 100644 --- a/src/app/banner.png/og.tsx +++ b/src/app/(main)/banner.png/og.tsx diff --git a/src/app/banner.png/route.tsx b/src/app/(main)/banner.png/route.tsx index 1cd53ac..d3bfdc8 100644 --- a/src/app/banner.png/route.tsx +++ b/src/app/(main)/banner.png/route.tsx @@ -1,4 +1,4 @@ -import { generateOGImage } from '@/app/banner.png/og'; +import { generateOGImage } from '@/app/(main)/banner.png/og'; async function loadAssets(): Promise< { name: string; data: Buffer; weight: 400 | 600; style: 'normal' }[] diff --git a/src/app/layout.client.tsx b/src/app/(main)/layout.client.tsx index 35726ba..35726ba 100644 --- a/src/app/layout.client.tsx +++ b/src/app/(main)/layout.client.tsx diff --git a/src/app/layout.config.tsx b/src/app/(main)/layout.config.tsx index f9efebb..f9efebb 100644 --- a/src/app/layout.config.tsx +++ b/src/app/(main)/layout.config.tsx diff --git a/src/app/layout.tsx b/src/app/(main)/layout.tsx index c7d2aaf..c9e7dee 100644 --- a/src/app/layout.tsx +++ b/src/app/(main)/layout.tsx @@ -5,9 +5,9 @@ import type { ReactNode } from 'react'; import '@/styles/globals.css'; import 'katex/dist/katex.css'; import { baseUrl } from '@/lib/constants'; -import { Body } from './layout.client'; -import { description as homeDescription } from './layout.config'; -import { Provider } from './provider'; +import { Body } from '@/app/(main)/layout.client'; +import { description as homeDescription } from '@/app/(main)/layout.config'; +import { Provider } from '@/app/(main)/provider'; const geistSans = Geist({ variable: '--font-geist-sans', diff --git a/src/app/not-found.tsx b/src/app/(main)/not-found.tsx index ecec57a..ecec57a 100644 --- a/src/app/not-found.tsx +++ b/src/app/(main)/not-found.tsx diff --git a/src/app/og/[...slug]/fonts/geist-regular-otf.json b/src/app/(main)/og/[...slug]/fonts/geist-regular-otf.json index f220c87..f220c87 100644 --- a/src/app/og/[...slug]/fonts/geist-regular-otf.json +++ b/src/app/(main)/og/[...slug]/fonts/geist-regular-otf.json diff --git a/src/app/og/[...slug]/fonts/geist-semibold-otf.json b/src/app/(main)/og/[...slug]/fonts/geist-semibold-otf.json index c119360..c119360 100644 --- a/src/app/og/[...slug]/fonts/geist-semibold-otf.json +++ b/src/app/(main)/og/[...slug]/fonts/geist-semibold-otf.json diff --git a/src/app/og/[...slug]/fonts/geistmono-regular-otf.json b/src/app/(main)/og/[...slug]/fonts/geistmono-regular-otf.json index f4200df..f4200df 100644 --- a/src/app/og/[...slug]/fonts/geistmono-regular-otf.json +++ b/src/app/(main)/og/[...slug]/fonts/geistmono-regular-otf.json diff --git a/src/app/og/[...slug]/og.tsx b/src/app/(main)/og/[...slug]/og.tsx index 5754e96..5754e96 100644 --- a/src/app/og/[...slug]/og.tsx +++ b/src/app/(main)/og/[...slug]/og.tsx diff --git a/src/app/og/[...slug]/route.tsx b/src/app/(main)/og/[...slug]/route.tsx index 8738616..d713923 100644 --- a/src/app/og/[...slug]/route.tsx +++ b/src/app/(main)/og/[...slug]/route.tsx @@ -1,4 +1,4 @@ -import { generateOGImage } from '@/app/og/[...slug]/og'; +import { generateOGImage } from '@/app/(main)/og/[...slug]/og'; import { metadataImage } from '@/lib/metadata-image'; import type { ImageResponse } from 'next/og'; diff --git a/src/app/provider.tsx b/src/app/(main)/provider.tsx index 085bf50..085bf50 100644 --- a/src/app/provider.tsx +++ b/src/app/(main)/provider.tsx diff --git a/src/app/rss.xml/route.ts b/src/app/(main)/rss.xml/route.ts index 6a3acf6..ee06a40 100644 --- a/src/app/rss.xml/route.ts +++ b/src/app/(main)/rss.xml/route.ts @@ -1,5 +1,5 @@ -import { description, title } from '@/app/layout.config'; -import { owner } from '@/app/layout.config'; +import { description, title } from '@/app/(main)/layout.config'; +import { owner } from '@/app/(main)/layout.config'; import { baseUrl } from '@/lib/constants'; import { getPosts } from '@/lib/source'; import { Feed } from 'feed'; diff --git a/src/app/(payload)/admin/[[...segments]]/not-found.tsx b/src/app/(payload)/admin/[[...segments]]/not-found.tsx new file mode 100644 index 0000000..b28df09 --- /dev/null +++ b/src/app/(payload)/admin/[[...segments]]/not-found.tsx @@ -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, importMap, params, searchParams }); + +export default NotFound; diff --git a/src/app/(payload)/admin/[[...segments]]/page.tsx b/src/app/(payload)/admin/[[...segments]]/page.tsx new file mode 100644 index 0000000..f04f258 --- /dev/null +++ b/src/app/(payload)/admin/[[...segments]]/page.tsx @@ -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; diff --git a/src/app/(payload)/admin/importMap.js b/src/app/(payload)/admin/importMap.js new file mode 100644 index 0000000..5bc8ec3 --- /dev/null +++ b/src/app/(payload)/admin/importMap.js @@ -0,0 +1,49 @@ +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' + +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 +} diff --git a/src/app/(payload)/api/[...slug]/route.ts b/src/app/(payload)/api/[...slug]/route.ts new file mode 100644 index 0000000..c3de612 --- /dev/null +++ b/src/app/(payload)/api/[...slug]/route.ts @@ -0,0 +1,20 @@ +/* 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) diff --git a/src/app/(payload)/api/graphql-playground/route.ts b/src/app/(payload)/api/graphql-playground/route.ts new file mode 100644 index 0000000..c14156d --- /dev/null +++ b/src/app/(payload)/api/graphql-playground/route.ts @@ -0,0 +1,6 @@ +/* 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_PLAYGROUND_GET } from '@payloadcms/next/routes'; + +export const GET = GRAPHQL_PLAYGROUND_GET(config); diff --git a/src/app/(payload)/api/graphql/route.ts b/src/app/(payload)/api/graphql/route.ts new file mode 100644 index 0000000..65fcf23 --- /dev/null +++ b/src/app/(payload)/api/graphql/route.ts @@ -0,0 +1,6 @@ +/* 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 } from '@payloadcms/next/routes'; + +export const POST = GRAPHQL_POST(config); diff --git a/src/app/(payload)/custom.scss b/src/app/(payload)/custom.scss new file mode 100644 index 0000000..f38c2f0 --- /dev/null +++ b/src/app/(payload)/custom.scss @@ -0,0 +1 @@ +/* Add custom Payload admin styles here */ diff --git a/src/app/(payload)/layout.tsx b/src/app/(payload)/layout.tsx new file mode 100644 index 0000000..f14247a --- /dev/null +++ b/src/app/(payload)/layout.tsx @@ -0,0 +1,30 @@ +/* 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; diff --git a/src/app/icon.png b/src/app/icon.png Binary files differdeleted file mode 100644 index 7532d9b..0000000 --- a/src/app/icon.png +++ /dev/null |
