canina/frontend/application/app/blog/[slug]/page.tsx
parsa aghaei 8d1b786a24
All checks were successful
Deploy Canina / deploy (push) Successful in 1m50s
chore(brand): rename all Persian occurrences of کانینا to کنینا across the project
2026-08-16 16:40:08 +03:30

144 lines
6.5 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React from "react";
import { ChevronRight, Calendar, User, Sparkles } from "lucide-react";
import Link from 'next/link';
import Image from 'next/image';
import type { Metadata } from 'next';
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'https://apicanina.parsaaghayi.ir';
async function getBlog(slug: string) {
try {
const res = await fetch(`${API_URL}/api/blogs/${slug}`, { next: { revalidate: 60 } });
if (!res.ok) throw new Error('Failed to fetch blog');
const b = await res.json();
return {
id: b.id,
slug: b.slug,
title: b.title,
excerpt: b.metaDescription || b.content.substring(0, 150).replace(/<[^>]+>/g, '') + '...',
image: b.imageUrl,
date: new Date(b.createdAt).toLocaleDateString('fa-IR'),
author: b.author ? `${b.author.firstName} ${b.author.lastName}` : 'نویسنده کنینا',
content: b.content
};
} catch (error) {
console.error(error);
return null;
}
}
export const revalidate = 60; // ensure SSR dynamic revalidation works correctly
export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise<Metadata> {
const { slug } = await params;
const blog = await getBlog(slug);
if (!blog) return { title: 'مقاله یافت نشد | کنینا ایران' };
const title = `${blog.title} | مجله سلامت کنینا`;
const description = blog.excerpt.substring(0, 150).trim();
return {
title,
description,
alternates: {
canonical: `/blog/${slug}`,
},
openGraph: {
title,
description,
images: [blog.image],
url: `/blog/${slug}`,
siteName: "کنینا ایران",
type: "article",
}
};
}
export default async function BlogPostPage({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params;
const selectedPost = await getBlog(slug);
if (!selectedPost) {
return (
<div className="min-h-[50vh] flex flex-col items-center justify-center font-vazir text-center" dir="rtl">
<h1 className="text-3xl font-black text-medical-gray-900 mb-4">مقالهای یافت نشد</h1>
<Link href="/blog" className="text-canina-blue font-bold flex items-center gap-2">
<ChevronRight className="w-4 h-4" />
بازگشت به مجله سلامت
</Link>
</div>
);
}
return (
<div className="min-h-screen bg-medical-gray-50 pt-8 pb-20 px-4 font-vazir" dir="rtl">
<div className="max-w-7xl mx-auto">
<div className="flex items-center gap-2 text-[10px] font-black text-medical-gray-400 uppercase tracking-widest mb-10">
<Link href="/" className="hover:text-canina-blue transition-colors">خانه</Link>
<ChevronRight className="w-2.5 h-2.5" />
<Link href="/blog" className="hover:text-canina-blue transition-colors">مجله سلامت</Link>
<ChevronRight className="w-2.5 h-2.5" />
<span className="text-canina-blue">{selectedPost.title}</span>
</div>
<div className="grid lg:grid-cols-12 gap-12">
<div className="lg:col-span-8">
<div className="bg-white rounded-[3.5rem] border border-medical-gray-200 shadow-2xl overflow-hidden">
<div className="aspect-video w-full overflow-hidden relative">
<Image src={selectedPost.image} alt={selectedPost.title} fill className="w-full h-full object-cover" unoptimized />
</div>
<div className="p-12">
<div className="flex items-center gap-4 text-xs font-bold text-canina-blue mb-8">
<div className="bg-canina-blue/10 px-4 py-1.5 rounded-full uppercase tracking-widest">تحقیق بالینی</div>
<div className="flex items-center gap-1">
<Calendar className="w-4 h-4" />
{selectedPost.date}
</div>
</div>
<h1 className="text-4xl lg:text-5xl font-black text-medical-gray-900 mb-10 leading-tight">
{selectedPost.title}
</h1>
<div className="prose prose-lg max-w-none text-medical-gray-600 leading-[2] text-justify space-y-6">
<p className="font-bold text-medical-gray-900 italic text-xl border-r-4 border-canina-blue pr-6 py-2">
{selectedPost.excerpt}
</p>
<div className="whitespace-pre-line" dangerouslySetInnerHTML={{ __html: selectedPost.content }} />
</div>
<div className="mt-16 pt-10 border-t border-medical-gray-100 flex items-center justify-between">
<div className="flex items-center gap-4">
<div className="w-14 h-14 bg-medical-gray-100 rounded-full flex items-center justify-center text-medical-gray-400 border-2 border-white shadow-lg">
<User className="w-8 h-8" />
</div>
<div>
<div className="font-black text-medical-gray-900 text-lg">{selectedPost.author}</div>
<div className="font-bold text-medical-gray-400">نویسنده کنینا</div>
</div>
</div>
<div className="flex gap-4">
<button className="p-4 bg-medical-gray-100 rounded-2xl hover:bg-canina-blue hover:text-white transition-all text-medical-gray-400">
<Sparkles className="w-6 h-6" />
</button>
</div>
</div>
</div>
</div>
</div>
<div className="lg:col-span-4 space-y-10">
<div className="bg-canina-blue rounded-[3rem] p-10 text-white shadow-2xl relative overflow-hidden">
<div className="absolute top-0 left-0 p-8 opacity-10">
<Sparkles className="w-20 h-20" />
</div>
<h4 className="text-2xl font-black mb-6 italic relative z-10">ارتباط با کارشناسان</h4>
<div className="space-y-6 relative z-10 text-white/90">
برای دریافت مشاوره تخصصی و انتخاب بهترین مکمل برای حیوان خانگی خود، با ما در تماس باشید.
</div>
</div>
</div>
</div>
</div>
</div>
);
}