PozoAppOptimUpdated/src/AdminPanel/Blog/BlogDetail.jsx

526 lines
27 KiB
React
Raw Normal View History

2025-11-05 17:52:07 +05:30
import React, { useState, useEffect, useRef } from 'react';
import { useParams, useNavigate, Link } from 'react-router-dom';
import { useDispatch } from 'react-redux';
import { message } from 'antd';
import Lenis from "@studio-freight/lenis";
import {
RiArrowLeftLine, RiCalendarLine, RiTimeLine, RiUserLine, RiHomeLine, RiArticleLine,
RiShareLine,
RiCloseLine,
} from 'react-icons/ri';
import { RxHamburgerMenu } from 'react-icons/rx';
import { FaFacebook, FaInstagram, FaWhatsapp, FaCopy, FaLinkedin, FaTelegram } from "react-icons/fa";
import { FaXTwitter } from "react-icons/fa6";
import { getPublishedBlogs, getPublishedBlogsBySlug } from '../../features/Blog/Blog.js';
import Loader from '../../PozoApp/Components/Loader/PozoLoader';
import PozoAppLogo from '../../Images/PozoAppLogo.svg';
import "./BlogDetail.scss"
import BlogExtras from "./BlogExtras.jsx";
import SEO from '../../Components/SEO/SEO.jsx';
const mainDirectory = import.meta.env.ENV_MAIN_BASE_URL
const subDirectory = import.meta.env.ENV_BASE_URL
const BlogDetail = () => {
const { slug } = useParams();
const navigate = useNavigate();
const dispatch = useDispatch();
const [blog, setBlog] = useState(null);
const [loading, setLoading] = useState(true);
const [drawerOpen, setDrawerOpen] = useState(false);
const [shareOpen, setShareOpen] = useState(false);
const shareRef = useRef(null);
console.log(blog, "blogblogblogblogblog")
useEffect(() => {
const handleClickOutside = (event) => {
if (shareRef.current && !shareRef.current.contains(event.target)) {
setShareOpen(false);
}
};
const handleEscKey = (event) => {
if (event.key === 'Escape') {
setShareOpen(false);
}
};
if (shareOpen) {
document.addEventListener('mousedown', handleClickOutside);
document.addEventListener('keydown', handleEscKey);
}
return () => {
document.removeEventListener('mousedown', handleClickOutside);
document.removeEventListener('keydown', handleEscKey);
};
}, [shareOpen]);
useEffect(() => {
fetchBlogDetail();
}, [slug]);
useEffect(() => {
const lenis = new Lenis({
duration: 1,
easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
smoothWheel: true,
smoothTouch: true,
});
function raf(time) {
lenis.raf(time);
requestAnimationFrame(raf);
}
requestAnimationFrame(raf);
return () => lenis.destroy();
}, []);
const fetchBlogDetail = async () => {
try {
const response = await dispatch(getPublishedBlogsBySlug({ slug })).unwrap();
if (response?.data?.statusCode === 1) {
const blogs = response.data.data?.[0];
setBlog(blogs);
} else {
message.error('Failed to load blog posts');
}
setLoading(false);
} catch (error) {
console.error('Error fetching blog:', error);
message.error('Failed to load blog post');
setLoading(false);
}
};
const handleBack = () => {
navigate('/blog');
};
if (loading) {
return <Loader />;
}
if (!blog) {
return (
<div className='BlogDetailMaster'>
<div className='blog-detail-error'>Blog post not found</div>
</div>
);
}
const handleBackHome = () => {
navigate(`${subDirectory}`);
}
const handleBackBlog = () => {
navigate(`${subDirectory}blog`);
}
const handlePrivacyPolicy = () => {
navigate(`${subDirectory}privacy-policy`);
}
const handleNavigateCookie = () => {
navigate(`${subDirectory}cookie-policy`);
}
return (
<>
<SEO
title={blog.BlogTitle}
description={blog.BlogSubtitle || "Read the latest blog on PozoApp"}
image={blog.HeaderImage}
url={`${mainDirectory}/home/blog/${slug}`}
type="article"
publishedTime={blog.CreatedDate}
modifiedTime={blog.UpdatedDate || blog.CreatedDate}
keywords={blog.Keywords?.join(', ') || "PozoApp, Blog, Technology, Articles"}
/>
{drawerOpen && <div className='drawer-overlay' onClick={() => setDrawerOpen(false)}></div>}
<div className={`drawer ${drawerOpen ? 'open' : ''}`}>
<div className='drawer-content'>
{/* <Link to='/' onClick={() => setDrawerOpen(false)}> */}
<div onClick={handleBackHome}>
<RiHomeLine /> Home
</div>
{/* </Link> */}
{/* <Link to='/blog' onClick={() => setDrawerOpen(false)}> */}
<div onClick={handleBackBlog}>
<RiArticleLine /> Blog
</div>
{/* </Link> */}
</div>
</div>
<div className='BlogDetailMaster'>
<header className='blog-detail-header'>
<nav className='navbar'>
<div className='navbar-left'>
<div className='hamburger-menu' onClick={() => setDrawerOpen(!drawerOpen)}>
<RxHamburgerMenu size={20} />
</div>
<div className='Blognavbar-logo' onClick={handleBackHome}>
{/* <Link to='/'> */}
<img src={PozoAppLogo} alt='Logo' className='logo-img' />
{/* </Link> */}
<div className='blogdetailsNavbarText'>
| Blog
</div>
</div>
</div>
</nav>
</header>
<div className="blog-detail-contentMain">
<article className='blog-detail-content'>
<header className='blog-header'>
<h1 className='blog-title'>{blog.BlogTitle}</h1>
<p className='blog-subtitle'>{blog.BlogSubtitle}</p>
<div className='blog-meta'>
<div className='meta-item'>
<RiUserLine />{blog.Author || "Admin"}
</div>
{blog.CreatedDate && (
<div className='meta-item'>
<RiCalendarLine /> {(() => {
if (!blog?.CreatedDate) return '';
const dateStr = blog.CreatedDate.split('T')[0]; // Get date part
const [year, month, day] = dateStr.split('-');
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const monthName = months[parseInt(month) - 1];
return `${parseInt(day)} ${monthName} ${year}`;
})()}
</div>
)}
{/* <div className={`status ${blog.IsPublished === 'Y' ? 'published' : 'draft'}`}>
{blog.IsPublished === 'Y' ? 'Published' : 'Draft'}
</div> */}
</div>
</header>
{blog.HeaderImage && (
<div >
<img className='blog-poster' src={blog.HeaderImage} alt={blog.BlogTitle} />
</div>
)}
<div className='blog-body'>
{blog.BlogContent && blog.BlogContent.map((block, index) => {
switch (block.type) {
case 'header':
case "oldHeader":
const HeaderTag = `h${block.data.level || 2}`;
return (
<HeaderTag
key={index}
className={`blog-header-block level-${block.data.level}`}
dangerouslySetInnerHTML={{ __html: block.data.text }}
/>
);
case 'paragraph':
return (
<p
key={index}
className="blog-paragraph"
dangerouslySetInnerHTML={{ __html: block.data.text }}
/>
);
case 'list':
if (block.data.style === 'checklist') {
return (
<ul key={index} className='blog-list checklist'>
{block.data.items.map((item, i) => (
<li key={i} className='checklist-item'>
<input type="checkbox" checked={item.meta?.checked} readOnly />
<span>{item.content}</span>
</li>
))}
</ul>
);
} else if (block.data.style === 'ordered') {
return (
<ol key={index} className='blog-list ordered'>
{block.data.items.map((item, i) => (
<li key={i}>{item.content}</li>
))}
</ol>
);
} else {
return (
<ul key={index} className='blog-list unordered'>
{block.data.items.map((item, i) => (
<li key={i}>{item.content}</li>
))}
</ul>
);
}
case "image":
return (
<div key={index} className="blog-image">
<img
src={block.data.file?.url}
alt={block.data.caption || "Blog image"}
/>
{block.data.caption && (
<p className="caption">{block.data.caption}</p>
)}
</div>
);
case "separator":
return (
<hr
key={index}
className={`blog-separator ${block.data.style || "line"
}`}
/>
);
case "video":
return (
<div key={index} className="blog-video">
{block.data.file?.url && (
<video
src={block.data.file.url}
controls
className="video-player"
/>
)}
{block.data.caption && (
<p className="caption">{block.data.caption}</p>
)}
</div>
);
case "link":
return (
<div key={index} className="blog-link">
<a href={block.data.url} target={block.data.target || "_self"}>
{block.data.text}
</a>
</div>
);
case "quote":
return (
<blockquote key={index} className="blog-quote">
<p>{block.data.text}</p>
{block.data.caption && (
<cite>{block.data.caption}</cite>
)}
</blockquote>
);
case "code":
return (
<pre key={index} className="blog-code">
<code>{block.data.code}</code>
</pre>
);
case "cta":
return (
<div key={index} className="blog-cta">
<h3>{block.data.heading}</h3>
<p>{block.data.subheading}</p>
<a href={block.data.buttonUrl} className={`cta-button ${block.data.style}`}>
{block.data.buttonText}
</a>
</div>
);
case "email":
return (
<div key={index} className="blog-email">
<h4>Subject: {block.data.subject}</h4>
<div dangerouslySetInnerHTML={{ __html: block.data.bodyHtml }} />
</div>
);
case "bookmark":
return (
<div key={index} className="blog-bookmark">
<a href={block.data.url} target="_blank" rel="noopener noreferrer" style={{ display: 'flex', alignItems: 'center', gap: '16px', textDecoration: 'none', border: '1px solid #e5e7eb', borderRadius: '8px', padding: '16px' }}>
<div className="bookmark-content" style={{ flex: 1 }}>
<h4 style={{ margin: '0 0 8px 0', color: '#1f2937' }}>{block.data.title}</h4>
<p style={{ margin: '0 0 8px 0', color: '#6b7280', fontSize: '14px' }}>{block.data.description}</p>
<span className="bookmark-url" style={{ color: '#3b82f6', fontSize: '12px' }}>{block.data.url}</span>
</div>
{block.data.image && (
<img src={block.data.image} alt={block.data.title} style={{ width: '120px', height: '90px', objectFit: 'cover', borderRadius: '6px', flexShrink: 0 }} />
)}
</a>
</div>
);
case "callout":
return (
<div key={index} className="blog-callout">
<span className="callout-emoji">{block.data.emoji || "💡"}</span>
<p>{block.data.text}</p>
</div>
);
case "index":
const IndexListTag = block.data.style === "numbered" ? "ol" : "ul";
return (
<div key={index} className="blog-index">
<h4>Table of Contents</h4>
<IndexListTag>
{(block.data.items || []).map((item, i) => (
<li key={i}>
<a href={`#${item.anchor}`}>{item.title}</a>
</li>
))}
</IndexListTag>
</div>
);
default:
return null;
}
})}
<div className="shareBlog">
<button
className="share-btn"
onClick={() => setShareOpen(!shareOpen)}
>
<RiShareLine /> Share
</button>
{shareOpen && (
<div className="share-options" ref={shareRef}>
<div className="share-header">
<span>Share to</span>
<button onClick={() => setShareOpen(false)}>
<RiCloseLine size={20} />
</button>
</div>
<div className="share-buttons">
<button
onClick={() => {
const url = `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(
window.location.href
)}`;
window.open(url, "_blank");
}}
className="share-facebook"
>
<FaFacebook />
</button>
<button
onClick={() => {
const url = `https://wa.me/?text=${encodeURIComponent(
blog.BlogTitle + " " + window.location.href
)}`;
window.open(url, "_blank");
}}
className="share-whatsapp"
>
<FaWhatsapp />
</button>
<button
onClick={() => {
const url = `https://twitter.com/intent/tweet?text=${encodeURIComponent(
blog.BlogTitle
)}&url=${encodeURIComponent(window.location.href)}`;
window.open(url, "_blank");
}}
className="share-twitter"
>
<FaXTwitter />
</button>
<button
onClick={() => {
const url = `https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(
window.location.href
)}`;
window.open(url, "_blank");
}}
className="share-linkedin"
>
<FaLinkedin />
</button>
<button
onClick={() => {
const url = `https://t.me/share/url?url=${encodeURIComponent(
window.location.href
)}&text=${encodeURIComponent(blog.BlogTitle)}`;
window.open(url, "_blank");
}}
className="share-telegram"
>
<FaTelegram />
</button>
<button
onClick={() => {
window.open("https://www.instagram.com/", "_blank");
navigator.clipboard.writeText(window.location.href);
message.success(
"Instagram opened! Link copied to paste in your story or bio."
);
}}
className="share-instagram"
>
<FaInstagram />
</button>
<button
onClick={async () => {
try {
await navigator.clipboard.writeText(
window.location.href
);
message.success("Link copied to clipboard!");
} catch (err) {
// Fallback for older browsers
const textArea = document.createElement("textarea");
textArea.value = window.location.href;
document.body.appendChild(textArea);
textArea.select();
document.execCommand("copy");
document.body.removeChild(textArea);
message.success("Link copied to clipboard!");
}
}}
className="share-copy"
>
<FaCopy /> Copy Link
</button>
</div>
</div>
)}
</div>
</div>
</article>
<BlogExtras allowComments={blog?.AllowComments} blogId={blog?.BlogId} />
</div>
</div>
<div className="liveFooterRights">
<div>
© 2025 PozoApp
<div className='linktoPrivayCookie' onClick={handlePrivacyPolicy}>
Privacy Policy
</div>
<div className='linktoPrivayCookie' onClick={handleNavigateCookie}>
Cookie Policy
</div>
</div>
<p>
For queries contact us: Manager, No 51 Step Colony, Dharga,Hosur,
Krishnagiri, Tamilnadu-635126, India
</p>
</div>
</>
);
};
export default BlogDetail;