sozsoft-platform/ui/src/views/public/BlogDetail.tsx

127 lines
4 KiB
TypeScript
Raw Normal View History

2026-02-24 20:44:16 +00:00
import React, { useState, useEffect } from 'react'
import { Link, useParams } from 'react-router-dom'
import { showDbDateAsIs } from '@/utils/dateUtils'
import { BlogPost } from '@/proxy/blog/blog'
import { blogService } from '@/services/blog.service'
import { useLocalization } from '@/utils/hooks/useLocalization'
import { useStoreState } from '@/store/store'
import { ROUTES_ENUM } from '@/routes/route.constant'
import { Helmet } from 'react-helmet'
import { Loading } from '@/components/shared'
import { APP_NAME } from '@/constants/app.constant'
interface PostData {
image?: string
2026-03-11 12:20:40 +00:00
author?: string
2026-02-24 20:44:16 +00:00
}
const BlogDetail: React.FC = () => {
const { id } = useParams<{ id: string }>()
const { translate } = useLocalization()
const { currentLang } = useStoreState((state) => state.locale)
const [blogPost, setBlogPost] = useState<BlogPost | null>(null)
const [postData, setPostData] = useState<PostData | null>(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
useEffect(() => {
const fetchBlogPost = async () => {
setLoading(true)
setError(null)
try {
if (id) {
const response = await blogService.getPostBySlug(id)
setBlogPost(response)
setPostData({
image: response.coverImage,
2026-03-11 12:20:40 +00:00
author: response.author,
2026-02-24 20:44:16 +00:00
})
} else {
setError('Blog post ID is missing.')
}
} catch (error: any) {
setError(error.message || 'Failed to fetch blog post.')
} finally {
setLoading(false)
}
}
fetchBlogPost()
}, [id])
if (loading) {
return (
2026-05-26 16:09:26 +00:00
<div className="flex items-center justify-center min-h-screen bg-gray-50 dark:bg-gray-950">
2026-02-24 20:44:16 +00:00
<div className="text-center">
<Loading loading={loading} />
</div>
</div>
)
}
if (error) {
return (
2026-05-26 16:09:26 +00:00
<div className="min-h-screen bg-gray-50 dark:bg-gray-950 flex items-center justify-center">
<h1 className="text-2xl font-bold text-gray-900 dark:text-gray-100">Error: {error}</h1>
2026-02-24 20:44:16 +00:00
</div>
)
}
if (!blogPost || !postData) {
return (
2026-05-26 16:09:26 +00:00
<div className="min-h-screen bg-gray-50 dark:bg-gray-950 flex items-center justify-center">
<h1 className="text-2xl font-bold text-gray-900 dark:text-gray-100">{translate('::Public.blog.notFound')}</h1>
2026-02-24 20:44:16 +00:00
</div>
)
}
return (
2026-05-26 16:09:26 +00:00
<div className="min-h-screen bg-gray-50 dark:bg-gray-950">
2026-02-24 20:44:16 +00:00
<Helmet
titleTemplate={`%s | ${APP_NAME}`}
title={translate('::' + 'App.BlogManagement')}
defaultTitle={APP_NAME}
></Helmet>
<div className="relative bg-blue-900 text-white py-12"></div>
<div className="container mx-auto px-4">
<Link
to={ROUTES_ENUM.public.blog}
2026-05-26 16:09:26 +00:00
className="text-blue-600 hover:underline mt-4 mb-4 inline-block dark:text-blue-400"
2026-02-24 20:44:16 +00:00
>
&larr; {translate('::App.BlogManagement')}
</Link>
{postData.image && (
<img
src={postData.image}
alt={translate('::Public.' + blogPost.title)}
className="w-full h-96 object-cover rounded-lg mb-8"
/>
)}
2026-05-26 16:09:26 +00:00
<h1 className="text-4xl font-bold text-gray-900 mb-6 dark:text-gray-100">
2026-02-24 20:44:16 +00:00
{translate('::Public.' + blogPost.title)}
</h1>
2026-05-26 16:09:26 +00:00
<div className="flex items-center text-sm text-gray-500 space-x-4 mb-8 dark:text-gray-400">
2026-02-24 20:44:16 +00:00
<div className="flex items-center">
2026-03-11 12:20:40 +00:00
<span>{postData.author}</span>
2026-02-24 20:44:16 +00:00
</div>
<div className="flex items-center">
{blogPost.publishedAt && showDbDateAsIs(blogPost.publishedAt)}
</div>
</div>
<div
2026-05-26 16:09:26 +00:00
className="prose max-w-none text-gray-800 dark:prose-invert dark:text-gray-200"
2026-02-24 20:44:16 +00:00
dangerouslySetInnerHTML={{
__html:
currentLang == 'tr'
? translate('::' + blogPost.contentTr!)
: translate('::' + blogPost.contentEn!),
}}
/>
</div>
</div>
)
}
export default BlogDetail