Bring the home product tile to search and explore: seller brand chip (SellerChip) plus price/discount overlay, instead of a bare image. - search: map discountPercent/discountPrice/basePrice + sellerName from ProductSearchResult (sellerName is already returned, so the chip shows now with the initial-letter fallback). - explore: map sellerName/sellerLogo/sellerUsername (and add them to the Product type); these populate once the backend ProductSerializer change ships. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
126 lines
4.3 KiB
TypeScript
126 lines
4.3 KiB
TypeScript
import React, { useState, useEffect } from "react";
|
||
import { MetaFunction } from "@remix-run/node";
|
||
import { Link, useNavigate, useParams } from "@remix-run/react";
|
||
import { onSearch } from "../utils/helpers";
|
||
import { PackageSearch } from "lucide-react";
|
||
import { useSearchInfinite } from "../requestHandler/use-search-hooks";
|
||
import MondrianProductList from "~/components/MondrianProductList";
|
||
import SearchTopBar from "~/components/SearchTopBar";
|
||
|
||
export default function Search() {
|
||
const { query } = useParams();
|
||
const navigate = useNavigate();
|
||
const [searchValue, setSearchValue] = useState(query || "");
|
||
|
||
// Update search value when route parameter changes
|
||
useEffect(() => {
|
||
if (query) {
|
||
setSearchValue(query);
|
||
}
|
||
}, [query]);
|
||
|
||
// Use infinite search hook to fetch data, passing the route query parameter
|
||
const {
|
||
data,
|
||
isLoading,
|
||
isError,
|
||
refetch,
|
||
fetchNextPage,
|
||
hasNextPage,
|
||
isFetchingNextPage,
|
||
} = useSearchInfinite(query);
|
||
|
||
return (
|
||
<div className="flex flex-col gap-6 bg-WHITE lg:max-w-[1320px] lg:mx-auto lg:w-full lg:px-8 lg:py-6">
|
||
<div className="lg:hidden">
|
||
<SearchTopBar
|
||
searchValue={searchValue}
|
||
setSearchValue={setSearchValue}
|
||
placeholder="جستجو..."
|
||
minimumPrice={320000}
|
||
maximumPrice={1000000}
|
||
onSearch={() => onSearch(searchValue, navigate)}
|
||
/>
|
||
</div>
|
||
|
||
{/* Desktop results heading */}
|
||
<div className="hidden lg:block">
|
||
<nav className="flex items-center gap-2 text-[13px] text-GRAY">
|
||
<Link to="/" className="hover:text-BLACK">
|
||
خانه
|
||
</Link>
|
||
<span>/</span>
|
||
<span>جستجو</span>
|
||
</nav>
|
||
<h1 className="text-[26px] font-extrabold mt-2">
|
||
نتایج برای «<span className="text-VITROWN_BLUE">{query}</span>»
|
||
</h1>
|
||
</div>
|
||
|
||
<MondrianProductList
|
||
products={
|
||
data?.pages
|
||
.flatMap((page) => page.products)
|
||
.map((product) => {
|
||
return {
|
||
id: product.id,
|
||
title: product.title,
|
||
images: product.imageUrls || [],
|
||
discountPercent: product.discountPercent || 0,
|
||
discountPrice:
|
||
product.discountPrice || product.price || 0,
|
||
basePrice: product.basePrice || product.price || 0,
|
||
sellerName: product.sellerName,
|
||
};
|
||
}) || []
|
||
}
|
||
fetchNextPage={fetchNextPage}
|
||
hasNextPage={hasNextPage}
|
||
isFetchingNextPage={isFetchingNextPage}
|
||
isLoading={isLoading}
|
||
isError={isError}
|
||
refetch={refetch}
|
||
emptyState={{
|
||
image: <PackageSearch size={80} />,
|
||
title: "محصولی در این جستجو یافت نشد",
|
||
description: "جستجوی دیگری انجام دهید یا فیلترها را تغییر دهید",
|
||
}}
|
||
/>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export const meta: MetaFunction = ({ params }) => {
|
||
const query = params.query;
|
||
const title = query ? `جستجو: ${query} | ویترون` : "جستجو | ویترون";
|
||
const description = query
|
||
? `نتایج جستجو برای "${query}" در ویترون - یافتن بهترین محصولات با قیمت مناسب`
|
||
: "جستجو در ویترون - یافتن محصولات مورد نظر با امکانات پیشرفته فیلتر و مرتبسازی";
|
||
const imageUrl =
|
||
"https://vitrownstatics.s3.ir-thr-at1.arvanstorage.ir/SVG-06.svg?versionId=";
|
||
|
||
return [
|
||
{ title },
|
||
{ name: "description", content: description },
|
||
{
|
||
name: "keywords",
|
||
content: `جستجو، ${query || ""}, محصولات، ویترون، فیلتر`,
|
||
},
|
||
{ name: "robots", content: "index, follow" },
|
||
|
||
// Open Graph
|
||
{ 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" },
|
||
|
||
// Twitter Card
|
||
{ name: "twitter:card", content: "summary_large_image" },
|
||
{ name: "twitter:title", content: title },
|
||
{ name: "twitter:description", content: description },
|
||
{ name: "twitter:image", content: imageUrl },
|
||
];
|
||
};
|