feat: enhance news detail page with dynamic data loading and social sharing functionality

This commit is contained in:
fredy.siswanto 2025-03-08 00:14:30 +07:00
parent ee209b6ceb
commit 50fdd6bc02
4 changed files with 55 additions and 14 deletions

View File

@ -11,6 +11,8 @@ type TParameters = {
slug: string slug: string
} & THttpServer } & THttpServer
export type TNewDetailResponse = z.infer<typeof dataResponseSchema>
export const getNewsBySlug = async (parameters: TParameters) => { export const getNewsBySlug = async (parameters: TParameters) => {
const { slug, accessToken } = parameters const { slug, accessToken } = parameters
try { try {

View File

@ -10,6 +10,7 @@ import { XIcon } from '~/components/icons/x'
type SocialMediaProperties = { type SocialMediaProperties = {
className?: string className?: string
slug?: string
} }
const dataSocialMedia = [ const dataSocialMedia = [

View File

@ -1,20 +1,28 @@
import htmlParse from 'html-react-parser' import htmlParse from 'html-react-parser'
import { useRouteLoaderData } from 'react-router'
import { Breadcrumb } from '~/components/ui/breadcrumb' import type { TTagResponse } from '~/apis/common/get-tags'
import { Card } from '~/components/ui/card' import { Card } from '~/components/ui/card'
import { CarouselSection } from '~/components/ui/carousel-section' import { CarouselSection } from '~/components/ui/carousel-section'
import { IconsSocial } from '~/components/ui/social-share' import { IconsSocial } from '~/components/ui/social-share'
import { BERITA } from '~/data/contents' import { BERITA } from '~/data/contents'
import type { loader } from '~/routes/_news.detail.$slug'
import { CONTENT } from './data' import { formatDate } from '~/utils/formatter'
export const NewsDetailPage = () => { export const NewsDetailPage = () => {
const { title, content, featured, slug, author, date, tags } = CONTENT const loaderData = useRouteLoaderData<typeof loader>(
'routes/_news.detail.$slug',
)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const { newsDetailData }: any = loaderData
const { title, content, featured_image, slug, author, live_at, tags } =
newsDetailData
return ( return (
<div className="sm-max:mx-5 relative"> <div className="sm-max:mx-5 relative">
<Card> <Card>
<div className="py-5 sm:px-30"> <div className="py-5 sm:px-30">
<Breadcrumb slug={slug} />
<h2 className="text-xl font-extrabold text-[#2E2F7C] sm:text-4xl"> <h2 className="text-xl font-extrabold text-[#2E2F7C] sm:text-4xl">
{title} {title}
</h2> </h2>
@ -28,10 +36,8 @@ export const NewsDetailPage = () => {
className="h-12 w-12 rounded-full" className="h-12 w-12 rounded-full"
/> />
<div> <div>
<h4 className="text-md">{author}</h4> <h4 className="text-md">{author.name}</h4>
<p className="text-sm"> <p className="text-sm">{formatDate(live_at)} . 5 min read </p>
{date.toJSON().slice(0, 10)} . 5 min read{' '}
</p>
</div> </div>
</div> </div>
<IconsSocial className="flex-row" /> <IconsSocial className="flex-row" />
@ -39,7 +45,7 @@ export const NewsDetailPage = () => {
{/* end next planing create component for this section */} {/* end next planing create component for this section */}
<div className="w-full bg-amber-200"> <div className="w-full bg-amber-200">
<img <img
src={featured} src={featured_image}
alt={title} alt={title}
className="w-full object-cover object-center sm:h-[600px]" className="w-full object-cover object-center sm:h-[600px]"
/> />
@ -53,15 +59,18 @@ export const NewsDetailPage = () => {
<div className="items-end justify-between border-b-gray-300 py-4 sm:flex"> <div className="items-end justify-between border-b-gray-300 py-4 sm:flex">
<div className="flex flex-col max-sm:mb-3"> <div className="flex flex-col max-sm:mb-3">
<p className="mb-2">Share this post</p> <p className="mb-2">Share this post</p>
<IconsSocial className="a" /> <IconsSocial
className="a"
slug={slug}
/>
</div> </div>
<div className="flex flex-wrap items-end gap-2"> <div className="flex flex-wrap items-end gap-2">
{tags?.map((tag) => ( {tags?.map((tag: TTagResponse) => (
<span <span
key={tag} key={tag.id}
className="rounded bg-gray-300 p-1" className="rounded bg-gray-300 p-1"
> >
{tag} {tag.name}
</span> </span>
))} ))}
</div> </div>

View File

@ -1,5 +1,34 @@
import { getNewsBySlug } from '~/apis/common/get-news-by-slug'
import { getUser } from '~/apis/news/get-user'
import { handleCookie } from '~/libs/cookies'
import { NewsDetailPage } from '~/pages/news-detail' import { NewsDetailPage } from '~/pages/news-detail'
import type { Route } from './+types/_news.detail.$slug'
export const loader = async ({ request }: Route.LoaderArgs) => {
const { userToken } = await handleCookie(request)
let userData
if (userToken) {
const { data } = await getUser({
accessToken: userToken,
})
userData = data
}
// TODO need handel if user not accses non premium data
const { data: newsDetailData } = await getNewsBySlug({
slug: request.url.split('/').pop() ?? '',
accessToken: userToken,
})
// const { data: categoriesData } = await getCategories()
return {
newsDetailData,
userData,
// categoriesData,
}
}
const NewsDetailLayout = () => <NewsDetailPage /> const NewsDetailLayout = () => <NewsDetailPage />
export default NewsDetailLayout export default NewsDetailLayout