feat: make blog data dynamic
This commit is contained in:
16
app/api/blogs/route.js
Normal file
16
app/api/blogs/route.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import axios from "axios";
|
||||
import { backendUrl } from "@/utils/axios";
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
const response = await axios.get(`${backendUrl}/blogs/`);
|
||||
return NextResponse.json(response.data);
|
||||
} catch (error) {
|
||||
console.error("Error fetching blogs:", error);
|
||||
return NextResponse.json(
|
||||
{ error: "Failed to fetch blog data" },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
19
app/api/blogs[slug]/route.js
Normal file
19
app/api/blogs[slug]/route.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import axios from "axios";
|
||||
import { backendUrl } from "@/utils/axios";
|
||||
|
||||
export async function GET(request, { params }) {
|
||||
const { slug } = params;
|
||||
|
||||
try {
|
||||
console.log(`API route fetching blog with slug: ${slug}`);
|
||||
const response = await axios.get(`${backendUrl}/blogs/${slug}/`);
|
||||
return NextResponse.json(response.data);
|
||||
} catch (error) {
|
||||
console.error(`Error fetching blog with slug ${slug}:`, error);
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to fetch blog: ${error.message}` },
|
||||
{ status: error.response?.status || 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
14
app/blogs/blog[slug]/page.jsx
Normal file
14
app/blogs/blog[slug]/page.jsx
Normal file
@@ -0,0 +1,14 @@
|
||||
"use client";
|
||||
import React from "react";
|
||||
import SingleBlog from "@/components/blog/SingleBlog";
|
||||
import { useParams } from "next/navigation";
|
||||
|
||||
export default function BlogPage() {
|
||||
const params = useParams();
|
||||
const slug = params?.["blog-id"];
|
||||
|
||||
console.log("BlogPage params:", params);
|
||||
console.log("BlogPage slug:", slug);
|
||||
|
||||
return <SingleBlog slug={slug} />;
|
||||
}
|
||||
@@ -2,11 +2,11 @@ export const DASHBOARD = {
|
||||
"images": [
|
||||
{
|
||||
"id": 1,
|
||||
"path": "page_images/1.jpg",
|
||||
"path": "/page_images/1.jpg",
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"path": "page_images/2.jpg",
|
||||
"path": "/page_images/2.jpg",
|
||||
},
|
||||
],
|
||||
"heading1": "Explore Gupta Rudraksha",
|
||||
@@ -19,35 +19,35 @@ export const DASHBOARD = {
|
||||
"title": "Since 1973",
|
||||
"image": {
|
||||
"id": 1,
|
||||
"path": "page_images/1.jpg",
|
||||
"path": "/page_images/1.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
"title": "Vedic Energization",
|
||||
"image": {
|
||||
"id": 1,
|
||||
"path": "page_images/1.jpg",
|
||||
"path": "/page_images/1.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
"title": "Lab Certification",
|
||||
"image": {
|
||||
"id": 1,
|
||||
"path": "page_images/1.jpg",
|
||||
"path": "/page_images/1.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
"title": "ISO 9001:2015 certified",
|
||||
"image": {
|
||||
"id": 1,
|
||||
"path": "page_images/1.jpg",
|
||||
"path": "/page_images/1.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
"title": "Secure Payment",
|
||||
"image": {
|
||||
"id": 1,
|
||||
"path": "page_images/1.jpg",
|
||||
"path": "/page_images/1.jpg",
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -58,21 +58,21 @@ export const DASHBOARD = {
|
||||
{
|
||||
"image": {
|
||||
"id": 1,
|
||||
"path": "page_images/1.jpg",
|
||||
"path": "/page_images/1.jpg",
|
||||
},
|
||||
"title": "Spirituality",
|
||||
},
|
||||
{
|
||||
"image": {
|
||||
"id": 1,
|
||||
"path": "page_images/1.jpg",
|
||||
"path": "/page_images/1.jpg",
|
||||
},
|
||||
"title": "Meditation",
|
||||
},
|
||||
{
|
||||
"image": {
|
||||
"id": 1,
|
||||
"path": "page_images/1.jpg",
|
||||
"path": "/page_images/1.jpg",
|
||||
},
|
||||
"title": "Wellness",
|
||||
},
|
||||
@@ -84,11 +84,11 @@ export const DASHBOARD = {
|
||||
"heading8": "Gupta Rudraksha - The Only Vendor in the World To 100% Lifetime Money Back Authenticity Guarantee.",
|
||||
"footer_image1": {
|
||||
"id": 1,
|
||||
"path": "page_images/1.jpg",
|
||||
"path": "/page_images/1.jpg",
|
||||
},
|
||||
"footer_image2": {
|
||||
"id": 1,
|
||||
"path": "page_images/1.jpg",
|
||||
"path": "/page_images/1.jpg",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ export const CONSULTATION = {
|
||||
"description": "Enhance personal growth, gain clarity, and overcome obstacles.",
|
||||
"image": {
|
||||
"id": 1,
|
||||
"path": "page_images/1.jpg",
|
||||
"path": "/page_images/1.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -120,7 +120,7 @@ export const CONSULTATION = {
|
||||
"description": "Scale your business, improve operations, and navigate challenges.",
|
||||
"image": {
|
||||
"id": 1,
|
||||
"path": "page_images/1.jpg",
|
||||
"path": "/page_images/1.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -128,7 +128,7 @@ export const CONSULTATION = {
|
||||
"description": "Plan your career, enhance job search strategies, and build professional skills.",
|
||||
"image": {
|
||||
"id": 1,
|
||||
"path": "page_images/1.jpg",
|
||||
"path": "/page_images/1.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -136,7 +136,7 @@ export const CONSULTATION = {
|
||||
"description": "Navigate significant life changes and find new directions for personal fulfillment.",
|
||||
"image": {
|
||||
"id": 1,
|
||||
"path": "page_images/1.jpg",
|
||||
"path": "/page_images/1.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -144,7 +144,7 @@ export const CONSULTATION = {
|
||||
"description": "Optimize health, wellness, and live a balanced lifestyle.",
|
||||
"image": {
|
||||
"id": 1,
|
||||
"path": "page_images/1.jpg",
|
||||
"path": "/page_images/1.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -152,7 +152,7 @@ export const CONSULTATION = {
|
||||
"description": "Resolve conflicts, improve communication, and build healthier relationships.",
|
||||
"image": {
|
||||
"id": 1,
|
||||
"path": "page_images/1.jpg",
|
||||
"path": "/page_images/1.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -160,7 +160,7 @@ export const CONSULTATION = {
|
||||
"description": "Optimize finances, plan for retirement, and grow wealth.",
|
||||
"image": {
|
||||
"id": 1,
|
||||
"path": "page_images/1.jpg",
|
||||
"path": "/page_images/1.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -168,7 +168,7 @@ export const CONSULTATION = {
|
||||
"description": "Overcome feeling stuck, gain perspective, and find purpose.",
|
||||
"image": {
|
||||
"id": 1,
|
||||
"path": "page_images/1.jpg",
|
||||
"path": "/page_images/1.jpg",
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -179,11 +179,11 @@ export const CONSULTATION = {
|
||||
"description": "Consultation provides access to expert advice and guidance from professionals who have in-depth knowledge and experience in their respective fields. We can offer valuable insights, strategies, and solutions tailored to your specific needs.",
|
||||
"image1": {
|
||||
"id": 1,
|
||||
"path": "page_images/1.jpg",
|
||||
"path": "/page_images/1.jpg",
|
||||
},
|
||||
"image2": {
|
||||
"id": 1,
|
||||
"path": "page_images/1.jpg",
|
||||
"path": "/page_images/1.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -191,11 +191,11 @@ export const CONSULTATION = {
|
||||
"description": "Rudraksha experts can recommend specific mantras that align with your spiritual goals and intentions. Mantras are considered powerful tools for spiritual growth, and the right mantra can enhance the effectiveness of your Rudraksha.",
|
||||
"image1": {
|
||||
"id": 1,
|
||||
"path": "page_images/1.jpg",
|
||||
"path": "/page_images/1.jpg",
|
||||
},
|
||||
"image2": {
|
||||
"id": 1,
|
||||
"path": "page_images/1.jpg",
|
||||
"path": "/page_images/1.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -203,11 +203,11 @@ export const CONSULTATION = {
|
||||
"description": "Rudraksha experts may also have knowledge of Vedic astrology. By analyzing your birth chart, they can provide insights into the planetary influences on your life and suggest Rudraksha combinations that may help balance and harmonize these influences.",
|
||||
"image1": {
|
||||
"id": 1,
|
||||
"path": "page_images/1.jpg",
|
||||
"path": "/page_images/1.jpg",
|
||||
},
|
||||
"image2": {
|
||||
"id": 1,
|
||||
"path": "page_images/1.jpg",
|
||||
"path": "/page_images/1.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -215,11 +215,11 @@ export const CONSULTATION = {
|
||||
"description": "Understanding the birth charts of family members can offer a holistic view of the energy dynamics within the family. Rudraksha experts can provide guidance on selecting Rudraksha beads that complement the energy of the entire family, fostering a harmonious environment.",
|
||||
"image1": {
|
||||
"id": 1,
|
||||
"path": "page_images/1.jpg",
|
||||
"path": "/page_images/1.jpg",
|
||||
},
|
||||
"image2": {
|
||||
"id": 1,
|
||||
"path": "page_images/1.jpg",
|
||||
"path": "/page_images/1.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -227,11 +227,11 @@ export const CONSULTATION = {
|
||||
"description": "Understanding the birth charts of family members can offer a holistic view of the energy dynamics within the family. Rudraksha experts can provide guidance on selecting Rudraksha beads that complement the energy of the entire family, fostering a harmonious environment.",
|
||||
"image1": {
|
||||
"id": 1,
|
||||
"path": "page_images/1.jpg",
|
||||
"path": "/page_images/1.jpg",
|
||||
},
|
||||
"image2": {
|
||||
"id": 1,
|
||||
"path": "page_images/1.jpg",
|
||||
"path": "/page_images/1.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -239,11 +239,11 @@ export const CONSULTATION = {
|
||||
"description": "Understanding the birth charts of family members can offer a holistic view of the energy dynamics within the family. Rudraksha experts can provide guidance on selecting Rudraksha beads that complement the energy of the entire family, fostering a harmonious environment.",
|
||||
"image1": {
|
||||
"id": 1,
|
||||
"path": "page_images/1.jpg",
|
||||
"path": "/page_images/1.jpg",
|
||||
},
|
||||
"image2": {
|
||||
"id": 1,
|
||||
"path": "page_images/1.jpg",
|
||||
"path": "/page_images/1.jpg",
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,39 +1,55 @@
|
||||
"use client";
|
||||
import React, { useState } from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Card, CardHeader, CardContent } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import axios from "axios";
|
||||
import { backendUrl } from "@/utils/axios";
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
const BlogPost = ({ title, author, date, excerpt, imageUrl }) => (
|
||||
<div className="mb-6 flex sm:flex-row flex-col">
|
||||
<div className=" h-full bg-purple-100 sm:w-1/2">
|
||||
<Link href={`/blogs/blog/${title}`}>
|
||||
<Image
|
||||
src={imageUrl}
|
||||
alt={title || "Image"}
|
||||
className="object-cover rounded-md"
|
||||
layout="responsive"
|
||||
width={500}
|
||||
height={300}
|
||||
const BlogPost = ({ title, author, date, excerpt, imageUrl, slug }) => {
|
||||
const sanitizedExcerpt = DOMPurify.sanitize(excerpt);
|
||||
|
||||
const formattedImageUrl = imageUrl !== "NA"
|
||||
? (imageUrl.startsWith('http') ? imageUrl : `${backendUrl}${imageUrl}`)
|
||||
: "/api/placeholder/300/200";
|
||||
|
||||
return (
|
||||
<div className="mb-6 flex sm:flex-row flex-col">
|
||||
<div className=" h-full bg-purple-100 sm:w-1/2">
|
||||
<Link href={`/blogs/blog/${slug}`}>
|
||||
<Image
|
||||
src={formattedImageUrl}
|
||||
alt={title || "Image"}
|
||||
className="object-cover rounded-md"
|
||||
layout="responsive"
|
||||
width={500}
|
||||
height={300}
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="sm:w-2/3 p-4">
|
||||
<Link href={`/blogs/blog/${slug}`}>
|
||||
<h3 className="text-xl sm:text-2xl font-bold">{title}</h3>
|
||||
</Link>
|
||||
<p className="text-sm sm:text-lg text-gray-500 mb-2">
|
||||
{author} | {date}
|
||||
</p>
|
||||
<div
|
||||
className="text-sm blog-content"
|
||||
dangerouslySetInnerHTML={{ __html: sanitizedExcerpt }}
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="sm:w-2/3 p-4">
|
||||
<Link href={`/blogs/blog/${title}`}>
|
||||
<h3 className="text-xl sm:text-2xl font-bold">{title}</h3>
|
||||
</Link>
|
||||
<p className="text-sm sm:text-lg text-gray-500 mb-2">
|
||||
{author} | {date}
|
||||
</p>
|
||||
<p className="text-sm">{excerpt}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
const PopularArticle = ({ title, author, date }) => (
|
||||
const PopularArticle = ({ title, author, date, slug }) => (
|
||||
<div className="mb-4">
|
||||
<h4 className="font-semibold">{title}</h4>
|
||||
<Link href={`/blogs/blog/${slug}`}>
|
||||
<h4 className="font-semibold">{title}</h4>
|
||||
</Link>
|
||||
<p className="text-sm text-gray-500">
|
||||
{author} | {date}
|
||||
</p>
|
||||
@@ -42,63 +58,77 @@ const PopularArticle = ({ title, author, date }) => (
|
||||
|
||||
const BlogHome = () => {
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [blogData, setBlogData] = useState({
|
||||
recentBlogPosts: [],
|
||||
popularArticles: []
|
||||
});
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
const postsPerPage = 3;
|
||||
|
||||
const recentBlogPosts = [
|
||||
{
|
||||
title: "Benefits of Wearing Rudraksha Mala",
|
||||
author: "Gupta Rudraksha",
|
||||
date: "29 August, 2024",
|
||||
excerpt:
|
||||
"Rudraksha are sacred beads of great significance in Hinduism and various spiritual practices. The term Rudraksha combines two Sanskrit words: 'Rudra,' another name for Lord Sh...",
|
||||
imageUrl: "/blogs/significance-of-dhanteras.webp",
|
||||
},
|
||||
{
|
||||
title: "Shravan Maas for Spiritual Growth and Divine Connection",
|
||||
author: "Gupta Rudraksha",
|
||||
date: "04 September, 2024",
|
||||
excerpt:
|
||||
"Shrawan Mass, a sacred month in the Hindu calendar, holds deep spiritual significance for millions of devotees. But what exactly is Shravan Maas, and why is it so important? Let...",
|
||||
imageUrl: "/blogs/navaratri-siginificance.webp",
|
||||
},
|
||||
{
|
||||
title: "The Complete Guide to Rudraksha Energization",
|
||||
author: "Gupta Rudraksha",
|
||||
date: "28 August, 2024",
|
||||
excerpt:
|
||||
"For centuries, Rudraksha beads have been valued not only for their aesthetic beauty, but also for their powerful spiritual and healing properties. Whether you're a seasoned prac...",
|
||||
imageUrl: "/blogs/rudraksha-pran-pratishtha-pooja.webp",
|
||||
},
|
||||
{
|
||||
title: "Strengthening Planetary Forces with Rudraksha",
|
||||
author: "Gupta Rudraksha",
|
||||
date: "26 April, 2024",
|
||||
excerpt:
|
||||
"In the vast universe, planets hold immense power over our lives, influencing everything from our moods to our destinies. However, Rudraksha beads, ancient treasures from the ear...",
|
||||
imageUrl: "/api/placeholder/300/200",
|
||||
},
|
||||
];
|
||||
useEffect(() => {
|
||||
const fetchBlogData = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const response = await axios.get(`${backendUrl}/blogs/`);
|
||||
setBlogData(response.data);
|
||||
setLoading(false);
|
||||
} catch (err) {
|
||||
console.error("Error fetching blog data:", err);
|
||||
setError("Failed to load blog data. Please try again later.");
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const popularArticles = [
|
||||
{
|
||||
title: "Dhanteras Significance and How Rudraksha Brings Prosperity",
|
||||
author: "Gupta Rudraksha",
|
||||
date: "30 September, 2024",
|
||||
},
|
||||
{
|
||||
title:
|
||||
"Certified Rudraksha: Nepal's 1st ISO 9001:2015 Certified Organization",
|
||||
author: "Gupta Admin",
|
||||
date: "30 September, 2024",
|
||||
},
|
||||
];
|
||||
fetchBlogData();
|
||||
}, []);
|
||||
|
||||
// Get featured blog (first blog from recent posts)
|
||||
const featuredBlog = blogData.recentBlogPosts && blogData.recentBlogPosts.length > 0
|
||||
? blogData.recentBlogPosts[0]
|
||||
: null;
|
||||
|
||||
// Get remaining blogs for pagination
|
||||
const remainingBlogs = blogData.recentBlogPosts && blogData.recentBlogPosts.length > 0
|
||||
? blogData.recentBlogPosts.slice(1)
|
||||
: [];
|
||||
|
||||
const indexOfLastPost = currentPage * postsPerPage;
|
||||
const indexOfFirstPost = indexOfLastPost - postsPerPage;
|
||||
const currentPosts = recentBlogPosts.slice(indexOfFirstPost, indexOfLastPost);
|
||||
const currentPosts = remainingBlogs.slice(indexOfFirstPost, indexOfLastPost);
|
||||
|
||||
const paginate = (pageNumber) => setCurrentPage(pageNumber);
|
||||
|
||||
const featuredImageUrl = featuredBlog && featuredBlog.imageUrl !== "NA"
|
||||
? (featuredBlog.imageUrl.startsWith('http') ? featuredBlog.imageUrl : `${backendUrl}${featuredBlog.imageUrl}`)
|
||||
: "/api/placeholder/300/200";
|
||||
|
||||
if (loading) {
|
||||
return <div className="container mx-auto px-4 py-8">Loading blogs...</div>;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <div className="container mx-auto px-4 py-8 text-red-500">{error}</div>;
|
||||
}
|
||||
|
||||
// Check if there are no blogs at all
|
||||
if (!blogData.recentBlogPosts || blogData.recentBlogPosts.length === 0) {
|
||||
return (
|
||||
<div className="container max-w-8xl mx-auto sm:px-20 px-3 py-20 text-center">
|
||||
<h1 className="sm:text-5xl pt-3 text-3xl font-bold mb-8">
|
||||
Insights from Gupta Rudraksha
|
||||
</h1>
|
||||
<div className="bg-gray-100 p-10 rounded-lg shadow-md">
|
||||
<h2 className="text-2xl font-semibold text-gray-700 mb-4">No Blogs Found</h2>
|
||||
<p className="text-gray-600">
|
||||
We're currently working on creating insightful content for you.
|
||||
Please check back later for updates on Rudraksha and spiritual practices.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="container max-w-8xl mx-auto sm:px-20 px-3 ">
|
||||
<h1 className="sm:text-5xl pt-3 text-3xl font-bold mb-8">
|
||||
@@ -108,73 +138,88 @@ const BlogHome = () => {
|
||||
Explore our latest articles on the spiritual, cultural, and healing
|
||||
aspects
|
||||
</p>
|
||||
{/* top container for one blog card */}
|
||||
<dir className="min-h-[30vh] flex sm:flex-row flex-col-reverse">
|
||||
<div className="mb-6 flex sm:flex-row flex-col">
|
||||
<div className="h-full bg-purple-100">
|
||||
<Link href={`/blogs/blog/`}>
|
||||
<Image
|
||||
src="/blogs/significance-of-dhanteras.webp"
|
||||
alt="images blog"
|
||||
layout="intrinsic"
|
||||
width={600}
|
||||
height={400}
|
||||
className="object-cover rounded-md"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="sm:w-2/3 p-4">
|
||||
<Link href={`/blogs/blog/`}>
|
||||
<h3 className="text-xl sm:text-2xl font-bold">
|
||||
Dhanteras Significance and How Rudraksha Brings Prosperity and
|
||||
Protection
|
||||
</h3>
|
||||
</Link>
|
||||
<p className="text-sm sm:text-lg text-gray-500 mb-2">
|
||||
Gupta Rudraksha | 30 September, 2024
|
||||
</p>
|
||||
<p className="text-sm py-4 line-clamp-2">
|
||||
Dhanteras, the first day of Diwali festival, marks a unique
|
||||
celebration of wealth and prosperity in Hindu tradition. As the
|
||||
name suggests - 'Dhan' meaning wealth and
|
||||
'Teras' ref
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full lg:w-1/3 px-4">
|
||||
<h2 className="text-2xl font-bold mb-4">Popular Articles</h2>
|
||||
{popularArticles.map((article, index) => (
|
||||
<PopularArticle key={index} {...article} />
|
||||
))}
|
||||
</div>
|
||||
</dir>
|
||||
|
||||
<div className="flex flex-wrap -mx-4">
|
||||
<div className="w-full lg:w-2/3 px-4">
|
||||
<h2 className="sm:text-4xl text-2xl font-bold mb-4">Recent Blogs</h2>
|
||||
{currentPosts.map((post, index) => (
|
||||
<BlogPost key={index} {...post} />
|
||||
))}
|
||||
<div className="flex justify-center space-x-2 mb-8 items-center pt-6">
|
||||
{[...Array(Math.ceil(recentBlogPosts.length / postsPerPage))].map(
|
||||
(_, index) => (
|
||||
<Button
|
||||
key={index}
|
||||
onClick={() => paginate(index + 1)}
|
||||
variant={currentPage === index + 1 ? "default" : "outline"}
|
||||
>
|
||||
{index + 1}
|
||||
</Button>
|
||||
)
|
||||
{/* Featured blog section */}
|
||||
{featuredBlog && (
|
||||
<div className="min-h-[30vh] flex sm:flex-row flex-col-reverse">
|
||||
<div className="mb-6 flex sm:flex-row flex-col">
|
||||
<div className="h-full bg-purple-100">
|
||||
<Link href={`/blogs/blog/${featuredBlog.slug}`}>
|
||||
<Image
|
||||
src={featuredImageUrl}
|
||||
alt={featuredBlog.title}
|
||||
layout="intrinsic"
|
||||
width={600}
|
||||
height={400}
|
||||
className="object-cover rounded-md"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="sm:w-2/3 p-4">
|
||||
<Link href={`/blogs/blog/${featuredBlog.slug}`}>
|
||||
<h3 className="text-xl sm:text-2xl font-bold">
|
||||
{featuredBlog.title}
|
||||
</h3>
|
||||
</Link>
|
||||
<p className="text-sm sm:text-lg text-gray-500 mb-2">
|
||||
{featuredBlog.author} | {featuredBlog.date}
|
||||
</p>
|
||||
<div
|
||||
className="text-sm py-4 line-clamp-2 blog-content"
|
||||
dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(featuredBlog.excerpt) }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full lg:w-1/3 px-4">
|
||||
<h2 className="text-2xl font-bold mb-4">Popular Articles</h2>
|
||||
{blogData.popularArticles && blogData.popularArticles.length > 0 ? (
|
||||
blogData.popularArticles.map((article, index) => (
|
||||
<PopularArticle key={index} {...article} />
|
||||
))
|
||||
) : (
|
||||
<p className="text-gray-500">No popular articles available</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-wrap -mx-4">
|
||||
<div className="w-full lg:w-2/3 px-4">
|
||||
<h2 className="sm:text-4xl text-2xl font-bold mb-4">Recent Blogs</h2>
|
||||
{currentPosts.length > 0 ? (
|
||||
currentPosts.map((post, index) => (
|
||||
<BlogPost key={post.id || index} {...post} />
|
||||
))
|
||||
) : (
|
||||
<p className="text-gray-500 py-4">No recent blogs available</p>
|
||||
)}
|
||||
|
||||
{remainingBlogs.length > postsPerPage && (
|
||||
<div className="flex justify-center space-x-2 mb-8 items-center pt-6">
|
||||
{[...Array(Math.ceil(remainingBlogs.length / postsPerPage))].map(
|
||||
(_, index) => (
|
||||
<Button
|
||||
key={index}
|
||||
onClick={() => paginate(index + 1)}
|
||||
variant={currentPage === index + 1 ? "default" : "outline"}
|
||||
>
|
||||
{index + 1}
|
||||
</Button>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="w-full lg:w-1/3 px-4">
|
||||
<h2 className="text-2xl font-bold mb-4">Popular Articles</h2>
|
||||
{popularArticles.map((article, index) => (
|
||||
<PopularArticle key={index} {...article} />
|
||||
))}
|
||||
{blogData.popularArticles && blogData.popularArticles.length > 0 ? (
|
||||
blogData.popularArticles.map((article, index) => (
|
||||
<PopularArticle key={index} {...article} />
|
||||
))
|
||||
) : (
|
||||
<p className="text-gray-500">No popular articles available</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,43 +2,120 @@
|
||||
import Image from "next/image";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import "./blog.css";
|
||||
import axios from "axios";
|
||||
import { backendUrl } from "@/utils/axios";
|
||||
import DOMPurify from 'dompurify';
|
||||
import { useParams } from "next/navigation";
|
||||
|
||||
|
||||
|
||||
const SingleBlog = () => {
|
||||
const [blogResData, setResData] = useState(null);
|
||||
const [blog, setBlog] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
// Debug log to check if slug is received
|
||||
const params = useParams();
|
||||
|
||||
console.log("BlogPage params:", params);
|
||||
console.log("SingleBlog received slug:", params?.["blog-id"]);
|
||||
const slug = params?.["blog-id"]
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
const fetchBlogData = async () => {
|
||||
if (!slug) {
|
||||
console.error("No slug provided");
|
||||
setError("Blog not found. Missing identifier.");
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/blog");
|
||||
const blog = await response.json();
|
||||
setResData(blog.blogdata);
|
||||
} catch (error) {
|
||||
console.error("Error fetching blog data:", error);
|
||||
console.log(`Fetching blog with slug: ${slug}`);
|
||||
setLoading(true);
|
||||
|
||||
// Log the full URL being called for debugging
|
||||
const url = `${backendUrl}/blogs/${slug}/`;
|
||||
console.log(`Making API call to: ${url}`);
|
||||
|
||||
const response = await axios.get(url);
|
||||
console.log("API response:", response.data);
|
||||
|
||||
if (response.data) {
|
||||
setBlog(response.data);
|
||||
} else {
|
||||
setError("No blog data received from server");
|
||||
}
|
||||
setLoading(false);
|
||||
} catch (err) {
|
||||
console.error("Error fetching blog data:", err);
|
||||
setError(`Failed to load blog content: ${err.message || "Unknown error"}`);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
fetchBlogData();
|
||||
}, [slug]);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto p-5 text-center">
|
||||
<p className="text-gray-500">Loading blog content for slug: {slug}...</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto p-5 text-center">
|
||||
<p className="text-red-500">Error: {error}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!blog) {
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto p-5 text-center">
|
||||
<p className="text-gray-500">Blog not found for slug: {slug}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Format the image URL
|
||||
const imageUrl = blog.imageUrl !== "NA"
|
||||
? (blog.imageUrl.startsWith('http') ? blog.imageUrl : `${backendUrl}${blog.imageUrl}`)
|
||||
: "/api/placeholder/800/500";
|
||||
|
||||
// Sanitize the blog description
|
||||
const sanitizedDescription = DOMPurify.sanitize(blog.description);
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto p-5 space-y-6 bg-white shadow-lg rounded-lg">
|
||||
{/* Blog Title */}
|
||||
<h1 className="text-3xl font-bold text-center mb-6">{blog.title}</h1>
|
||||
|
||||
{/* Blog Meta Information */}
|
||||
<div className="text-center text-gray-600 mb-6">
|
||||
<p>{blog.author} | {blog.date}</p>
|
||||
</div>
|
||||
|
||||
{/* Blog Image */}
|
||||
<div className="w-full flex justify-center">
|
||||
<Image
|
||||
src={"/blogs/significance-of-dhanteras.webp"}
|
||||
src={imageUrl}
|
||||
height={500}
|
||||
width={800}
|
||||
alt="Blog Cover Image"
|
||||
alt={blog.title || "Blog Cover Image"}
|
||||
className="rounded-lg object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Blog Content */}
|
||||
<div className="prose lg:prose-xl mx-auto text-gray-800 leading-relaxed">
|
||||
{blogResData ? (
|
||||
<div dangerouslySetInnerHTML={{ __html: blogResData }} />
|
||||
) : (
|
||||
<p className="text-center text-gray-500">Loading blog content...</p>
|
||||
)}
|
||||
<div
|
||||
className="blog-content"
|
||||
dangerouslySetInnerHTML={{ __html: sanitizedDescription }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
17
package-lock.json
generated
17
package-lock.json
generated
@@ -19,6 +19,7 @@
|
||||
"axios": "^1.7.9",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
"dompurify": "^3.2.5",
|
||||
"embla-carousel-autoplay": "^8.3.0",
|
||||
"embla-carousel-react": "^8.3.0",
|
||||
"framer-motion": "^12.0.6",
|
||||
@@ -1807,6 +1808,13 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/trusted-types": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
||||
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.6.0.tgz",
|
||||
@@ -2833,6 +2841,15 @@
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dompurify": {
|
||||
"version": "3.2.5",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.5.tgz",
|
||||
"integrity": "sha512-mLPd29uoRe9HpvwP2TxClGQBzGXeEC/we/q+bFlmPPmj2p2Ugl3r6ATu/UU1v77DXNcehiBg9zsr1dREyA/dJQ==",
|
||||
"license": "(MPL-2.0 OR Apache-2.0)",
|
||||
"optionalDependencies": {
|
||||
"@types/trusted-types": "^2.0.7"
|
||||
}
|
||||
},
|
||||
"node_modules/eastasianwidth": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"axios": "^1.7.9",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
"dompurify": "^3.2.5",
|
||||
"embla-carousel-autoplay": "^8.3.0",
|
||||
"embla-carousel-react": "^8.3.0",
|
||||
"framer-motion": "^12.0.6",
|
||||
|
||||
Reference in New Issue
Block a user