review section + question section + prosCons section
This commit is contained in:
@@ -8,10 +8,14 @@ import {
|
||||
BreadcrumbPage,
|
||||
BreadcrumbSeparator,
|
||||
} from '@/components/ui/breadcrumb'
|
||||
import ProductImage from '../components/ProductImage'
|
||||
import BaseInformation from '../components/BaseInformation'
|
||||
import ShopInformation from '../components/ShopInformation'
|
||||
import Specifications from '../components/Specifications'
|
||||
import ProductImage from '@/app/product/components/ProductImage'
|
||||
import BaseInformation from '@/app/product/components/BaseInformation'
|
||||
import ShopInformation from '@/app/product/components/ShopInformation'
|
||||
import Specifications from '@/app/product/components/Specifications'
|
||||
import ProsCons from '@/app/product/components/ProsCons'
|
||||
import Description from '@/app/product/components/Description'
|
||||
import Reviews from '@/app/product/components/Reviews'
|
||||
import Questions from '@/app/product/components/Questions'
|
||||
|
||||
const SingleProduct: NextPage = () => {
|
||||
return (
|
||||
@@ -36,15 +40,21 @@ const SingleProduct: NextPage = () => {
|
||||
<div className='text-primary text-sm'>فروشنده شوید</div>
|
||||
</div>
|
||||
|
||||
<div className='flex gap-6 mt-14'>
|
||||
<ProductImage />
|
||||
<BaseInformation />
|
||||
<div className='flex gap-6 mt-14 flex-row-reverse'>
|
||||
<ShopInformation />
|
||||
</div>
|
||||
|
||||
<div className='flex gap-6'>
|
||||
<Specifications />
|
||||
<div className='min-w-[325px] px-7'></div>
|
||||
<div className='flex-1 flex flex-col gap-6'>
|
||||
<div className='flex gap-6'>
|
||||
<ProductImage />
|
||||
<BaseInformation />
|
||||
</div>
|
||||
<div>
|
||||
<Specifications />
|
||||
<Description />
|
||||
<ProsCons />
|
||||
<Reviews />
|
||||
<Questions />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { FC } from 'react'
|
||||
|
||||
const DESCRIPTION: string[] = [
|
||||
'همواره گوشیهای هوشمند پرچمدار سامسونگ توانستهاند با بهره بردن از مشخصات فنی قدرتمند، توجه هر بینندهای را به خود جلب کنند. سامسونگ Galaxy S24 Ultra نیز از این قاعده مستثنا نیست و با نمایشگری باکیفیت، پردازنده پرتوان و سیستم دوربین قدرتمند به میدان آمده است.',
|
||||
'صفحهنمایش 6.8 اینچی این گوشی با رزولوشن 1440×3120 پیکسل و نرخ نوسازی 120 هرتز، تجربهای روان و چشمنواز ارائه میدهد. در بخش دوربین نیز سنسور اصلی 200 مگاپیکسلی در کنار سنسور 12 مگاپیکسلی فوقعریض و سنسور تلهفوتو با بزرگنمایی اپتیکال 3 برابر، ثبت تصاویر با جزئیات و داینامیک رنج عالی را ممکن کرده است.',
|
||||
'ترکیب این سختافزار قدرتمند با نرمافزار بهینه، باعث شده که Galaxy S24 Ultra انتخاب مناسبی برای بازی، عکاسی و استفاده روزمره باشد. اگر به دنبال گوشیای با کارایی بالا و امکانات کامل هستید، این مدل میتواند گزینهای ایدهآل برای شما باشد.'
|
||||
]
|
||||
|
||||
const Description: FC = () => {
|
||||
return (
|
||||
<div className="mt-16 w-full">
|
||||
<div className="flex items-center justify-end gap-3 flex-row-reverse">
|
||||
<h2 className="text-primary text-lg">توضیحات</h2>
|
||||
<div className="w-1 h-6 bg-primary rounded"></div>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 rounded-xl bg-white p-6 max-h-[420px] overflow-y-auto">
|
||||
<div className="text-sm text-[#333333] leading-8 text-justify space-y-4">
|
||||
{DESCRIPTION.map((paragraph) => (
|
||||
<p key={paragraph}>{paragraph}</p>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Description
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import { FC } from 'react'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
|
||||
const PROS: string[] = [
|
||||
'مجهز به حسگر اثرانگشت',
|
||||
'مقاوم در برابر آب',
|
||||
'مناسب بازی',
|
||||
'مناسب عکاسی',
|
||||
'مناسب عکاسی سلفی',
|
||||
]
|
||||
|
||||
const CONS: string[] = [
|
||||
'وزن نسبتاً بالا',
|
||||
'عدم وجود شارژر در جعبه',
|
||||
]
|
||||
|
||||
const ProsCons: FC = () => {
|
||||
return (
|
||||
<div className="mt-16 w-full">
|
||||
<div className="flex items-center justify-end gap-3 flex-row-reverse">
|
||||
<h2 className="text-primary text-lg">نقاط مثبت و منفی</h2>
|
||||
<div className="w-1 h-6 bg-primary rounded"></div>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 rounded-xl bg-white">
|
||||
<div className="grid grid-cols-12 items-start px-6 py-5">
|
||||
<div className="col-span-12 md:col-span-3 flex items-center justify-end gap-2 text-[#7F7F7F] flex-row-reverse">
|
||||
<span className="text-sm">نقاط مثبت</span>
|
||||
<span className="size-1.5 rounded-full bg-[#7F7F7F]"></span>
|
||||
</div>
|
||||
<div className="col-span-12 md:col-span-9">
|
||||
<div className="flex flex-col gap-3 text-sm text-[#333333] text-center md:text-center">
|
||||
{PROS.map((v) => (
|
||||
<div key={v}>{v}</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="grid grid-cols-12 items-start px-6 py-5">
|
||||
<div className="col-span-12 md:col-span-3 flex items-center justify-end gap-2 text-[#7F7F7F] flex-row-reverse">
|
||||
<span className="text-sm">نقاط منفی</span>
|
||||
<span className="size-1.5 rounded-full bg-[#7F7F7F]"></span>
|
||||
</div>
|
||||
<div className="col-span-12 md:col-span-9">
|
||||
<div className="flex flex-col gap-3 text-sm text-[#333333] text-center md:text-center">
|
||||
{CONS.map((v) => (
|
||||
<div key={v}>{v}</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProsCons
|
||||
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
'use client'
|
||||
|
||||
import { FC, Fragment, useMemo, useState } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { ArrowDown2, MessageQuestion } from 'iconsax-react'
|
||||
|
||||
type AnswerItem = {
|
||||
id: number
|
||||
text: string
|
||||
}
|
||||
|
||||
type QuestionItem = {
|
||||
id: number
|
||||
text: string
|
||||
answers: AnswerItem[]
|
||||
}
|
||||
|
||||
const QUESTIONS: QuestionItem[] = [
|
||||
{
|
||||
id: 1,
|
||||
text: 'سلام ویتنام هست یا هند؟',
|
||||
answers: [],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
text:
|
||||
'سلام من دوتا اپلیکیشن همراه بانک نصب نمیشه، دلیلش چیه؟ همچنین مشکل برای کسی پیش اومده؟',
|
||||
answers: [
|
||||
{ id: 1, text: 'مشکل از همراه بانکهای هست که با نسخه جدید طراحی نشدن' },
|
||||
{ id: 2, text: 'اپ رو آپدیت کنید یا کش رو پاک کنید، برای من حل شد' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
text: 'وزن چطاست؟ ویتنام که نیست؟',
|
||||
answers: [
|
||||
{ id: 1, text: 'زیر عکس که نوشتند ویتنام' },
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const Questions: FC = () => {
|
||||
const [sort, setSort] = useState<'new' | 'answers'>('new')
|
||||
const [expandedId, setExpandedId] = useState<number | null>(null)
|
||||
|
||||
const sorted = useMemo(() => {
|
||||
if (sort === 'new') return [...QUESTIONS].sort((a, b) => b.id - a.id)
|
||||
return [...QUESTIONS].sort((a, b) => b.answers.length - a.answers.length)
|
||||
}, [sort])
|
||||
|
||||
return (
|
||||
<div className="mt-16 w-full">
|
||||
<div className="flex items-center justify-end gap-3 flex-row-reverse">
|
||||
<h2 className="text-primary text-lg">پرسشها</h2>
|
||||
<div className="w-1 h-6 bg-primary rounded" />
|
||||
</div>
|
||||
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<div className="col-span-12 md:col-span-9 md:order-last">
|
||||
<div className="flex items-center justify-end gap-4 text-sm text-[#7F7F7F] flex-row-reverse">
|
||||
<span>مرتب سازی بر اساس:</span>
|
||||
<div className="flex items-center gap-6">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setSort('new')}
|
||||
className={sort === 'new' ? 'text-primary font-medium' : 'text-[#B2B2B2] hover:text-primary'}
|
||||
>
|
||||
جدیدترین
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setSort('answers')}
|
||||
className={
|
||||
sort === 'answers' ? 'text-primary font-medium' : 'text-[#B2B2B2] hover:text-primary'
|
||||
}
|
||||
>
|
||||
بیشترین پاسخ
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 rounded-xl bg-white divide-y divide-[#E6E6E6]">
|
||||
{sorted.map((q) => (
|
||||
<Fragment key={q.id}>
|
||||
<div className="px-6 py-5">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="shrink-0 mt-0.5">
|
||||
<div className="size-8 rounded-full border border-[#DDE9FF] bg-[#F6FAFF] flex items-center justify-center">
|
||||
<MessageQuestion size={18} color="#2F80ED" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-[#333333] text-sm leading-7 text-right flex-1">
|
||||
{q.text}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{q.answers.length === 0 && (
|
||||
<div className="mt-3 text-primary text-xs cursor-pointer w-fit">ثبت پاسخ</div>
|
||||
)}
|
||||
|
||||
{q.answers.length > 0 && (
|
||||
<div className="mt-4 text-[#7F7F7F]">
|
||||
{(expandedId === q.id ? q.answers : q.answers.slice(0, 1)).map((a) => (
|
||||
<div key={a.id} className='flex gap-2 items-center mt-2 first:mt-0'>
|
||||
<div className="text-xs">پاسخ</div>
|
||||
<div className="text-xs leading-7 text-right text-[#333333]">
|
||||
{a.text}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{q.answers.length > 1 && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setExpandedId(expandedId === q.id ? null : q.id)}
|
||||
className="mt-3 flex items-center gap-1 text-xs text-primary"
|
||||
>
|
||||
{expandedId === q.id ? 'بستن پاسخها' : 'مشاهده پاسخ های دیگر'}
|
||||
<ArrowDown2 size={14} color="blue" className={expandedId === q.id ? 'rotate-180 transition' : 'transition'} />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{/* rows divided by parent divide-y */}
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-span-12 md:col-span-3 order-first md:order-first">
|
||||
<div className=" rounded-[10px] p-6 h-fit md:sticky md:top-[170px]">
|
||||
<div className="text-primary text-sm">پرسشها</div>
|
||||
<div className="mt-4 text-xs text-[#7F7F7F] leading-6 text-justify">
|
||||
شما هم درباره این کالا پرسش خود را ثبت کنید
|
||||
</div>
|
||||
<Button className="w-full mt-5">ثبت پرسش</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Questions
|
||||
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
'use client'
|
||||
import { FC, useMemo, useState } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { Star1 } from 'iconsax-react'
|
||||
|
||||
type ReviewItem = {
|
||||
id: number
|
||||
author: string
|
||||
date: string
|
||||
rating: number
|
||||
text: string
|
||||
isBuyer: boolean
|
||||
}
|
||||
|
||||
const REVIEWS: ReviewItem[] = [
|
||||
{
|
||||
id: 1,
|
||||
author: 'علی رضوانی',
|
||||
date: '1401 آذر 18',
|
||||
rating: 3.2,
|
||||
text: 'قیمتش خوب بود',
|
||||
isBuyer: true,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
author: 'علی رضوانی',
|
||||
date: '1401 آذر 18',
|
||||
rating: 3.0,
|
||||
text: 'گوشیشه همه چی تمام',
|
||||
isBuyer: true,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
author: 'علی رضوانی',
|
||||
date: '1401 آذر 18',
|
||||
rating: 1.5,
|
||||
text: 'با کیفیت، خوش دست. بدون هیچ امتیاز قابل توجه نسبت به s23',
|
||||
isBuyer: true,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
author: 'علی رضوانی',
|
||||
date: '1401 آذر 18',
|
||||
rating: 4.0,
|
||||
text: 'واقعاً گوشی فوقالعادهای هست هیچ نکته ی منفی ندارد',
|
||||
isBuyer: true,
|
||||
},
|
||||
]
|
||||
|
||||
const Reviews: FC = () => {
|
||||
const [sort, setSort] = useState<'new' | 'helpful'>('new')
|
||||
|
||||
const sortedReviews = useMemo(() => {
|
||||
if (sort === 'new') {
|
||||
return [...REVIEWS].sort((a, b) => b.id - a.id)
|
||||
}
|
||||
return [...REVIEWS].sort((a, b) => b.rating - a.rating)
|
||||
}, [sort])
|
||||
|
||||
const average = useMemo(() => {
|
||||
const sum = REVIEWS.reduce((acc, r) => acc + r.rating, 0)
|
||||
return (sum / REVIEWS.length).toFixed(1)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="mt-16 w-full">
|
||||
<div className="flex items-center justify-end gap-3 flex-row-reverse">
|
||||
<h2 className="text-primary text-lg">نظرات خریداران</h2>
|
||||
<div className="w-1 h-6 bg-primary rounded"></div>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<div className="col-span-12 md:col-span-9 md:order-last">
|
||||
<div className="flex items-center justify-start gap-4 text-sm text-[#7F7F7F]">
|
||||
<span>مرتب سازی بر اساس:</span>
|
||||
<div className="flex items-center gap-6">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setSort('new')}
|
||||
className={
|
||||
sort === 'new' ? 'text-primary font-medium' : 'text-[#B2B2B2] hover:text-primary'
|
||||
}
|
||||
>
|
||||
جدیدترین
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setSort('helpful')}
|
||||
className={
|
||||
sort === 'helpful' ? 'text-primary font-medium' : 'text-[#B2B2B2] hover:text-primary'
|
||||
}
|
||||
>
|
||||
مفیدترین
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 rounded-xl bg-white">
|
||||
{sortedReviews.map((r, idx) => (
|
||||
<div key={r.id} className="px-6 py-5">
|
||||
<div className="flex items-center justify-start gap-3">
|
||||
<div className="px-2 py-0.5 rounded-full bg-[#02A55A] text-white text-xs">{r.rating}</div>
|
||||
<div className="flex items-center gap-2 text-xs text-[#999999]">
|
||||
{r.isBuyer && (
|
||||
<span className="px-2 py-0.5 rounded bg-[#F1F1F1] text-[#7F7F7F]">خریدار</span>
|
||||
)}
|
||||
<span className="size-1.5 rounded-full bg-[#E5E5E5]"></span>
|
||||
<span>{r.date}</span>
|
||||
<span className="size-1.5 rounded-full bg-[#E5E5E5]"></span>
|
||||
<span>{r.author}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-3 text-[#333333] text-sm leading-7 text-right">{r.text}</div>
|
||||
|
||||
{idx !== sortedReviews.length - 1 && <div className="mt-5"> <Separator /> </div>}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-span-12 md:col-span-3 order-first md:order-first">
|
||||
<div className=" rounded-[10px] p-6 h-fit md:sticky md:top-[170px]">
|
||||
<div className="text-primary text-sm">نظرات خریداران</div>
|
||||
<div className="mt-4 flex items-center justify-end gap-2">
|
||||
<Star1 size={18} color="#FFC107" variant="Bold" />
|
||||
<div className="text-sm">
|
||||
<span className="font-medium">{average}</span>
|
||||
<span className="text-[#7F7F7F]"> / 5</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4 text-xs text-[#7F7F7F] leading-6 text-justify">
|
||||
شما هم درباره این کالا نظر خود را ثبت کنید. نظر شما پس از بررسی و تایید نمایش داده میشود.
|
||||
</div>
|
||||
<Button className="w-full mt-5">ثبت دیدگاه</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Reviews
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import React from 'react'
|
||||
|
||||
const ShopInformation = () => {
|
||||
return (
|
||||
<div className='w-[325px] bg-[#FAFAFA] rounded-[10px] p-7 border border-border h-fit'>
|
||||
<div className='w-[325px] bg-[#FAFAFA] rounded-[10px] p-7 border border-border h-fit sticky top-[170px] self-start'>
|
||||
<div className='text-lg text-[#191919]'>
|
||||
فروشنده
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user