diff --git a/src/app/(main)/blog/[slug]/page.tsx b/src/app/(main)/blog/[slug]/page.tsx index 204a1f0..8287882 100644 --- a/src/app/(main)/blog/[slug]/page.tsx +++ b/src/app/(main)/blog/[slug]/page.tsx @@ -1,3 +1,4 @@ +import ListOfRecentBlog from "@/components/blogs/ListOfRecentBlog"; import HeroImage from "@/components/HeroImage"; import { fetchBlogDetail } from "@/services/payload/blog"; import { getDefaultMetadata } from "@/utils/metadata"; @@ -110,65 +111,31 @@ export default async function BlogDetail(props: { params: Promise<{ slug: string Share this post
  • - +
  • - +
  • - +
  • -
    -
    -
    Recent Posts
    -
    -
    -
    -
    - -
    -
    - -
    -
    -
    + + diff --git a/src/components/blogs/CardBlog.tsx b/src/components/blogs/CardBlog.tsx index 52e7afa..0d41600 100644 --- a/src/components/blogs/CardBlog.tsx +++ b/src/components/blogs/CardBlog.tsx @@ -3,12 +3,47 @@ import Image from "next/image"; import Link from "next/link"; type CardBlogProps = { + colorPreset?: 1 | 2; + isDescriptionVisible?: boolean; data: BlogData; }; -export default function CardBlog({ data }: CardBlogProps) { +export default function CardBlog({ data, colorPreset = 1, isDescriptionVisible = true }: CardBlogProps) { const linkDetail = `/blog/${data.slug}`; + if (colorPreset === 2) { + return ( +
    +
    +
    + + {data.img?.alt + +
    +
    +
    +

    + {data.title} +

    +
    + {isDescriptionVisible && !!data?.description && ( + <> +
    +
    +

    {data.description}

    +
    + + )} +
    + + {data.posted_at} +
    +
    +
    +
    + ); + } + return (
    @@ -23,10 +58,14 @@ export default function CardBlog({ data }: CardBlogProps) { {data.title}
    -
    -
    -

    {data.description}

    -
    + {isDescriptionVisible && !!data?.description && ( + <> +
    +
    +

    {data.description}

    +
    + + )}
    {data.posted_at} diff --git a/src/components/blogs/ListOfBlog.tsx b/src/components/blogs/ListOfBlog.tsx index 12a2da8..010f516 100644 --- a/src/components/blogs/ListOfBlog.tsx +++ b/src/components/blogs/ListOfBlog.tsx @@ -36,7 +36,7 @@ export default function ListOfBlog({ searchKeyword }: ListOfBlogProps) {
    {blogQuery.isFetching && } - {blogQuery.hasNext && ( + {!blogQuery.isFetching && blogQuery.hasNext && ( diff --git a/src/components/blogs/ListOfRecentBlog.tsx b/src/components/blogs/ListOfRecentBlog.tsx new file mode 100644 index 0000000..591c1e5 --- /dev/null +++ b/src/components/blogs/ListOfRecentBlog.tsx @@ -0,0 +1,48 @@ +"use client"; + +import Loader from "@/components/loaders/Loader"; +import { useRecentBlogQuery } from "@/services/hooks/blog"; +import { useEffect } from "react"; +import CardBlog from "./CardBlog"; + +type ListOfRecentBlogProps = { + currentBlogId?: number; +}; + +export default function ListOfRecentBlog({ currentBlogId }: ListOfRecentBlogProps) { + const recentBlogQuery = useRecentBlogQuery(); + + useEffect(() => { + if (!!currentBlogId) { + recentBlogQuery._fetch({ + currentBlogId, + }); + } + }, [currentBlogId]); + + if (recentBlogQuery.isFetching) { + return ( +
    + +
    + ); + } + + if (recentBlogQuery.data.length <= 0) return <>; + + return ( + <> +
    +
    +
    Recent Posts
    +
    +
    +
    + {recentBlogQuery.data.map((blog) => ( + + ))} +
    +
    + + ); +} diff --git a/src/payload.config.ts b/src/payload.config.ts index c53d1fc..83618ec 100644 --- a/src/payload.config.ts +++ b/src/payload.config.ts @@ -18,8 +18,6 @@ const filename = fileURLToPath(import.meta.url); const dirname = path.dirname(filename); export default buildConfig({ - cors: [process.env.SITE_URL || ""], - csrf: [process.env.SITE_URL || ""], admin: { user: Users.slug, importMap: { diff --git a/src/schema/blog.ts b/src/schema/blog.ts index 6349fc2..ea26548 100644 --- a/src/schema/blog.ts +++ b/src/schema/blog.ts @@ -1,7 +1,7 @@ export type BlogData = { slug?: string | null; title: string; - description: string; + description?: string; img?: { url: string; alt?: string }; posted_at: string; }; diff --git a/src/schema/services/blog.ts b/src/schema/services/blog.ts index ab91539..16e2b5d 100644 --- a/src/schema/services/blog.ts +++ b/src/schema/services/blog.ts @@ -4,3 +4,7 @@ export type FetchBlogParams = { categoryId?: number; tagId?: number; }; + +export type FetchRecentBlogParams = { + currentBlogId: number; +}; diff --git a/src/services/hooks/blog.ts b/src/services/hooks/blog.ts index ba9f31e..dc8a757 100644 --- a/src/services/hooks/blog.ts +++ b/src/services/hooks/blog.ts @@ -1,7 +1,7 @@ import { BlogData } from "@/schema/blog"; -import { FetchBlogParams } from "@/schema/services/blog"; +import { FetchBlogParams, FetchRecentBlogParams } from "@/schema/services/blog"; import { useState } from "react"; -import { fetchBlogREST } from "../rest/blog"; +import { fetchBlogREST, fetchRecentBlogREST } from "../rest/blog"; export function useBlogQuery() { const [data, setData] = useState([]); @@ -14,7 +14,13 @@ export function useBlogQuery() { setFetching(false); if (Array.isArray(res?.formattedData)) { - setData(res.formattedData); + if (!!params.page && params.page > 1) { + setData((currentData) => { + return [...currentData, ...res.formattedData]; + }); + } else { + setData(res.formattedData); + } } setHasNext(res?.hasNextPage ?? false); } @@ -26,3 +32,24 @@ export function useBlogQuery() { hasNext, }; } + +export function useRecentBlogQuery() { + const [data, setData] = useState([]); + const [isFetching, setFetching] = useState(false); + + async function _fetch(params: FetchRecentBlogParams) { + setFetching(true); + const res = await fetchRecentBlogREST(params); + setFetching(false); + + if (Array.isArray(res?.formattedData)) { + setData(res.formattedData); + } + } + + return { + _fetch, + data, + isFetching, + }; +} diff --git a/src/services/payload/blog.ts b/src/services/payload/blog.ts index 4e57278..de75adb 100644 --- a/src/services/payload/blog.ts +++ b/src/services/payload/blog.ts @@ -37,11 +37,13 @@ export async function fetchBlog({ page, search = "", categoryId, tagId }: FetchB where: queryCondition, }); - const formattedData = blogDataQuery.docs.map((item) => { + const formattedData: BlogData[] = blogDataQuery.docs.map((item) => { return { - ...item, - imgFormatted: typeof item.img !== "number" ? { url: item?.img?.url ?? "", alt: item.img.alt } : undefined, - createdAtFormatted: formatDate(item.createdAt), + slug: item.slug, + title: item.title, + description: sanitizeBlogContentIntoStringPreview(item.content), + img: typeof item.img !== "number" ? { url: item?.img?.url ?? "", alt: item.img.alt } : undefined, + posted_at: formatDate(item.createdAt), }; }); diff --git a/src/services/rest/blog.ts b/src/services/rest/blog.ts index 1ea4527..19d5b76 100644 --- a/src/services/rest/blog.ts +++ b/src/services/rest/blog.ts @@ -1,6 +1,6 @@ import { Blog } from "@/payload-types"; import { BlogData } from "@/schema/blog"; -import { FetchBlogParams } from "@/schema/services/blog"; +import { FetchBlogParams, FetchRecentBlogParams } from "@/schema/services/blog"; import { formatDate } from "@/utils/datetime"; import { sanitizeBlogContentIntoStringPreview } from "@/utils/sanitize"; import { PaginatedDocs, Where } from "payload"; @@ -59,3 +59,43 @@ export async function fetchBlogREST({ page, search = "", categoryId, tagId }: Fe return null; } } + +export async function fetchRecentBlogREST({ currentBlogId }: FetchRecentBlogParams) { + const queryCondition: Where = { + _status: { equals: "published" }, + id: { + not_equals: currentBlogId, + }, + }; + + const queryParams = stringify( + { + pagination: true, + limit: 2, + where: queryCondition, + }, + { addQueryPrefix: true } + ); + + const blogRequest = await fetch(`/api/blogs${queryParams}`); + + if (blogRequest.ok) { + const resData = (await blogRequest.json()) as PaginatedDocs; + const formattedData: BlogData[] = resData.docs.map((item) => { + return { + slug: item.slug, + title: item.title, + description: sanitizeBlogContentIntoStringPreview(item.content), + img: typeof item.img !== "number" ? { url: item?.img?.url ?? "", alt: item.img.alt } : undefined, + posted_at: formatDate(item.createdAt), + }; + }); + + return { + ...resData, + formattedData, + }; + } else { + return null; + } +}