diff --git a/app/blogs/[slug]/loading.tsx b/app/blogs/[slug]/loading.tsx new file mode 100644 index 0000000..09dbf97 --- /dev/null +++ b/app/blogs/[slug]/loading.tsx @@ -0,0 +1,7 @@ +import React from "react"; + +const Loading = () => { + return
Loading
; +}; + +export default Loading; diff --git a/app/blogs/[slug]/page.tsx b/app/blogs/[slug]/page.tsx new file mode 100644 index 0000000..62c3094 --- /dev/null +++ b/app/blogs/[slug]/page.tsx @@ -0,0 +1,70 @@ +import { getOneBlog } from "@/utils/fetch-blogs"; +import React, { Suspense } from "react"; +import Loading from "./loading"; +import { formatDate } from "@/utils/helper"; +import ImageFallback from "@/components/image-fallback"; +import { Metadata } from "next"; + +interface BlogProps { + params: { + slug: string; + }; +} + +export async function generateMetadata({ params: { slug } }: BlogProps): Promise { + const blogData = await getOneBlog(slug); + if (!blogData) { + return { + title: "Bài viết không tồn tại", + description: "Bài viết không tồn tại", + }; + } + return { + title: blogData.title, + description: blogData.description, + openGraph: { + title: blogData.title, + description: blogData.description, + images: [`/api/og?title=${blogData.title}&image=${blogData.imageUrl}`, blogData.imageUrl], + }, + }; +} + +const BlogDetailPage = async ({ params: { slug } }: BlogProps) => { + const blogData = await getOneBlog(slug); + if (!blogData) { + return
Blog not found
; + } + + const content = ( + + ); + + return ( + }> +
+
+

Ngày cập nhật: {formatDate(blogData.createdAt)}

+
+

{blogData.title}

+

Tác giả: {blogData.normalizedName || "Unknown"}

+ + {content} +
+
+
+
+ ); +}; + +export default BlogDetailPage; diff --git a/components/image-fallback.tsx b/components/image-fallback.tsx index 3c27e34..f027ba9 100644 --- a/components/image-fallback.tsx +++ b/components/image-fallback.tsx @@ -1,3 +1,4 @@ +"use client"; import Image from "next/image"; import { useEffect, useState } from "react"; diff --git a/package-lock.json b/package-lock.json index 5c5c410..2ba5895 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "clsx": "^2.1.1", "lucide-react": "^0.411.0", "mini-svg-data-uri": "^1.4.4", + "moment": "^2.30.1", "next": "14.2.5", "next-themes": "^0.3.0", "react": "^18", @@ -5640,6 +5641,15 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", diff --git a/package.json b/package.json index 2c9f6af..970958a 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "clsx": "^2.1.1", "lucide-react": "^0.411.0", "mini-svg-data-uri": "^1.4.4", + "moment": "^2.30.1", "next": "14.2.5", "next-themes": "^0.3.0", "react": "^18", diff --git a/utils/fetch-blogs.ts b/utils/fetch-blogs.ts index ce54461..d27d315 100644 --- a/utils/fetch-blogs.ts +++ b/utils/fetch-blogs.ts @@ -7,3 +7,13 @@ export async function getListBlog() { const res = await fetch(`${baseUrl}/Blog/getList?type=${type}`); return res.json() as Promise; } + +export async function getOneBlog(slug: string) { + const res = await fetch(`${baseUrl}/Blog/getOneSlug/${slug}?type=${type}`); + return res.json() as Promise; +} + +export async function getImageBlog(slug: string) { + const data = await getOneBlog(slug); + return data.imageUrl || ""; +} diff --git a/utils/helper.ts b/utils/helper.ts new file mode 100644 index 0000000..1ab0f4b --- /dev/null +++ b/utils/helper.ts @@ -0,0 +1,6 @@ +import moment from "moment"; + +export function formatDate(date: string) { + const convertDate = new Date(date); + return moment(convertDate).format("DD-MM-YYYY"); +}