perf: product detail page modified

This commit is contained in:
Afra ‌ 2026-09-02 00:29:50 +03:30
parent 89d2a090e1
commit 418a63734f
3 changed files with 42 additions and 34 deletions

View file

@ -14,8 +14,8 @@ export default async function ProductDetailPage({ params }: PageProps<"/products
if (!product) notFound();
return (
<main className="flex min-h-full flex-1 flex-col bg-white pb-[140px]">
<div className="relative h-[320px] w-full shrink-0 overflow-hidden bg-muted">
<main className="flex min-h-full flex-1 flex-col bg-white pb-35">
<div className="relative h-80 w-full shrink-0 overflow-hidden bg-muted">
{product.image_url && (
// eslint-disable-next-line @next/next/no-img-element
<img src={product.image_url} alt={product.name} className="h-full w-full object-cover" />
@ -24,9 +24,9 @@ export default async function ProductDetailPage({ params }: PageProps<"/products
</div>
<div className="flex flex-col gap-5 px-5 pt-5">
<div className="flex flex-col gap-2">
<div className="flex flex-col gap-6">
<h1 className="text-[24px] font-bold leading-none text-foreground">{product.name}</h1>
<Link href={`/stores/${product.store.uuid}`} className="w-fit text-[14px] font-semibold leading-none text-primary-action">
<Link href={`/stores/${product.store.uuid}`} className="w-fit text-[14px] font-medium leading-none text-gray-600">
{product.store.name}
</Link>
<p className="text-headline-medium font-extrabold leading-none text-primary-action">

View file

@ -2,7 +2,7 @@
import { useEffect, useState, useTransition, type ReactNode } from "react";
import { useRouter, usePathname } from "next/navigation";
import { Minus, Plus, X, CheckCircle2 } from "lucide-react";
import { Minus, Plus, X, CheckCircle2, Check } from "lucide-react";
import { addToCart } from "@/lib/api/cart";
import { toPersianDigits } from "@/lib/format/persian-digits";
import type { ProductDetail } from "@/types/api";
@ -41,17 +41,19 @@ export function ProductPurchasePanel({ product, children }: { product: ProductDe
return (
<>
{/* DOM order intentionally mirrors the screenshot's left→right layout, not reading
order — under this app's inherited dir="rtl", the first child renders at the
right edge and the last at the left, so "what should appear on the left" must
come last in JSX (stepper group, then its own "+" trailing last so it lands
leftmost within the group). */}
<div className="flex h-10 items-center justify-between">
<span
className={`inline-flex items-center gap-1 text-[14px] font-semibold leading-none ${product.is_out_of_stock ? "text-error" : "text-emerald-500"}`}
>
{product.is_out_of_stock ? "ناموجود" : "موجود"}
{!product.is_out_of_stock && <Check className="h-3.5 w-3.5" />}
</span>
<div className="flex items-center gap-4">
<button
type="button"
onClick={() => setQuantity((q) => q + 1)}
aria-label="افزایش تعداد"
className="flex h-10 w-10 items-center justify-center rounded-[20px] border border-gray-300 text-primary-action"
>
<Plus className="h-4 w-4" />
</button>
<span className="text-[18px] font-bold leading-none text-black">{toPersianDigits(quantity)}</span>
<button
type="button"
onClick={() => setQuantity((q) => Math.max(1, q - 1))}
@ -60,10 +62,16 @@ export function ProductPurchasePanel({ product, children }: { product: ProductDe
>
<Minus className="h-4 w-4" />
</button>
<span className="text-[18px] font-bold leading-none text-black">{toPersianDigits(quantity)}</span>
<button
type="button"
onClick={() => setQuantity((q) => q + 1)}
aria-label="افزایش تعداد"
className="flex h-10 w-10 items-center justify-center rounded-[20px] border border-gray-300 text-primary-action"
>
<Plus className="h-4 w-4" />
</button>
</div>
<span className={`text-[14px] font-semibold leading-none ${product.is_out_of_stock ? "text-error" : "text-emerald-500"}`}>
{product.is_out_of_stock ? "ناموجود" : "موجود ●"}
</span>
</div>
<div className="h-px w-full bg-gray-300" />
@ -73,35 +81,35 @@ export function ProductPurchasePanel({ product, children }: { product: ProductDe
{error && <p className="px-5 text-body-medium text-error">{error}</p>}
{showToast && (
<div className="fixed inset-x-0 bottom-[135px] z-20 mx-auto flex w-[358px] max-w-[calc(100%-32px)] items-center gap-3 rounded-2xl border border-emerald-border bg-emerald-bg p-4 shadow-[0_4px_12px_0_rgba(16,185,129,0.102)]">
<button type="button" onClick={() => setShowToast(false)} aria-label="بستن">
<X className="h-4 w-4 text-emerald-500" />
</button>
<div className="fixed inset-x-0 bottom-33.75 z-20 mx-auto flex w-89.5 max-w-[calc(100%-32px)] items-center gap-3 rounded-2xl border border-emerald-border bg-emerald-bg p-4 shadow-[0_4px_12px_0_rgba(16,185,129,0.102)]">
<CheckCircle2 className="h-6 w-6 shrink-0 fill-emerald-500 text-white" />
<span className="flex-1 text-right text-[14px] font-bold leading-none text-emerald-500">
محصول به سبد خرید اضافه شد
</span>
<CheckCircle2 className="h-6 w-6 shrink-0 fill-emerald-500 text-white" />
<button type="button" onClick={() => setShowToast(false)} aria-label="بستن">
<X className="h-4 w-4 text-emerald-500" />
</button>
</div>
)}
<div className="fixed inset-x-0 bottom-0 z-10 mx-auto flex w-full max-w-[390px] flex-col items-center gap-2 bg-white px-4 pt-4 pb-2 shadow-[0_-4px_15px_0_rgba(0,0,0,0.102)]">
<div className="fixed inset-x-0 bottom-0 z-10 mx-auto flex w-full max-w-97.5 flex-col items-center gap-2 bg-white px-4 pt-4 pb-2 shadow-[0_-4px_15px_0_rgba(0,0,0,0.102)]">
<div className="flex w-full items-center justify-between gap-4">
<button
type="button"
onClick={handleAddToCart}
disabled={disabled || isPending}
className="flex h-[53px] w-[245px] items-center justify-center rounded-xl bg-primary-action text-body-large font-bold text-white disabled:opacity-40"
>
{isPending ? "در حال افزودن..." : "افزودن به سبد خرید"}
</button>
<div className="flex flex-col items-end gap-0.5">
<span className="text-[12px] leading-none text-gray-600">جمع کل</span>
<span className="text-[18px] font-bold leading-none text-foreground">
{(product.price * quantity).toLocaleString("fa-IR")} تومان
</span>
</div>
<button
type="button"
onClick={handleAddToCart}
disabled={disabled || isPending}
className="flex h-13.25 w-61.25 items-center justify-center rounded-xl bg-primary-action text-body-large font-bold text-white disabled:opacity-40"
>
{isPending ? "در حال افزودن..." : "افزودن به سبد خرید"}
</button>
</div>
<div className="mt-2 h-[5px] w-[134px] rounded-full bg-black" />
<div className="mt-2 h-1.5 w-32 rounded-full bg-black" />
</div>
</>
);

View file

@ -7,9 +7,9 @@ export function HeroBackButton({ href }: { href: string }) {
<Link
href={href}
aria-label="بازگشت"
className="absolute top-4 right-4 flex h-9 w-9 items-center justify-center rounded-full border border-white bg-white/80 backdrop-blur-lg"
className="absolute top-4 right-4 flex h-9 w-9 items-center justify-center rounded-full bg-black/35 backdrop-blur-lg"
>
<ArrowRight className="h-[19.8px] w-[19.8px] text-black" />
<ArrowRight className="h-[19.8px] w-[19.8px] text-white" />
</Link>
);
}