perf: product detail page modified
This commit is contained in:
parent
89d2a090e1
commit
418a63734f
3 changed files with 42 additions and 34 deletions
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue