Vitron-Front/app/routes/search.$query.tsx
Arda Samadi 252d3f8f50 feat(search/explore): home-style product tiles with seller brand chip
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>
2026-06-27 17:09:58 +03:30

126 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

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 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 },
];
};