From 6c002f6b5802cad4ec4ab048735bf3fef2dd5d01 Mon Sep 17 00:00:00 2001 From: RizqiSyahrendra Date: Mon, 21 Apr 2025 11:31:07 +0700 Subject: [PATCH] fix: blog detail render data and metadata --- src/app/(main)/blog/[slug]/page.tsx | 83 ++++++++++++++++++++++++----- src/app/(main)/blog/page.tsx | 2 +- src/app/(main)/globals.css | 1 + src/components/HeroImage.tsx | 5 +- src/components/blogs/CardBlog.tsx | 4 +- src/services/payload/blog.ts | 7 ++- 6 files changed, 83 insertions(+), 19 deletions(-) diff --git a/src/app/(main)/blog/[slug]/page.tsx b/src/app/(main)/blog/[slug]/page.tsx index e81e588..f4239d9 100644 --- a/src/app/(main)/blog/[slug]/page.tsx +++ b/src/app/(main)/blog/[slug]/page.tsx @@ -3,29 +3,87 @@ import { fetchBlogDetail } from "@/services/payload/blog"; import { getDefaultMetadata } from "@/utils/metadata"; import { RichText } from "@payloadcms/richtext-lexical/react"; import { Metadata } from "next"; +import { headers } from "next/headers"; import Image from "next/image"; import { notFound } from "next/navigation"; const metaDesc = "Explore the latest insights, news, and resources on the Dynamic Realty blog. Read our articles today."; -export async function generateMetadata(): Promise { +export async function generateMetadata(props: { params: Promise<{ slug: string }> }): Promise { const metadata = await getDefaultMetadata(); - metadata.title = `Blog - ${metadata.openGraph?.siteName}`; - metadata.description = metaDesc; + const params = await props.params; + + let title = "Page"; + let description = "Page"; + let publishedAt = ""; + let updatedAt = ""; + let imgUrl = ""; + let createdByName = ""; + let canonicalUrl = ""; + + const blog = await fetchBlogDetail(params.slug); + if (!!blog) { + // check for blog data + title = `${!!blog.data?.meta?.title ? blog.data?.meta?.title : blog.data.title} - ${metadata.openGraph?.siteName}`; + description = `${!!blog.data?.meta?.description ? blog.data?.meta?.description : blog.data.title}`; + imgUrl = blog.img.url; + publishedAt = blog.data.createdAt; + updatedAt = blog.data.updatedAt; + if (!!blog.data?.meta?.canonical_url) { + canonicalUrl = blog.data.meta.canonical_url; + } + if (!!blog?.data?.createdBy && typeof blog.data.createdBy !== "number") { + createdByName = blog.data.createdBy?.name ?? ""; + } + } + + metadata.title = title; + metadata.description = description; + if (!!metadata.openGraph) { + // @ts-ignore + metadata.openGraph.type = "article"; + metadata.openGraph.title = title; + metadata.openGraph.description = description; + metadata.openGraph.images = !!imgUrl ? [imgUrl] : undefined; + } + if (!!metadata.alternates && !!canonicalUrl) { + metadata.alternates.canonical = canonicalUrl; + } + metadata.twitter = { + card: "summary_large_image", + title: title, + description: description, + images: !!imgUrl ? [imgUrl] : undefined, + }; + metadata.other = { + "article:published_time": publishedAt, + "article:modified_time": updatedAt, + "twitter:label1": "Written by", + "twitter:data1": !!createdByName ? createdByName : "Admin", + "twitter:label2": "Est. reading time", + "twitter:data2": "3 minutes", + }; return metadata; } export default async function BlogDetail(props: { params: Promise<{ slug: string }> }) { const params = await props.params; + const headersList = await headers(); + const fullUrl = headersList.get("x-full-url"); const blog = await fetchBlogDetail(params.slug); - if (!blog) return notFound(); + const shareUrl = { + facebook: `https://www.facebook.com/sharer/sharer.php?u=${fullUrl}`, + linkedin: `https://www.linkedin.com/sharing/share-offsite/?url=${fullUrl}`, + twitter: `https://twitter.com/intent/tweet?url=${fullUrl}`, + }; + return ( <> - +
@@ -33,7 +91,11 @@ export default async function BlogDetail(props: { params: Promise<{ slug: string
- +
+ {blog.img.alt} +
+ +
@@ -51,16 +113,13 @@ export default async function BlogDetail(props: { params: Promise<{ slug: string Share this post
  • - +
  • - +
  • - -
  • -
  • - +
  • diff --git a/src/app/(main)/blog/page.tsx b/src/app/(main)/blog/page.tsx index 8abc1b9..d0eeeda 100644 --- a/src/app/(main)/blog/page.tsx +++ b/src/app/(main)/blog/page.tsx @@ -19,7 +19,7 @@ export default async function Blog(props: { searchParams?: Promise<{ s?: string return ( <> - +
    diff --git a/src/app/(main)/globals.css b/src/app/(main)/globals.css index 1f6a236..08bd10e 100644 --- a/src/app/(main)/globals.css +++ b/src/app/(main)/globals.css @@ -24,6 +24,7 @@ --color-colorHeaderText: var(--color-colorExt20); --color-colorHeaderTextHover: var(--color-colorext40); --color-colorHeroOverlay: var(--color-colorExt50); + --color-colorImgPlaceholder: var(--color-colorExt50); --color-colorFooter: var(--color-colorExt10); --color-colorFooter2: var(--color-colorExt50); --color-colorFooterText: var(--color-colorExt20); diff --git a/src/components/HeroImage.tsx b/src/components/HeroImage.tsx index c0627e8..583bad4 100644 --- a/src/components/HeroImage.tsx +++ b/src/components/HeroImage.tsx @@ -1,10 +1,11 @@ import Image from "next/image"; type HeroImageProps = { + title?: string; imgSrc?: string; }; -export default function HeroImage({ imgSrc = "/images/breadcrumbs-bg-05-1922x441.jpg" }: HeroImageProps) { +export default function HeroImage({ title = "", imgSrc = "/images/breadcrumbs-bg-05-1922x441.jpg" }: HeroImageProps) { return (
    -

    Blog

    +

    {title}

    ); diff --git a/src/components/blogs/CardBlog.tsx b/src/components/blogs/CardBlog.tsx index e7730ce..52e7afa 100644 --- a/src/components/blogs/CardBlog.tsx +++ b/src/components/blogs/CardBlog.tsx @@ -20,7 +20,7 @@ export default function CardBlog({ data }: CardBlogProps) {

    - {data.title} + {data.title}

    @@ -29,7 +29,7 @@ export default function CardBlog({ data }: CardBlogProps) {
    - {data.posted_at} + {data.posted_at}
    diff --git a/src/services/payload/blog.ts b/src/services/payload/blog.ts index 77ce6e4..4e57278 100644 --- a/src/services/payload/blog.ts +++ b/src/services/payload/blog.ts @@ -106,13 +106,16 @@ export async function fetchBlogDetail(slug: string | undefined) { const data = blogDataQuery?.docs?.[0]; const createdAt = formatDate(data.createdAt); const updatedAt = formatDate(data.updatedAt); - const imgUrl = typeof data.img !== "number" ? (data?.img?.url ?? "") : ""; + const img = { + url: typeof data.img !== "number" ? (data?.img?.url ?? "") : "", + alt: typeof data.img !== "number" ? (data?.img?.alt ?? "") : "", + }; return { data, createdAt, updatedAt, - imgUrl, + img, }; }