responsive

This commit is contained in:
hamid zarghami
2026-07-21 15:16:47 +03:30
parent 14d1422970
commit 112aee1cdf
10 changed files with 46 additions and 44 deletions
+6 -6
View File
@@ -14,10 +14,10 @@ const CategoryHero: FC = () => {
backgroundImage: "linear-gradient(108.5deg, rgba(33, 88, 140, 0.1) 89.65%, rgba(237, 243, 248, 0.3) 100%)", backgroundImage: "linear-gradient(108.5deg, rgba(33, 88, 140, 0.1) 89.65%, rgba(237, 243, 248, 0.3) 100%)",
}} }}
> >
<div className="flex flex-col lg:flex-row lg:items-center gap-6 lg:gap-10"> <div className="flex flex-col gap-6 lg:flex-row lg:items-center lg:gap-10">
<div className="flex-1 space-y-3 sm:space-y-4"> <div className="min-w-0 flex-1 space-y-3 sm:space-y-4">
<h1 className="text-[#0A1B2C] text-2xl sm:text-3xl lg:text-[32px] font-bold">{CATEGORY_TITLE}</h1> <h1 className="text-2xl font-bold text-[#0A1B2C] sm:text-3xl lg:text-[32px]">{CATEGORY_TITLE}</h1>
<p className="text-sm text-[#0A1B2C]/80 leading-7 max-w-140">{CATEGORY_DESCRIPTION}</p> <p className="max-w-140 text-sm leading-7 text-[#0A1B2C]/80">{CATEGORY_DESCRIPTION}</p>
</div> </div>
<div className="relative z-10 mx-auto shrink-0 lg:mx-0 lg:-mb-28"> <div className="relative z-10 mx-auto shrink-0 lg:mx-0 lg:-mb-28">
@@ -27,13 +27,13 @@ const CategoryHero: FC = () => {
width={360} width={360}
height={360} height={360}
priority priority
className="aspect-square w-55 sm:w-70 lg:w-[320px] rounded-2xl sm:rounded-3xl object-cover shadow-[0px_-4px_12px_0px_rgba(0,0,0,0.12)]" className="aspect-square w-48 rounded-2xl object-cover shadow-[0px_-4px_12px_0px_rgba(0,0,0,0.12)] sm:w-60 sm:rounded-3xl lg:w-80"
/> />
</div> </div>
</div> </div>
</section> </section>
<nav aria-label="مسیر صفحه" className="relative z-0 flex items-center justify-start gap-2 bg-secondary px-4 sm:px-8 lg:px-30 py-3 text-sm text-[#3F4D5A]"> <nav aria-label="مسیر صفحه" className="relative z-0 flex items-center justify-start gap-2 overflow-x-auto bg-secondary px-4 py-3 text-sm whitespace-nowrap text-[#3F4D5A] sm:px-8 lg:px-30">
<Link href="/" className="transition-colors hover:text-primary"> <Link href="/" className="transition-colors hover:text-primary">
صفحه اصلی صفحه اصلی
</Link> </Link>
+1 -1
View File
@@ -5,7 +5,7 @@ import { CATEGORY_PRODUCTS } from "../constants";
const CategoryProducts: FC = () => { const CategoryProducts: FC = () => {
return ( return (
<GridWrapper desktop={4} mobile={2} gapDesktop={24} gapMobile={12}> <GridWrapper desktop={4} mobile={2} gapDesktop={24} gapMobile={12} className="sm:gap-4">
{CATEGORY_PRODUCTS.map((title, index) => ( {CATEGORY_PRODUCTS.map((title, index) => (
<ProductCard key={`${title}-${index}`} title={title} /> <ProductCard key={`${title}-${index}`} title={title} />
))} ))}
+4 -4
View File
@@ -5,13 +5,13 @@ import { SORT_OPTIONS } from "../constants";
const CategoryToolbar: FC = () => { const CategoryToolbar: FC = () => {
return ( return (
<div className="flex items-center gap-4"> <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:gap-4">
<div className="max-w-50"> <div className="min-w-0 flex-1">
<Input variant="search" placeholder="جستجو..." className="rounded-xl border-[#E9EFF4]" /> <Input variant="search" placeholder="جستجو..." className="rounded-xl border-[#E9EFF4]" />
</div> </div>
<div className="max-w-50"> <div className="min-w-0 w-full sm:w-auto sm:max-w-50">
<Select options={SORT_OPTIONS} placeholder="مرتب‌سازی" className="min-w-50 rounded-xl border-[#E9EFF4]" /> <Select options={SORT_OPTIONS} placeholder="مرتب‌سازی" className="w-full rounded-xl border-[#E9EFF4] sm:min-w-50" />
</div> </div>
</div> </div>
); );
+4 -6
View File
@@ -9,14 +9,12 @@ const CategoryDetailPage = () => {
<div> <div>
<CategoryHero /> <CategoryHero />
<div className="space-y-4 px-4 mt-10 sm:space-y-5 sm:px-8 lg:px-30 pt-8 sm:pt-10 pb-8 sm:pb-10"> <div className="px-4 pb-8 pt-8 sm:px-8 sm:pb-10 sm:pt-10 lg:px-30 lg:pb-10 lg:pt-16">
<div className="lg:grid lg:gap-x-6">
<CategoryToolbar />
</div>
<div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:gap-6"> <div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:gap-6">
<CategoryFilters /> <CategoryFilters />
<div className="min-w-0 flex-1">
<div className="min-w-0 flex-1 space-y-4 sm:space-y-5">
<CategoryToolbar />
<CategoryProducts /> <CategoryProducts />
</div> </div>
</div> </div>
+1 -1
View File
@@ -5,7 +5,7 @@ import { BREADCRUMB_ITEMS } from "../constants";
const ProductBreadcrumb: FC = () => { const ProductBreadcrumb: FC = () => {
return ( return (
<nav aria-label="مسیر صفحه" className="flex items-center justify-start gap-2 bg-secondary px-4 py-3 text-sm text-[#3F4D5A] sm:px-8 lg:px-30"> <nav aria-label="مسیر صفحه" className="flex items-center justify-start gap-2 overflow-x-auto bg-secondary px-4 py-3 text-sm whitespace-nowrap text-[#3F4D5A] sm:px-8 lg:px-30">
{BREADCRUMB_ITEMS.map((item, index) => ( {BREADCRUMB_ITEMS.map((item, index) => (
<Fragment key={item.label}> <Fragment key={item.label}>
{index > 0 && <ArrowLeft2 size={14} color="currentColor" className="shrink-0" />} {index > 0 && <ArrowLeft2 size={14} color="currentColor" className="shrink-0" />}
+4 -2
View File
@@ -17,10 +17,11 @@ const ProductDetail: FC = () => {
<> <>
<ProductBreadcrumb /> <ProductBreadcrumb />
<div className="pb-28 lg:pb-0">
<div className="px-4 py-6 sm:px-8 sm:py-8 lg:px-30"> <div className="px-4 py-6 sm:px-8 sm:py-8 lg:px-30">
<div className="flex flex-col items-start gap-8 lg:flex-row lg:items-start lg:gap-10"> <div className="flex flex-col items-start gap-6 sm:gap-8 lg:flex-row lg:items-start lg:gap-10">
<ProductGallery images={PRODUCT_GALLERY_IMAGES} alt={PRODUCT_TITLE} /> <ProductGallery images={PRODUCT_GALLERY_IMAGES} alt={PRODUCT_TITLE} />
<div className="flex w-full min-w-0 flex-1 flex-col items-end"> <div className="flex w-full min-w-0 flex-1 flex-col">
<ProductPurchaseOptions selectedTier={selectedTier} onSelectTier={setSelectedTier} /> <ProductPurchaseOptions selectedTier={selectedTier} onSelectTier={setSelectedTier} />
<ProductOrderActions selectedTier={selectedTier} /> <ProductOrderActions selectedTier={selectedTier} />
</div> </div>
@@ -30,6 +31,7 @@ const ProductDetail: FC = () => {
<ProductTabs /> <ProductTabs />
<ProductRelated /> <ProductRelated />
<ContactCtaSection /> <ContactCtaSection />
</div>
</> </>
); );
}; };
+3 -3
View File
@@ -13,12 +13,12 @@ const ProductGallery: FC<ProductGalleryProps> = ({ images, alt }) => {
const [activeIndex, setActiveIndex] = useState(0); const [activeIndex, setActiveIndex] = useState(0);
return ( return (
<div className="flex w-full shrink-0 flex-col gap-3 lg:w-[45%] lg:max-w-120"> <div className="flex w-full shrink-0 flex-col gap-3 sm:gap-4 lg:w-[45%] lg:max-w-120">
<div className="relative aspect-square w-full overflow-hidden rounded-2xl bg-[#F7FAFC]"> <div className="relative aspect-square w-full overflow-hidden rounded-xl bg-[#F7FAFC] sm:rounded-2xl">
<Image src={images[activeIndex]} alt={alt} fill priority className="object-contain p-4" sizes="(max-width: 1024px) 100vw, 480px" /> <Image src={images[activeIndex]} alt={alt} fill priority className="object-contain p-4" sizes="(max-width: 1024px) 100vw, 480px" />
</div> </div>
<div className="grid grid-cols-4 gap-2.5"> <div className="grid grid-cols-4 gap-2 sm:gap-2.5">
{images.slice(0, 4).map((image, index) => ( {images.slice(0, 4).map((image, index) => (
<button <button
key={index} key={index}
@@ -10,18 +10,18 @@ type ProductOrderActionsProps = {
const ProductOrderActions = ({ selectedTier }: ProductOrderActionsProps) => { const ProductOrderActions = ({ selectedTier }: ProductOrderActionsProps) => {
return ( return (
<div className="mt-5 flex w-full flex-col gap-3 self-end rounded-t-2xl bg-white px-4 py-4 shadow-[0px_-4px_34px_0px_#1A456C14] sm:flex-row sm:items-center sm:justify-between sm:gap-5 sm:px-5 sm:py-4"> <div className="fixed inset-x-0 bottom-0 z-40 flex w-full flex-col gap-3 rounded-t-2xl bg-white px-4 py-3 shadow-[0px_-4px_34px_0px_#1A456C14] sm:flex-row sm:items-center sm:justify-between sm:gap-4 sm:px-8 sm:py-4 lg:relative lg:inset-auto lg:mt-5 lg:gap-5 lg:rounded-t-2xl lg:px-5 lg:shadow-[0px_-4px_34px_0px_#1A456C14]">
<div className="space-y-1 text-right"> <div className="space-y-1 text-right">
<p className="text-base font-bold text-[#0A1B2C] sm:text-lg">{formatPrice(selectedTier.totalPrice)}</p> <p className="text-base font-bold text-[#0A1B2C] sm:text-lg">{formatPrice(selectedTier.totalPrice)}</p>
<div className="flex items-center justify-end gap-2"> <div className="flex flex-wrap items-center justify-end gap-2">
<span className="text-xs text-[#0A1B2C] sm:text-sm">{formatPrice(selectedTier.unitPrice)} به ازای هر عدد</span> <span className="text-xs text-[#0A1B2C] sm:text-sm">{formatPrice(selectedTier.unitPrice)} به ازای هر عدد</span>
<span className="rounded-lg bg-[#00893E] px-2 py-0.5 text-[10px] font-medium text-white sm:text-xs">{PRODUCT_PROFIT_PERCENT}٪ سود</span> <span className="rounded-lg bg-[#00893E] px-2 py-0.5 text-[10px] font-medium text-white sm:text-xs">{PRODUCT_PROFIT_PERCENT}٪ سود</span>
</div> </div>
</div> </div>
<div className="hidden h-16 w-0 shrink-0 border-s-2 border-primary sm:block" /> <div className="hidden h-16 w-0 shrink-0 border-s-2 border-primary lg:block" />
<div className="flex flex-col gap-2 sm:flex-row sm:items-end sm:gap-5"> <div className="flex flex-col gap-2 sm:flex-row sm:items-end sm:gap-4 lg:gap-5">
<div className="w-full space-y-1 sm:w-32"> <div className="w-full space-y-1 sm:w-32">
<span className="block text-right text-xs font-medium text-[#0A1B2C]">تعداد</span> <span className="block text-right text-xs font-medium text-[#0A1B2C]">تعداد</span>
<Select options={[...STICKY_QUANTITY_OPTIONS]} defaultValue="1000" className="h-10 rounded-xl border-[#E9EEF2] text-sm" /> <Select options={[...STICKY_QUANTITY_OPTIONS]} defaultValue="1000" className="h-10 rounded-xl border-[#E9EEF2] text-sm" />
@@ -14,11 +14,11 @@ type ProductPurchaseOptionsProps = {
const ProductPurchaseOptions: FC<ProductPurchaseOptionsProps> = ({ selectedTier, onSelectTier }) => { const ProductPurchaseOptions: FC<ProductPurchaseOptionsProps> = ({ selectedTier, onSelectTier }) => {
return ( return (
<div className="flex w-full min-w-0 flex-1 flex-col items-end gap-4"> <div className="flex w-full min-w-0 flex-1 flex-col gap-4">
<div className="w-full space-y-2 text-right"> <div className="w-full space-y-2 text-right">
<h1 className="text-xl font-bold text-[#0A1B2C] sm:text-2xl">{PRODUCT_TITLE}</h1> <h1 className="text-xl font-bold text-[#0A1B2C] sm:text-2xl">{PRODUCT_TITLE}</h1>
<div className="flex items-center gap-2"> <div className="flex items-center justify-end gap-2">
<Rating initialValue={PRODUCT_RATING} size={16} readonly rtl allowFraction SVGstyle={{ display: "inline-block" }} /> <Rating initialValue={PRODUCT_RATING} size={16} readonly rtl allowFraction SVGstyle={{ display: "inline-block" }} />
<span className="text-sm font-medium text-[#0A1B2C]">{PRODUCT_RATING}</span> <span className="text-sm font-medium text-[#0A1B2C]">{PRODUCT_RATING}</span>
</div> </div>
@@ -55,17 +55,19 @@ const ProductPurchaseOptions: FC<ProductPurchaseOptionsProps> = ({ selectedTier,
type="button" type="button"
onClick={() => onSelectTier(tier)} onClick={() => onSelectTier(tier)}
className={cn( className={cn(
"flex h-10 w-full items-center justify-between gap-2 rounded-xl border border-[#E9EEF2] px-3 transition-colors", "flex min-h-10 w-full items-center justify-between gap-1.5 rounded-xl border border-[#E9EEF2] px-2.5 transition-colors sm:gap-2 sm:px-3",
isSelected ? "bg-secondary" : "bg-white hover:bg-secondary/50", isSelected ? "bg-secondary" : "bg-white hover:bg-secondary/50",
)} )}
> >
<span className="w-6 shrink-0 text-end text-xs font-medium text-[#0A1B2C]">{tier.quantity}</span> <span className="hidden w-6 shrink-0 text-end text-xs font-medium text-[#0A1B2C] sm:inline">{tier.quantity}</span>
<div className="flex min-w-0 flex-1 items-center justify-center gap-2"> <div className="flex min-w-0 flex-1 items-center justify-between gap-1.5 sm:justify-center sm:gap-2">
<span className="text-[11px] text-[#0A1B2C] sm:text-xs">{formatPrice(tier.unitPrice)} / هر عدد</span> <span className="text-[10px] text-[#0A1B2C] sm:text-xs">{formatPrice(tier.unitPrice)} / هر عدد</span>
<span className="text-xs font-medium text-[#0A1B2C]">{formatPrice(tier.totalPrice)}</span> <span className="text-[11px] font-medium text-[#0A1B2C] sm:text-xs">{formatPrice(tier.totalPrice)}</span>
</div> </div>
<span className={cn("w-6 shrink-0 text-start text-xs font-medium", tier.discount ? "text-[#DC0000]" : "text-[#0A1B2C]")}>{tier.discount ? `-${tier.discount}%` : tier.quantity}</span> <span className={cn("w-8 shrink-0 text-start text-[10px] font-medium sm:w-6 sm:text-xs", tier.discount ? "text-[#DC0000]" : "text-[#0A1B2C]")}>
{tier.discount ? `-${tier.discount}%` : tier.quantity}
</span>
</button> </button>
); );
})} })}
+1 -1
View File
@@ -5,7 +5,7 @@ import { RELATED_PRODUCTS } from "../constants";
const ProductRelated: FC = () => { const ProductRelated: FC = () => {
return ( return (
<section className="mt-10 px-4 sm:mt-12 sm:px-8 lg:px-30"> <section className="mt-8 px-4 sm:mt-10 sm:px-8 lg:mt-12 lg:px-30">
<Carousel slidesPerView={{ base: 1.4, md: 3, lg: 5 }} gap={{ base: 12, md: 16, lg: 24 }}> <Carousel slidesPerView={{ base: 1.4, md: 3, lg: 5 }} gap={{ base: 12, md: 16, lg: 24 }}>
<div className="flex items-center justify-between gap-4"> <div className="flex items-center justify-between gap-4">
<h2 className="text-lg font-bold text-[#0A1B2C] sm:text-xl">محصولات مرتبط</h2> <h2 className="text-lg font-bold text-[#0A1B2C] sm:text-xl">محصولات مرتبط</h2>