fix: blog detail render data and metadata
This commit is contained in:
parent
9dfa9d2dd8
commit
6c002f6b58
@ -3,29 +3,87 @@ import { fetchBlogDetail } from "@/services/payload/blog";
|
|||||||
import { getDefaultMetadata } from "@/utils/metadata";
|
import { getDefaultMetadata } from "@/utils/metadata";
|
||||||
import { RichText } from "@payloadcms/richtext-lexical/react";
|
import { RichText } from "@payloadcms/richtext-lexical/react";
|
||||||
import { Metadata } from "next";
|
import { Metadata } from "next";
|
||||||
|
import { headers } from "next/headers";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
|
|
||||||
const metaDesc =
|
const metaDesc =
|
||||||
"Explore the latest insights, news, and resources on the Dynamic Realty blog. Read our articles today.";
|
"Explore the latest insights, news, and resources on the Dynamic Realty blog. Read our articles today.";
|
||||||
|
|
||||||
export async function generateMetadata(): Promise<Metadata> {
|
export async function generateMetadata(props: { params: Promise<{ slug: string }> }): Promise<Metadata> {
|
||||||
const metadata = await getDefaultMetadata();
|
const metadata = await getDefaultMetadata();
|
||||||
metadata.title = `Blog - ${metadata.openGraph?.siteName}`;
|
const params = await props.params;
|
||||||
metadata.description = metaDesc;
|
|
||||||
|
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;
|
return metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function BlogDetail(props: { params: Promise<{ slug: string }> }) {
|
export default async function BlogDetail(props: { params: Promise<{ slug: string }> }) {
|
||||||
const params = await props.params;
|
const params = await props.params;
|
||||||
|
const headersList = await headers();
|
||||||
|
const fullUrl = headersList.get("x-full-url");
|
||||||
const blog = await fetchBlogDetail(params.slug);
|
const blog = await fetchBlogDetail(params.slug);
|
||||||
|
|
||||||
if (!blog) return notFound();
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<HeroImage />
|
<HeroImage title={blog.data.title} />
|
||||||
|
|
||||||
<section className="section section-md bg-colorSection1">
|
<section className="section section-md bg-colorSection1">
|
||||||
<div className="container">
|
<div className="container">
|
||||||
@ -33,7 +91,11 @@ export default async function BlogDetail(props: { params: Promise<{ slug: string
|
|||||||
<div className="col-lg-8">
|
<div className="col-lg-8">
|
||||||
<article className="blog-post-solo">
|
<article className="blog-post-solo">
|
||||||
<div className="blog-post-solo-part">
|
<div className="blog-post-solo-part">
|
||||||
<RichText data={blog.data.content} />
|
<div className="w-full h-56 md:h-80 bg-colorImgPlaceholder/50 relative">
|
||||||
|
<Image className="object-cover" src={blog.img.url} alt={blog.img.alt} fill />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<RichText className="mt-4" data={blog.data.content} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="blog-post-solo-footer">
|
<div className="blog-post-solo-footer">
|
||||||
@ -51,16 +113,13 @@ export default async function BlogDetail(props: { params: Promise<{ slug: string
|
|||||||
<span>Share this post</span>
|
<span>Share this post</span>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a className="icon icon-circle icon-rounded icon-5 fa-facebook" href="#"></a>
|
<a className="icon icon-circle icon-rounded icon-5 fa-facebook" href={shareUrl.facebook}></a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a className="icon icon-circle icon-rounded icon-4 fa-google-plus" href="#"></a>
|
<a className="icon icon-circle icon-rounded icon-6 fa-twitter" href={shareUrl.twitter}></a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a className="icon icon-circle icon-rounded icon-6 fa-twitter" href="#"></a>
|
<a className="icon icon-circle icon-rounded icon-4 fa-linkedin" href={shareUrl.linkedin}></a>
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a className="icon icon-circle icon-rounded icon-6 fa-pinterest-p" href="#"></a>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -19,7 +19,7 @@ export default async function Blog(props: { searchParams?: Promise<{ s?: string
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<HeroImage />
|
<HeroImage title="Blog" />
|
||||||
<section className="section section-md bg-colorSection2">
|
<section className="section section-md bg-colorSection2">
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div>
|
<div>
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
--color-colorHeaderText: var(--color-colorExt20);
|
--color-colorHeaderText: var(--color-colorExt20);
|
||||||
--color-colorHeaderTextHover: var(--color-colorext40);
|
--color-colorHeaderTextHover: var(--color-colorext40);
|
||||||
--color-colorHeroOverlay: var(--color-colorExt50);
|
--color-colorHeroOverlay: var(--color-colorExt50);
|
||||||
|
--color-colorImgPlaceholder: var(--color-colorExt50);
|
||||||
--color-colorFooter: var(--color-colorExt10);
|
--color-colorFooter: var(--color-colorExt10);
|
||||||
--color-colorFooter2: var(--color-colorExt50);
|
--color-colorFooter2: var(--color-colorExt50);
|
||||||
--color-colorFooterText: var(--color-colorExt20);
|
--color-colorFooterText: var(--color-colorExt20);
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
|
||||||
type HeroImageProps = {
|
type HeroImageProps = {
|
||||||
|
title?: string;
|
||||||
imgSrc?: 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 (
|
return (
|
||||||
<section className="breadcrumbs-custom bg-image context-dark">
|
<section className="breadcrumbs-custom bg-image context-dark">
|
||||||
<Image
|
<Image
|
||||||
@ -19,7 +20,7 @@ export default function HeroImage({ imgSrc = "/images/breadcrumbs-bg-05-1922x441
|
|||||||
/>
|
/>
|
||||||
<div className="bg-colorHeroOverlay/50 w-full h-full absolute top-0 left-0" />
|
<div className="bg-colorHeroOverlay/50 w-full h-full absolute top-0 left-0" />
|
||||||
<div className="container relative">
|
<div className="container relative">
|
||||||
<h2 className="breadcrumbs-custom-title">Blog</h2>
|
<h2 className="breadcrumbs-custom-title">{title}</h2>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
@ -20,7 +20,7 @@ export default function CardBlog({ data }: CardBlogProps) {
|
|||||||
<div className="post-default-body">
|
<div className="post-default-body">
|
||||||
<div className="post-default-title">
|
<div className="post-default-title">
|
||||||
<h4>
|
<h4>
|
||||||
<a href="blog-post.html">{data.title}</a>
|
<Link href={linkDetail}>{data.title}</Link>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
<div className="post-default-divider"></div>
|
<div className="post-default-divider"></div>
|
||||||
@ -29,7 +29,7 @@ export default function CardBlog({ data }: CardBlogProps) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="post-default-time">
|
<div className="post-default-time">
|
||||||
<span className="icon mdi mdi-clock"></span>
|
<span className="icon mdi mdi-clock"></span>
|
||||||
<Link href={linkDetail}>{data.posted_at}</Link>
|
<span>{data.posted_at}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
@ -106,13 +106,16 @@ export async function fetchBlogDetail(slug: string | undefined) {
|
|||||||
const data = blogDataQuery?.docs?.[0];
|
const data = blogDataQuery?.docs?.[0];
|
||||||
const createdAt = formatDate(data.createdAt);
|
const createdAt = formatDate(data.createdAt);
|
||||||
const updatedAt = formatDate(data.updatedAt);
|
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 {
|
return {
|
||||||
data,
|
data,
|
||||||
createdAt,
|
createdAt,
|
||||||
updatedAt,
|
updatedAt,
|
||||||
imgUrl,
|
img,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user