Refactor CMS to handle ContentLayer and WordPress platforms
This commit refactors the CMS to handle two platforms: ContentLayer and WordPress. The CMS layer is abstracted into a core package, and separate implementations for each platform are created. This change allows the app to switch the CMS type based on environment variable, which can improve the flexibility of content management. It also updates several functions in the `server-sitemap.xml` route to accommodate these changes and generate sitemaps based on the CMS client. Further, documentation content and posts have been relocated to align with the new structure. Notably, this refactor is a comprehensive update to the way the CMS is structured and managed.
This commit is contained in:
@@ -1,12 +1,10 @@
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
import { notFound } from 'next/navigation';
|
||||
import Script from 'next/script';
|
||||
|
||||
import { allPosts } from 'contentlayer/generated';
|
||||
import { createCmsClient } from '@kit/cms';
|
||||
|
||||
import Post from '~/(marketing)/blog/_components/post';
|
||||
import appConfig from '~/config/app.config';
|
||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||
|
||||
export async function generateMetadata({
|
||||
@@ -14,14 +12,14 @@ export async function generateMetadata({
|
||||
}: {
|
||||
params: { slug: string };
|
||||
}): Promise<Metadata | undefined> {
|
||||
const post = allPosts.find((post) => post.slug === params.slug);
|
||||
const cms = await createCmsClient();
|
||||
const post = await cms.getContentItemById(params.slug);
|
||||
|
||||
if (!post) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const { title, date, description, image, slug } = post;
|
||||
const url = [appConfig.url, 'blog', slug].join('/');
|
||||
const { title, publishedAt, description, image } = post;
|
||||
|
||||
return Promise.resolve({
|
||||
title,
|
||||
@@ -30,8 +28,8 @@ export async function generateMetadata({
|
||||
title,
|
||||
description,
|
||||
type: 'article',
|
||||
publishedTime: date,
|
||||
url,
|
||||
publishedTime: publishedAt.toDateString(),
|
||||
url: post.url,
|
||||
images: image
|
||||
? [
|
||||
{
|
||||
@@ -49,8 +47,9 @@ export async function generateMetadata({
|
||||
});
|
||||
}
|
||||
|
||||
function BlogPost({ params }: { params: { slug: string } }) {
|
||||
const post = allPosts.find((post) => post.slug === params.slug);
|
||||
async function BlogPost({ params }: { params: { slug: string } }) {
|
||||
const cms = await createCmsClient();
|
||||
const post = await cms.getContentItemById(params.slug);
|
||||
|
||||
if (!post) {
|
||||
notFound();
|
||||
@@ -58,11 +57,7 @@ function BlogPost({ params }: { params: { slug: string } }) {
|
||||
|
||||
return (
|
||||
<div className={'container mx-auto'}>
|
||||
<Script id={'ld-json'} type="application/ld+json">
|
||||
{JSON.stringify(post.structuredData)}
|
||||
</Script>
|
||||
|
||||
<Post post={post} content={post.body.code} />
|
||||
<Post post={post} content={post.content} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { Post } from 'contentlayer/generated';
|
||||
|
||||
import { Cms } from '@kit/cms';
|
||||
import { Heading } from '@kit/ui/heading';
|
||||
import { If } from '@kit/ui/if';
|
||||
|
||||
@@ -7,9 +6,9 @@ import { CoverImage } from '~/(marketing)/blog/_components/cover-image';
|
||||
import { DateFormatter } from '~/(marketing)/blog/_components/date-formatter';
|
||||
|
||||
export const PostHeader: React.FC<{
|
||||
post: Post;
|
||||
post: Cms.ContentItem;
|
||||
}> = ({ post }) => {
|
||||
const { title, date, readingTime, description, image } = post;
|
||||
const { title, publishedAt, description, image } = post;
|
||||
|
||||
// NB: change this to display the post's image
|
||||
const displayImage = true;
|
||||
@@ -30,11 +29,8 @@ export const PostHeader: React.FC<{
|
||||
<div className="flex">
|
||||
<div className="flex flex-row items-center space-x-2 text-sm text-gray-600 dark:text-gray-400">
|
||||
<div>
|
||||
<DateFormatter dateString={date} />
|
||||
<DateFormatter dateString={publishedAt.toISOString()} />
|
||||
</div>
|
||||
|
||||
<span>·</span>
|
||||
<span>{readingTime} minutes reading</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
import type { Post } from 'contentlayer/generated';
|
||||
|
||||
import { Cms } from '@kit/cms';
|
||||
import { If } from '@kit/ui/if';
|
||||
|
||||
import { CoverImage } from '~/(marketing)/blog/_components/cover-image';
|
||||
import { DateFormatter } from '~/(marketing)/blog/_components/date-formatter';
|
||||
|
||||
type Props = {
|
||||
post: Post;
|
||||
post: Cms.ContentItem;
|
||||
preloadImage?: boolean;
|
||||
imageHeight?: string | number;
|
||||
};
|
||||
@@ -20,15 +19,16 @@ export function PostPreview({
|
||||
preloadImage,
|
||||
imageHeight,
|
||||
}: React.PropsWithChildren<Props>) {
|
||||
const { title, image, date, readingTime, description } = post;
|
||||
const { title, image, publishedAt, description } = post;
|
||||
const height = imageHeight ?? DEFAULT_IMAGE_HEIGHT;
|
||||
const url = post.url;
|
||||
|
||||
return (
|
||||
<div className="rounded-xl transition-shadow duration-500 dark:text-gray-800">
|
||||
<div className="rounded-xl transition-shadow duration-500">
|
||||
<If condition={image}>
|
||||
{(imageUrl) => (
|
||||
<div className="relative mb-2 w-full" style={{ height }}>
|
||||
<Link href={post.url}>
|
||||
<Link href={url}>
|
||||
<CoverImage
|
||||
preloadImage={preloadImage}
|
||||
title={title}
|
||||
@@ -40,27 +40,21 @@ export function PostPreview({
|
||||
</If>
|
||||
|
||||
<div className={'px-1'}>
|
||||
<div className="flex flex-col space-y-1 px-1 py-2">
|
||||
<h3 className="px-1 text-2xl font-bold leading-snug dark:text-white">
|
||||
<Link href={post.url} className="hover:underline">
|
||||
<div className="flex flex-col space-y-1 py-2">
|
||||
<h3 className="text-2xl font-bold leading-snug dark:text-white">
|
||||
<Link href={url} className="hover:underline">
|
||||
{title}
|
||||
</Link>
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="mb-2 flex flex-row items-center space-x-2 px-1 text-sm">
|
||||
<div className="text-gray-600 dark:text-gray-300">
|
||||
<DateFormatter dateString={date} />
|
||||
<div className="mb-2 flex flex-row items-center space-x-2 text-sm">
|
||||
<div className="text-muted-foreground">
|
||||
<DateFormatter dateString={publishedAt.toISOString()} />
|
||||
</div>
|
||||
|
||||
<span className="text-gray-600 dark:text-gray-300">·</span>
|
||||
|
||||
<span className="text-gray-600 dark:text-gray-300">
|
||||
{readingTime} mins reading
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p className="mb-4 px-1 text-sm leading-relaxed dark:text-gray-300">
|
||||
<p className="mb-4 text-sm leading-relaxed text-muted-foreground">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
import type { Post as PostType } from 'contentlayer/generated';
|
||||
import type { Cms } from '@kit/cms';
|
||||
import { ContentRenderer } from '@kit/cms';
|
||||
|
||||
import { PostHeader } from './post-header';
|
||||
|
||||
const Mdx = dynamic(() =>
|
||||
import('@kit/ui/mdx').then((mod) => ({ default: mod.Mdx })),
|
||||
);
|
||||
|
||||
export const Post: React.FC<{
|
||||
post: PostType;
|
||||
post: Cms.ContentItem;
|
||||
content: string;
|
||||
}> = ({ post, content }) => {
|
||||
return (
|
||||
@@ -17,7 +12,7 @@ export const Post: React.FC<{
|
||||
<PostHeader post={post} />
|
||||
|
||||
<article className={'mx-auto flex justify-center'}>
|
||||
<Mdx code={content} />
|
||||
<ContentRenderer content={content} />
|
||||
</article>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
import { allPosts } from 'contentlayer/generated';
|
||||
import { createCmsClient } from '@kit/cms';
|
||||
|
||||
import { GridList } from '~/(marketing)/_components/grid-list';
|
||||
import { SitePageHeader } from '~/(marketing)/_components/site-page-header';
|
||||
@@ -13,11 +13,11 @@ export const metadata: Metadata = {
|
||||
description: `Tutorials, Guides and Updates from our team`,
|
||||
};
|
||||
|
||||
function BlogPage() {
|
||||
const livePosts = allPosts.filter((post) => {
|
||||
const isProduction = appConfig.production;
|
||||
async function BlogPage() {
|
||||
const cms = await createCmsClient();
|
||||
|
||||
return isProduction ? post.live : true;
|
||||
const posts = await cms.getContentItems({
|
||||
type: 'post',
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -29,7 +29,7 @@ function BlogPage() {
|
||||
/>
|
||||
|
||||
<GridList>
|
||||
{livePosts.map((post, idx) => {
|
||||
{posts.map((post, idx) => {
|
||||
return <PostPreview key={idx} post={post} />;
|
||||
})}
|
||||
</GridList>
|
||||
|
||||
Reference in New Issue
Block a user