fix: category pages integration

This commit is contained in:
RizqiSyahrendra 2025-03-11 16:48:30 +07:00
parent 66fdb977e9
commit e9c800ab95
5 changed files with 139 additions and 20 deletions

View File

@ -2,14 +2,24 @@ import { BeforeFooterBlock } from "@/components/Blocks/BeforeFooter";
import { BlogCardItemSkeleton } from "@/components/Blogs/BlogCardItem"; import { BlogCardItemSkeleton } from "@/components/Blogs/BlogCardItem";
import Blogs from "@/components/Blogs/Blogs"; import Blogs from "@/components/Blogs/Blogs";
import HeroOther from "@/components/HeroOther"; import HeroOther from "@/components/HeroOther";
import { fetchBlogCategoryBySlug } from "@/services/payload/blog";
import { getDefaultMetadata } from "@/utils/metadata"; import { getDefaultMetadata } from "@/utils/metadata";
import { sanitizePageNumber } from "@/utils/sanitize"; import { sanitizePageNumber } from "@/utils/sanitize";
import { Metadata } from "next"; import { Metadata } from "next";
// import { headers } from "next/headers"; import { headers } from "next/headers";
import { notFound } from "next/navigation";
import { Suspense } from "react"; import { Suspense } from "react";
export async function generateMetadata(): Promise<Metadata> { export async function generateMetadata({ params }: { params: Promise<{ path: string[0] }> }): Promise<Metadata> {
const path = (await params)?.path;
const paramsCategory = path?.[0] ?? "";
const metadata = await getDefaultMetadata(); const metadata = await getDefaultMetadata();
const category = await fetchBlogCategoryBySlug(paramsCategory);
if (!!category?.data) {
metadata.title = `${category.data.name} Archives - ${metadata.openGraph?.siteName}`;
}
return metadata; return metadata;
} }
@ -21,19 +31,92 @@ export default async function CategoryPage({
searchParams?: Promise<{ page?: string; s?: string }>; searchParams?: Promise<{ page?: string; s?: string }>;
}) { }) {
const path = (await params)?.path; const path = (await params)?.path;
// const headersList = await headers(); const paramsCategory = path?.[0] ?? "";
// const paramsCategory = path?.[0] ?? "";
const paramsPage = path?.[2] ?? ""; const paramsPage = path?.[2] ?? "";
const paramsSearch = (await searchParams)?.s; const paramsSearch = (await searchParams)?.s;
const page = sanitizePageNumber(paramsPage); const page = sanitizePageNumber(paramsPage);
const headersList = await headers();
const mainUrl = headersList.get("x-main-url");
const fullUrl = headersList.get("x-full-url");
const siteName = headersList.get("x-site-name");
const category = await fetchBlogCategoryBySlug(paramsCategory);
if (!category) {
notFound();
}
const jsonLd = {
"@context": "https://schema.org",
"@graph": [
{
"@type": "CollectionPage",
"@id": fullUrl,
url: fullUrl,
name: `${category.data.name} - ${siteName}`,
isPartOf: {
"@id": `${mainUrl}/#website`,
},
primaryImageOfPage: {
"@id": `${fullUrl}#primaryimage`,
},
image: {
"@id": `${fullUrl}#primaryimage`,
},
inLanguage: "en-US",
},
{
"@type": "WebSite",
"@id": `${mainUrl}/#website`,
url: `${mainUrl}/`,
name: siteName,
description: "",
publisher: {
"@id": `${mainUrl}/#organization`,
},
potentialAction: [
{
"@type": "SearchAction",
target: {
"@type": "EntryPoint",
urlTemplate: `${mainUrl}/?s={search_term_string}`,
},
"query-input": {
"@type": "PropertyValueSpecification",
valueRequired: true,
valueName: "search_term_string",
},
},
],
inLanguage: "en-US",
},
{
"@type": "Organization",
"@id": `${mainUrl}/#organization`,
name: siteName,
url: `${mainUrl}/`,
logo: {
"@type": "ImageObject",
inLanguage: "en-US",
"@id": `${mainUrl}/#/schema/logo/image/`,
url: `${mainUrl}/assets/images/logo-dark.webp`,
contentUrl: `${mainUrl}/assets/images/logo-dark.webp`,
caption: siteName,
},
image: {
"@id": `${mainUrl}/#/schema/logo/image/`,
},
},
],
};
return ( return (
<> <>
<HeroOther title="Blog" /> <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} />
<HeroOther title={`Category: ${category.data.name}`} />
<section className="page-section" id="blog"> <section className="page-section" id="blog">
<div className="w-full max-w-7xl mx-auto px-4 md:px-8"> <div className="w-full max-w-7xl mx-auto px-4 md:px-8">
<form action={`category/chemotherapy/page/1`} method="GET"> <form action={`/category/${paramsCategory}`} method="GET">
<div className="input-group"> <div className="input-group">
<input <input
type="text" type="text"
@ -52,7 +135,7 @@ export default async function CategoryPage({
</div> </div>
<Suspense fallback={<Loading />}> <Suspense fallback={<Loading />}>
<Blogs preset="categories" page={page} search={paramsSearch} /> <Blogs preset="categories" categoryId={category.data.id} page={page} search={paramsSearch} />
</Suspense> </Suspense>
</section> </section>

View File

@ -3,14 +3,30 @@ import { fetchBlog } from "@/services/payload/blog";
import { BlogCardItem } from "./BlogCardItem"; import { BlogCardItem } from "./BlogCardItem";
import { sanitizeBlogContentIntoStringPreview } from "@/utils/sanitize"; import { sanitizeBlogContentIntoStringPreview } from "@/utils/sanitize";
export interface BlogsProps { export type BlogsProps = {
preset?: "blogs" | "categories";
page: number; page: number;
search?: string; search?: string;
} & (
| {
preset?: "blogs";
}
| {
preset?: "categories";
categoryId: number;
}
);
export default async function Blogs({ page, search, ...params }: BlogsProps) {
const fetchBlogParams: Parameters<typeof fetchBlog>[0] = {
page,
search,
};
if (params.preset === "categories") {
fetchBlogParams.categoryId = params.categoryId;
} }
export default async function Blogs({ page, search, preset = "blogs" }: BlogsProps) { const data = await fetchBlog(fetchBlogParams);
const data = await fetchBlog({ page, search });
if (!data?.totalDocs) return <></>; if (!data?.totalDocs) return <></>;
@ -42,7 +58,7 @@ export default async function Blogs({ page, search, preset = "blogs" }: BlogsPro
hasNextPage={data.hasNextPage} hasNextPage={data.hasNextPage}
hasPreviousPage={data.hasPrevPage} hasPreviousPage={data.hasPrevPage}
totalPages={data.totalPages} totalPages={data.totalPages}
usePathParams={preset === "blogs" ? false : true} usePathParams={params.preset === "blogs" ? false : true}
/> />
</div> </div>
)} )}

View File

@ -28,8 +28,13 @@ export default function Pagination({
const searchParams = new URLSearchParams(url.search); const searchParams = new URLSearchParams(url.search);
if (usePathParams) { if (usePathParams) {
searchParams.set("page", `${page}`); let updatedPath = "";
window.location.href = `${pathName}/page/${page}/?${searchParams}`; if (pathName.includes("/page")) {
updatedPath = pathName.replace(/\/page\/\d+/, `/page/${page}`);
} else {
updatedPath = `${pathName}/page/${page}`;
}
window.location.href = `${updatedPath}?${searchParams}`;
} else { } else {
searchParams.set("page", `${page}`); searchParams.set("page", `${page}`);
window.location.href = `${pathName}/?${searchParams}`; window.location.href = `${pathName}/?${searchParams}`;

View File

@ -5,10 +5,10 @@ import { getPayload, Where } from "payload";
type FetchBlogParams = { type FetchBlogParams = {
page?: number; page?: number;
search?: string; search?: string;
categorySlug?: string; categoryId?: number;
}; };
export async function fetchBlog({ page, search = "", categorySlug = "" }: FetchBlogParams = {}) { export async function fetchBlog({ page, search = "", categoryId }: FetchBlogParams = {}) {
const payload = await getPayload({ config: payloadConfig }); const payload = await getPayload({ config: payloadConfig });
const queryCondition: Where = {}; const queryCondition: Where = {};
@ -17,9 +17,9 @@ export async function fetchBlog({ page, search = "", categorySlug = "" }: FetchB
contains: search, contains: search,
}; };
} }
if (!!categorySlug) { if (!!categoryId) {
queryCondition["categories"] = { queryCondition["categories"] = {
equals: 9, equals: categoryId,
}; };
} }
@ -70,3 +70,19 @@ export async function fetchBlogDetail(slug: string | undefined) {
imgUrl, imgUrl,
}; };
} }
export async function fetchBlogCategoryBySlug(slug: string) {
const payload = await getPayload({ config: payloadConfig });
const category = await payload.find({
collection: "blogCategories",
where: {
slug: { equals: slug },
},
});
if (!category?.docs?.[0]) return null;
return {
data: category.docs[0],
};
}

View File

@ -21,7 +21,6 @@ export async function getDefaultMetadata(): Promise<Metadata> {
}, },
openGraph: { openGraph: {
siteName, siteName,
title: siteName,
description: metaDesc, description: metaDesc,
type: "website", type: "website",
locale: "en_US", locale: "en_US",