detail blog

This commit is contained in:
hamid zarghami
2025-10-04 15:49:30 +03:30
parent 49c2dc7f2a
commit bd0dd4302a
4 changed files with 121 additions and 57 deletions
+100 -53
View File
@@ -1,16 +1,80 @@
'use client'
import Image from 'next/image'
import { Calendar, Eye, User } from 'iconsax-react'
import { Calendar, Eye, User, Share } from 'iconsax-react'
import LastPost from '../components/LastPost'
import { NextPage } from 'next'
import Layout from '@/hoc/Layout'
import { SHOP_CONFIG } from '@/config/const'
import { useGetBlogDetails, useGetLastBlogs } from '../hooks/useBlogData'
import { useParams } from 'next/navigation'
import { useEffect, useState } from 'react'
const BlogDetail: NextPage = () => {
const { id } = useParams()
const { data, isLoading } = useGetBlogDetails(id as string)
const { data: lastBlogsData } = useGetLastBlogs(3)
const [currentUrl, setCurrentUrl] = useState('')
useEffect(() => {
if (typeof window !== 'undefined') {
setCurrentUrl(window.location.href)
}
}, [])
const shareOnTelegram = () => {
const blog = data?.results?.blog
if (blog && currentUrl) {
const text = `مشاهده مقاله: ${blog.title_fa}`
const url = `https://t.me/share/url?url=${encodeURIComponent(currentUrl)}&text=${encodeURIComponent(text)}`
window.open(url, '_blank')
}
}
const shareOnWhatsApp = () => {
const blog = data?.results?.blog
if (blog && currentUrl) {
const text = `${blog.title_fa}\n\n${currentUrl}`
const url = `https://wa.me/?text=${encodeURIComponent(text)}`
window.open(url, '_blank')
}
}
const handleNativeShare = async () => {
const blog = data?.results?.blog
if (blog && currentUrl && typeof navigator !== 'undefined' && 'share' in navigator) {
try {
const shareData = {
title: blog.title_fa,
text: `مشاهده مقاله: ${blog.title_fa}`,
url: currentUrl,
}
await (navigator as { share: (data: typeof shareData) => Promise<void> }).share(shareData)
} catch (error) {
console.log('Error sharing:', error)
}
}
}
if (isLoading) {
return <div className="flex justify-center items-center min-h-screen">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary"></div>
</div>
}
if (!data?.results?.blog) {
return <div className="flex justify-center items-center min-h-screen">
<p className="text-gray-500">بلاگ یافت نشد</p>
</div>
}
const blog = data.results.blog
return (
<div className="max-w-7xl mx-auto px-4 py-8">
{/* Breadcrumb */}
<div className="text-sm text-gray-500 mb-6">
فروشگاه {SHOP_CONFIG.name} / راهنما ها / تست اقلام و قطعات / گلکسی بوک
فروشگاه {SHOP_CONFIG.name} / {blog.category.title_fa} / {blog.title_fa}
</div>
<div className="flex gap-8">
@@ -19,25 +83,25 @@ const BlogDetail: NextPage = () => {
{/* Blog Header */}
<div className="mb-8">
<div className="bg-orange-100 text-orange-600 px-3 py-1 rounded text-sm inline-block mb-4">
اخبار
{blog.category.title_fa}
</div>
<h1 className="text-2xl font-bold text-gray-900 mb-4 leading-relaxed">
مشخصات گلکسی بوک 3 پرو و اولترا پیش از رونمایی فاش شد
{blog.title_fa}
</h1>
<div className="flex items-center gap-6 text-gray-500 text-sm">
<div className="flex items-center gap-2">
<User size={16} color="#9CA3AF" />
<span>نویسنده: حمید ضرغامی</span>
<span>نویسنده: {blog.author.fullName}</span>
</div>
<div className="flex items-center gap-2">
<Calendar size={16} color="#9CA3AF" />
<span>چهارشنبه 15 آذر 1401 ساعت 15:30</span>
<span>{new Date(blog.createdAt).toLocaleDateString('fa-IR')}</span>
</div>
<div className="flex items-center gap-2">
<Eye size={16} color="#9CA3AF" />
<span>1,234 بازدید</span>
<span>{blog.view} بازدید</span>
</div>
</div>
</div>
@@ -45,8 +109,8 @@ const BlogDetail: NextPage = () => {
{/* Featured Image */}
<div className="mb-8">
<Image
src="https://images.unsplash.com/photo-1496181133206-80ce9b88a853?w=1200&h=600&fit=crop"
alt="Samsung Galaxy Book3 Ultra"
src={blog.imageUrl}
alt={blog.title_fa}
width={1200}
height={600}
className="w-full h-[400px] object-cover rounded-lg"
@@ -54,48 +118,20 @@ const BlogDetail: NextPage = () => {
</div>
{/* Blog Content */}
<div className="prose prose-lg max-w-none text-gray-800 leading-8">
<p className="text-lg leading-8 mb-6">
گلکسی بوک 3 اولترا قویترین عضو این خانواده خواهد بود که با پردازنده نسل سیزدهم اینتل Core i9-13900H، حداکثر 32 گیگابایت رم LPDDR5 و 1 ترابایت حافظه داخلی NVMe PCIe Gen 4 از آن میرسد. این دستگاه همچنین میتواند به حداکثر گرافیک RTX 4070 اویدیا مجهز شود.
</p>
<h2 className="text-xl font-bold mt-8 mb-4">گلکسی بوک 3 پرو</h2>
<p className="mb-6 leading-8">
گلکسی بوک 3 پرو در دو اندازه 14 و 16 اینچی عرضه میشود که هر دو از نوع اموled با روزنمایی 3K خواهند بود. این لپتاپ قرار است از پردازنده نسل سیزدهم اینتل Core i7-1360P و Core i5-1340P برخوردار شود؛ بههمراه حداکثر 1 ترابایت حافظه DDR5 رم و 16 گیگابایت استفاده کند 16 گیگابایت رم.
</p>
<div className="bg-gray-50 p-6 rounded-lg mb-6">
<h3 className="font-bold text-lg mb-4">مشخصات فنی کلیدی:</h3>
<ul className="space-y-2">
<li> پردازنده: Intel Core i9-13900H (نسل سیزدهم)</li>
<li> رم: تا 32 گیگابایت LPDDR5</li>
<li> حافظه: 1 ترابایت NVMe PCIe Gen 4</li>
<li> کارت گرافیک: RTX 4070</li>
<li> صفحه نمایش: OLED با کیفیت 3K</li>
<li> سیستم عامل: Windows 11</li>
</ul>
</div>
<p className="mb-6 leading-8">
این لپتاپ برای کاربران حرفهای طراحی شده که نیاز به قدرت پردازشی بالا برای کارهای گرافیکی، ویرایش ویدئو و بازی دارند. طراحی زیبا و ساخت با کیفیت سامسونگ، آن را به گزینهای جذاب برای کسانی تبدیل میکند که به دنبال ترکیب عملکرد و زیبایی هستند.
</p>
<h2 className="text-xl font-bold mt-8 mb-4">نتیجه گیری</h2>
<p className="mb-6 leading-8">
سامسونگ با ارائه گلکسی بوک 3 سری، قصد دارد جایگاه خود را در بازار لپتاپهای پریمیوم تقویت کند. این محصولات با ترکیب سختافزار قدرتمند و طراحی شیک، میتوانند رقیبی جدی برای سایر برندهای معتبر باشند.
</p>
</div>
<div
className="prose prose-lg max-w-none text-gray-800 leading-8"
dangerouslySetInnerHTML={{ __html: blog.content }}
/>
{/* Tags */}
<div className="mt-8 pt-6 border-t border-gray-200">
<div className="flex items-center gap-2 flex-wrap">
<span className="text-gray-600 font-medium">برچسبها:</span>
<span className="bg-primary/10 text-primary px-3 py-1 rounded-full text-sm">سامسونگ</span>
<span className="bg-primary/10 text-primary px-3 py-1 rounded-full text-sm">گلکسی بوک</span>
<span className="bg-primary/10 text-primary px-3 py-1 rounded-full text-sm">لپتاپ</span>
<span className="bg-primary/10 text-primary px-3 py-1 rounded-full text-sm">OLED</span>
{blog.tags.map((tag, index) => (
<span key={index} className="bg-primary/10 text-primary px-3 py-1 rounded-full text-sm">
{tag}
</span>
))}
</div>
</div>
@@ -104,24 +140,35 @@ const BlogDetail: NextPage = () => {
<div className="flex items-center justify-between">
<div className="flex items-center gap-4">
<span className="text-gray-600 font-medium">اشتراکگذاری:</span>
<button className="flex items-center gap-2 bg-primary text-white px-4 py-2 rounded-lg hover:bg-primary/90 transition-colors">
<button
onClick={shareOnTelegram}
className="flex items-center gap-2 bg-primary text-white px-4 py-2 rounded-lg hover:bg-primary/90 transition-colors"
>
<span>تلگرام</span>
</button>
<button className="flex items-center gap-2 bg-green-600 text-white px-4 py-2 rounded-lg hover:bg-green-700 transition-colors">
<button
onClick={shareOnWhatsApp}
className="flex items-center gap-2 bg-green-600 text-white px-4 py-2 rounded-lg hover:bg-green-700 transition-colors"
>
<span>واتساپ</span>
</button>
</div>
<button className="flex items-center gap-2 text-gray-600 hover:text-red-500 transition-colors">
<span></span>
<span>پسندیدن</span>
{typeof navigator !== 'undefined' && 'share' in navigator && (
<button
onClick={handleNativeShare}
className="flex items-center gap-2 bg-gray-600 text-white px-4 py-2 rounded-lg hover:bg-gray-700 transition-colors"
>
<Share size={16} color='#ffffff' />
<span>اشتراکگذاری</span>
</button>
)}
</div>
</div>
</div>
</div>
{/* Sidebar */}
<div className="w-80 space-y-8">
<LastPost blogs={[]} />
<LastPost blogs={lastBlogsData?.results?.blogs || []} />
</div>
</div>
</div>
+9 -2
View File
@@ -1,6 +1,6 @@
import * as api from "../service/Service";
import { useQuery } from "@tanstack/react-query";
import { BlogResponse, BlogCategory } from "../types/Types";
import { BlogResponse, BlogDetailResponse, BlogCategory } from "../types/Types";
export const useGetBlogs = (page = 1, limit = 20) => {
return useQuery<BlogResponse>({
@@ -17,7 +17,7 @@ export const useGetBlogCategories = () => {
};
export const useGetBlogDetails = (id: string) => {
return useQuery<BlogResponse>({
return useQuery<BlogDetailResponse>({
queryKey: ["blog-details", id],
queryFn: () => api.getBlogDetails(id),
enabled: !!id,
@@ -35,3 +35,10 @@ export const useGetBlogsByCategory = (
enabled: !!slug,
});
};
export const useGetLastBlogs = (limit: number = 3) => {
return useQuery<BlogResponse>({
queryKey: ["last-blogs", limit],
queryFn: () => api.getBlogs(1, limit),
});
};
+4 -2
View File
@@ -1,5 +1,5 @@
import axios from "@/config/axios";
import { BlogResponse, BlogCategory } from "../types/Types";
import { BlogResponse, BlogDetailResponse, BlogCategory } from "../types/Types";
export const getBlogs = async (
page: number,
@@ -19,7 +19,9 @@ export const getBlogCategories = async (): Promise<BlogCategory[]> => {
}
};
export const getBlogDetails = async (id: string): Promise<BlogResponse> => {
export const getBlogDetails = async (
id: string
): Promise<BlogDetailResponse> => {
const { data } = await axios.get(`/blogs/${id}`);
return data;
};
+8
View File
@@ -63,3 +63,11 @@ export interface BlogResponse {
success: boolean;
results: BlogResults;
}
export interface BlogDetailResponse {
status: number;
success: boolean;
results: {
blog: Blog;
};
}