feat(discounts): full تخفیف‌ها page with infinite scroll

Add a complete /discounts page (MondrianProductList grid, infinite scroll via
useGetHomeDiscounts) and link the home page "تخفیف‌ها" section to it with a
«مشاهده همه» link, mirroring the برترین فروشگاه‌ها → /sellers pattern.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Arda Samadi 2026-07-08 14:00:44 +03:30
parent 877b01f483
commit fea80647fa
2 changed files with 119 additions and 5 deletions

View File

@ -134,11 +134,23 @@ export default function HomePage() {
id="section1"
className="w-full gap-2 flex flex-col max-w-[100vw] lg:max-w-none lg:gap-5"
>
<div className="w-full px-4 lg:px-0">
<p className="text-B16 lg:text-B24 font-bold">تخفیفها</p>
<p className="text-R14 lg:text-R16 text-GRAY">
فرصتهایی محدود برای انتخابهای هوشمندانه
</p>
<div className="w-full px-4 lg:px-0 flex items-center justify-between">
<div>
<p className="text-B16 lg:text-B24 font-bold">تخفیفها</p>
<p className="text-R14 lg:text-R16 text-GRAY">
فرصتهایی محدود برای انتخابهای هوشمندانه
</p>
</div>
<Link
to="/discounts"
className="flex items-center gap-1 text-R14 text-gray-500 hover:text-gray-800 hover:underline transition-colors group"
>
<span>مشاهده همه</span>
<ChevronLeft
size={16}
className="group-hover:-translate-x-1 transition-transform"
/>
</Link>
</div>
<HorizontalProductList
products={

102
app/routes/discounts.tsx Normal file
View File

@ -0,0 +1,102 @@
import { MetaFunction } from "@remix-run/node";
import { useNavigate } from "@remix-run/react";
import { ArrowRight, Tag } from "lucide-react";
import { safeGoBack } from "~/utils/helpers";
import { useGetHomeDiscounts } from "~/requestHandler/use-home-hooks";
import MondrianProductList from "~/components/MondrianProductList";
export default function Discounts() {
const navigate = useNavigate();
const {
data,
isLoading,
isError,
refetch,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
} = useGetHomeDiscounts();
const products =
data?.pages.flatMap((page) => (page?.results as unknown[]) || []) || [];
const handleBack = () => safeGoBack(navigate);
return (
<div className="flex flex-col bg-WHITE pb-20">
{/* Header (mobile) */}
<div className="lg:hidden flex flex-row h-[65px] sticky top-0 z-30 bg-WHITE w-full items-center justify-center border-b border-inner-border">
<ArrowRight
onClick={handleBack}
className="absolute top-5 right-5 cursor-pointer"
/>
<p className="text-B16 font-bold">تخفیفها</p>
</div>
{/* Content */}
<div className="flex flex-col py-6 lg:max-w-[1320px] lg:mx-auto lg:w-full lg:px-8">
<div className="hidden lg:block mb-6 px-4 lg:px-0">
<h1 className="text-[28px] font-extrabold">تخفیفها</h1>
<p className="text-R16 text-GRAY mt-1">
فرصتهایی محدود برای انتخابهای هوشمندانه
</p>
</div>
<MondrianProductList
// eslint-disable-next-line @typescript-eslint/no-explicit-any
products={(products as any[]).map((product) => ({
id: product?.id || "",
title: product?.title || "",
images: Array.isArray(product?.imageUrls) ? product.imageUrls : [],
basePrice: product?.basePrice || 0,
discountPercent: product?.discountPercent || 0,
discountPrice: product?.discountPrice || 0,
sellerName: product?.sellerName,
sellerLogo: product?.sellerLogo,
sellerUsername: product?.sellerUsername,
}))}
fetchNextPage={fetchNextPage}
hasNextPage={!!hasNextPage}
isFetchingNextPage={isFetchingNextPage}
isLoading={isLoading}
isError={isError}
refetch={refetch}
emptyState={{
image: <Tag size={80} />,
title: "در حال حاضر تخفیفی موجود نیست",
description: "به زودی پیشنهادهای ویژه‌ای اضافه خواهد شد",
}}
/>
</div>
</div>
);
}
export const meta: MetaFunction = () => {
const title = "تخفیف‌ها | پیشنهادهای ویژه ویترون";
const description =
"جدیدترین محصولات تخفیف‌دار ویترون. خرید با بهترین قیمت از فروشگاه‌های معتبر.";
const imageUrl =
"https://vitrownstatics.s3.ir-thr-at1.arvanstorage.ir/SVG-06.svg?versionId=";
return [
{ title },
{ name: "description", content: description },
{
name: "keywords",
content: "تخفیف، حراج، فروش ویژه، خرید ارزان، ویترون",
},
{ name: "robots", content: "index, follow" },
{ property: "og:type", content: "website" },
{ property: "og:title", content: title },
{ property: "og:description", content: description },
{ property: "og:image", content: imageUrl },
{ property: "og:site_name", content: "ویترون" },
{ property: "og:locale", content: "fa_IR" },
{ name: "twitter:card", content: "summary_large_image" },
{ name: "twitter:title", content: title },
{ name: "twitter:description", content: description },
];
};