Vitron-Front/app/routes/_index.tsx
2026-04-29 01:44:16 +03:30

284 lines
10 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.

import { MetaFunction } from "@remix-run/node";
import { Link, useSearchParams } from "@remix-run/react";
import { useEffect, useState } from "react";
import { ChevronLeft } from "lucide-react";
import HomePageTopBar from "~/components/home/HomePageTopBar";
import { ProductImageCarousel } from "~/components/product";
import {
useGetHomeBanners,
useGetHomeDiscounts,
useGetHomeForYou,
useGetHomeSellerTiles,
} from "~/requestHandler/use-home-hooks";
import { useCollectionsInfiniteQuery } from "~/requestHandler/use-collection-hooks";
import { Skeleton } from "~/components/ui/skeleton";
import MondrianProductList from "~/components/MondrianProductList";
import HorizontalProductList from "~/components/HorizontalProductList";
import HorizontalCollectionsList from "~/components/HorizontalCollectionsList";
import SellerTilesGrid from "~/components/SellerTilesGrid";
import InvoiceModal from "~/components/home/InvoiceModal";
export default function HomePage() {
const [searchParams] = useSearchParams();
const [showInvoiceModal, setShowInvoiceModal] = useState(false);
// Fetch home page data
const { data: banners, isLoading: isLoadingBanners } = useGetHomeBanners();
const {
data: discountsData,
isLoading: isLoadingDiscounts,
isError: isErrorDiscounts,
refetch: refetchDiscounts,
} = useGetHomeDiscounts();
const {
data: forYouData,
isLoading: isLoadingForYou,
isError: isErrorForYou,
refetch: refetchForYou,
fetchNextPage: fetchNextForYou,
hasNextPage: hasNextForYou,
isFetchingNextPage: isFetchingNextForYou,
} = useGetHomeForYou();
const {
data: sellerTilesData,
isLoading: isLoadingSellerTiles,
isError: isErrorSellerTiles,
refetch: refetchSellerTiles,
} = useGetHomeSellerTiles();
const {
data: collectionsData,
isLoading: isLoadingCollections,
isError: isErrorCollections,
refetch: refetchCollections,
} = useCollectionsInfiniteQuery({
pageSize: 10,
});
// Flatten the paginated data - extract results from each page
// For discounts, only get first page
const discounts = discountsData?.pages[0]?.results || [];
const forYouProducts =
forYouData?.pages.flatMap((page) => page?.results || []) || [];
const collections = collectionsData?.pages[0]?.results || [];
useEffect(() => {
// Check if the URL has an invoice parameter
if (searchParams.has("invoice") && searchParams.has("shippinginfo")) {
setShowInvoiceModal(true);
}
}, [searchParams]);
return (
<>
<div className="flex flex-col pb-12">
<HomePageTopBar />
{/* Adding padding to account for fixed header */}
<div className="flex flex-col gap-10">
{/* Banners Section */}
<div className="w-full mt-2 h-[260px]">
{isLoadingBanners ? (
<Skeleton className="w-full h-full" />
) : (
<ProductImageCarousel
images={banners?.map((banner) => banner.imageUrl || "") || []}
hideIndex={true}
borderRadius={0}
/>
)}
</div>
<section
id="section1"
className="w-full gap-2 flex flex-col max-w-[100vw]"
>
<div className="w-full px-4">
<p className="text-B16 font-bold">تخفیفها</p>
<p className="text-R14">
فرصتهایی محدود برای انتخابهای هوشمندانه
</p>
</div>
<HorizontalProductList
products={
discounts?.map((product) => ({
id: product?.id || "",
title: product?.title || "",
images: Array.isArray(product?.imageUrls)
? product.imageUrls
: [],
discountPercent: product?.discountPercent || 0,
discountPrice: product?.discountPrice || 0,
basePrice: product?.basePrice || 0,
})) || []
}
isLoading={isLoadingDiscounts}
isError={isErrorDiscounts}
refetch={refetchDiscounts}
/>
</section>
<section
id="section2"
className="w-full gap-2 flex flex-col max-w-[100vw]"
>
<div className="w-full px-4 flex items-center justify-between">
<div>
<p className="text-B16 font-bold">برترین فروشگاهها</p>
<p className="text-R14">
برگرفته از بالاترین سطح رضایت کاربران
</p>
</div>
<Link
to="/sellers"
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>
<SellerTilesGrid
sellers={sellerTilesData || []}
isLoading={isLoadingSellerTiles}
isError={isErrorSellerTiles}
refetch={refetchSellerTiles}
showViewAllButton={false}
/>
</section>
<section
id="collections"
className="w-full gap-2 flex flex-col max-w-[100vw]"
>
<div className="w-full px-4">
<p className="text-B16 font-bold">کالکشنها</p>
<p className="text-R14">مجموعههای منتخب از بهترین محصولات</p>
</div>
<HorizontalCollectionsList
collections={
collections?.map((collection) => ({
id: collection?.id || "",
title: collection?.title || "",
description: collection?.description || "",
coverImageUrl: collection?.coverImageUrl || "",
productCount: collection?.productCount || 0,
})) || []
}
isLoading={isLoadingCollections}
isError={isErrorCollections}
refetch={refetchCollections}
showViewAllButton={true}
/>
</section>
<section
id="section3"
className="w-full gap-2 flex flex-col max-w-[100vw]"
>
<div className="w-full px-4">
<p className="text-B16 font-bold">برای شما</p>
<p className="text-R14">با الهام از انتخابهای شما</p>
</div>
<MondrianProductList
products={
forYouProducts?.map((product) => ({
id: product?.id || "",
title: product?.title || "",
images: Array.isArray(product?.imageUrls)
? product.imageUrls
: [],
discountPercent: product?.discountPercent || 0,
discountPrice: product?.discountPrice || 0,
basePrice: product?.basePrice || 0,
})) || []
}
fetchNextPage={fetchNextForYou}
hasNextPage={hasNextForYou || false}
isFetchingNextPage={isFetchingNextForYou}
isLoading={isLoadingForYou}
isError={isErrorForYou}
refetch={refetchForYou}
emptyState={{}}
/>
</section>
</div>
</div>
<InvoiceModal
open={showInvoiceModal}
onOpenChange={setShowInvoiceModal}
/>
</>
);
}
export const meta: MetaFunction = () => {
const title = "ویترون | پلتفرم خرید آنلاین - محصولات باکیفیت با بهترین قیمت";
const description =
"ویترون بهترین پلتفرم خرید آنلاین در ایران. محصولات متنوع، قیمت مناسب، تحویل سریع. خرید امن از فروشگاه‌های معتبر با بالاترین کیفیت خدمات.";
const imageUrl =
"https://vitrownstatics.s3.ir-thr-at1.arvanstorage.ir/SVG-06.svg?versionId=";
const siteUrl = import.meta.env.VITE_SITE_URL || "https://vitrown.com/";
return [
{ title },
{ name: "description", content: description },
{
name: "keywords",
content:
"خرید آنلاین، فروشگاه اینترنتی، ویترون، محصولات باکیفیت، خرید امن، تحویل سریع",
},
{ name: "author", content: "Vitrown" },
{ name: "robots", content: "index, follow" },
{ name: "viewport", content: "width=device-width, initial-scale=1" },
// Open Graph
{ property: "og:type", content: "website" },
{ property: "og:title", content: title },
{ property: "og:description", content: description },
{ property: "og:image", content: imageUrl },
{ property: "og:url", content: siteUrl },
{ property: "og:site_name", content: "ویترون" },
{ property: "og:locale", content: "fa_IR" },
// Twitter Card
{ name: "twitter:card", content: "summary_large_image" },
{ name: "twitter:title", content: title },
{ name: "twitter:description", content: description },
{ name: "twitter:image", content: imageUrl },
// Additional SEO
{ name: "theme-color", content: "#ffffff" },
{ name: "mobile-web-app-capable", content: "yes" },
{ name: "apple-mobile-web-app-status-bar-style", content: "default" },
{ name: "format-detection", content: "telephone=no" },
// Structured Data
{
"script:ld+json": {
"@context": "https://schema.org",
"@type": "WebSite",
name: "ویترون",
description: description,
url: siteUrl,
potentialAction: {
"@type": "SearchAction",
target: {
"@type": "EntryPoint",
urlTemplate: siteUrl + "/search/{search_term_string}",
},
"query-input": "required name=search_term_string",
},
publisher: {
"@type": "Organization",
name: "ویترون",
url: siteUrl,
},
},
},
];
};