Files
website/src/components/product-detail/ProductTabs.tsx
T

195 lines
6.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
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.
"use client";
import Image from "next/image";
import { useEffect, useState } from "react";
import type { ProductComment, ProductDetail } from "@/data/product-detail";
type TabId = "technical" | "comments" | "sizing";
const TABS: Array<{ id: TabId; labelFa: string; labelEn: string }> = [
{ id: "technical", labelFa: "مشخصات فنی", labelEn: "Technical info" },
{ id: "comments", labelFa: "نظرات", labelEn: "Comments" },
{ id: "sizing", labelFa: "راهنمای سایز", labelEn: "Sizing guide" },
];
type ProductTabsProps = {
product: ProductDetail;
initialTab?: TabId;
};
function Stars({ rating }: { rating: number }) {
return (
<span dir="ltr" className="font-montserrat text-xs tracking-widest text-ink" aria-label={`${rating} از ۵`}>
{"★".repeat(Math.max(0, Math.min(5, rating)))}
<span className="text-muted">{"★".repeat(Math.max(0, 5 - rating))}</span>
</span>
);
}
function CommentsList({ comments }: { comments: ProductComment[] }) {
if (!comments.length) {
return (
<p className="text-sm text-muted">هنوز نظری ثبت نشده است.</p>
);
}
return (
<ul className="space-y-4">
{comments.map((c) => (
<li key={c.id} className="border border-line bg-white p-4">
<div className="flex flex-wrap items-center justify-between gap-2">
<p className="text-sm text-ink">{c.author}</p>
<Stars rating={c.rating} />
</div>
<p dir="ltr" className="mt-1 font-montserrat text-[10px] text-muted">
{c.date}
</p>
<p className="mt-3 text-sm leading-7 text-ink/80">{c.body}</p>
</li>
))}
</ul>
);
}
export default function ProductTabs({
product,
initialTab = "technical",
}: ProductTabsProps) {
const [tab, setTab] = useState<TabId>(initialTab);
useEffect(() => {
const onHash = () => {
if (window.location.hash === "#product-tabs") {
setTab("sizing");
}
};
onHash();
window.addEventListener("hashchange", onHash);
return () => window.removeEventListener("hashchange", onHash);
}, []);
return (
<section id="product-tabs" className="scroll-mt-28 border-t border-line pt-10">
<div className="flex flex-wrap gap-2 border-b border-line pb-px">
{TABS.map((t) => {
const active = tab === t.id;
return (
<button
key={t.id}
type="button"
onClick={() => setTab(t.id)}
className={`-mb-px border-b-2 px-3 py-3 text-start transition-colors sm:px-4 ${
active
? "border-ink text-ink"
: "border-transparent text-muted hover:text-ink"
}`}
aria-selected={active}
role="tab"
>
<span className="block text-sm sm:text-base">{t.labelFa}</span>
<span
dir="ltr"
className="mt-0.5 block font-montserrat text-[10px] tracking-[0.12em] uppercase opacity-70"
>
{t.labelEn}
</span>
</button>
);
})}
</div>
<div className="py-8" role="tabpanel">
{tab === "technical" ? (
<div className="space-y-6">
<p className="max-w-3xl text-sm leading-7 text-ink/75">
{product.description}
</p>
<dl className="divide-y divide-line border border-line">
{product.technical.map((row) => (
<div
key={row.labelEn}
className="grid grid-cols-1 gap-1 px-4 py-3 sm:grid-cols-[minmax(0,11rem)_1fr] sm:items-baseline sm:gap-6"
>
<dt>
<span className="text-sm text-ink">{row.labelFa}</span>
<span
dir="ltr"
className="mt-0.5 block font-montserrat text-[10px] tracking-wide text-muted"
>
{row.labelEn}
</span>
</dt>
<dd className="text-sm text-ink/80 sm:text-end">{row.value}</dd>
</div>
))}
</dl>
</div>
) : null}
{tab === "comments" ? (
<CommentsList comments={product.comments} />
) : null}
{tab === "sizing" ? (
<div className="grid gap-6 lg:grid-cols-[minmax(0,1.1fr)_minmax(0,0.9fr)] lg:items-start">
<div className="relative aspect-[4/5] overflow-hidden rounded-[5px] border border-line bg-[#f6f6f4]">
{product.sizeGuideImage ? (
<Image
src={product.sizeGuideImage}
alt="راهنمای سایز"
fill
sizes="(max-width: 1024px) 100vw, 520px"
className="object-cover object-top"
/>
) : (
<span className="absolute inset-0 flex items-center justify-center text-sm text-muted">
Size guide image
</span>
)}
</div>
<div>
<h3 className="font-doran text-xl text-ink">جدول اندازه‌ها</h3>
<p
dir="ltr"
className="mt-1 font-montserrat text-[10px] uppercase tracking-[0.16em] text-muted"
>
Measurements (cm)
</p>
<p className="mt-3 text-sm leading-7 text-ink/75">
{product.sizeGuideNote}
</p>
<div className="mt-5 overflow-x-auto border border-line">
<table className="w-full min-w-[320px] text-sm">
<thead className="bg-[#f7f7f5] text-muted">
<tr>
<th className="px-3 py-2.5 text-start font-normal">سایز</th>
<th className="px-3 py-2.5 text-start font-normal">سینه</th>
<th className="px-3 py-2.5 text-start font-normal">کمر</th>
<th className="px-3 py-2.5 text-start font-normal">قد</th>
</tr>
</thead>
<tbody>
{[
["S", "۸۸", "۷۰", "۹۲"],
["M", "۹۲", "۷۴", "۹۵"],
["L", "۹۶", "۷۸", "۹۸"],
["XL", "۱۰۰", "۸۲", "۱۰۰"],
].map((row) => (
<tr key={row[0]} className="border-t border-line">
{row.map((cell) => (
<td key={cell} className="px-3 py-2.5 text-ink">
{cell}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
) : null}
</div>
</section>
);
}