Desktop polish: seller header contrast, collection image crop, infinite scroll

- seller: only the logo overlaps the cover now; name/handle/stats sit below the
  cover on white (fixes low-contrast name on the gradient)
- collections (home rail, /collections grid, /collection cover): portrait 3/4
  images so full-body figures aren't cropped; detail cover uses object-contain
- MondrianProductList: add IntersectionObserver sentinel for reliable
  infinite scroll on desktop (window-scroll math missed under the footer)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Arda Samadi 2026-06-14 22:19:40 +03:30
parent cf55e16a9a
commit f57fe4e9d6
5 changed files with 27 additions and 8 deletions

View File

@ -131,7 +131,7 @@ const HorizontalCollectionsList = ({
> >
<div className="flex flex-col gap-3 p-3 bg-WHITE2 rounded-2xl border border-inner-border hover:border-gray-300 transition-all duration-300 hover:shadow-lg h-full"> <div className="flex flex-col gap-3 p-3 bg-WHITE2 rounded-2xl border border-inner-border hover:border-gray-300 transition-all duration-300 hover:shadow-lg h-full">
{/* Collection Image */} {/* Collection Image */}
<div className="relative overflow-hidden rounded-xl w-full h-64 bg-gray-100"> <div className="relative overflow-hidden rounded-xl w-full h-64 lg:h-auto lg:aspect-[3/4] bg-gray-100">
{collection.coverImageUrl ? ( {collection.coverImageUrl ? (
<img <img
src={collection.coverImageUrl} src={collection.coverImageUrl}

View File

@ -87,6 +87,24 @@ const MondrianProductList = ({
return () => window.removeEventListener("scroll", handleScroll); return () => window.removeEventListener("scroll", handleScroll);
}, [fetchNextPage, hasNextPage, isFetchingNextPage]); }, [fetchNextPage, hasNextPage, isFetchingNextPage]);
// Sentinel-based trigger — more reliable than scroll math on desktop, where
// the footer can keep the window-scroll threshold from being reached.
const loadMoreRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const el = loadMoreRef.current;
if (!el) return;
const observer = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting && hasNextPage && !isFetchingNextPage) {
fetchNextPage();
}
},
{ rootMargin: "600px" }
);
observer.observe(el);
return () => observer.disconnect();
}, [fetchNextPage, hasNextPage, isFetchingNextPage]);
// Memoize expensive filtering operations // Memoize expensive filtering operations
const { oddProducts, evenProducts } = useMemo(() => { const { oddProducts, evenProducts } = useMemo(() => {
const odd = products.filter((_, index) => index % 2 === 0); const odd = products.filter((_, index) => index % 2 === 0);
@ -177,6 +195,7 @@ const MondrianProductList = ({
</div> </div>
{hasNextPage && <MondrianSkeleton isMini={true} />} {hasNextPage && <MondrianSkeleton isMini={true} />}
<div ref={loadMoreRef} className="h-px w-full" />
</> </>
)} )}
</> </>

View File

@ -143,12 +143,12 @@ export default function CollectionDetail() {
{/* Collection Header */} {/* Collection Header */}
<div className="flex flex-col gap-4 p-4 border-b border-inner-border lg:grid lg:grid-cols-[1.3fr_1fr] lg:gap-8 lg:items-center lg:p-0 lg:py-8 lg:border-0"> <div className="flex flex-col gap-4 p-4 border-b border-inner-border lg:grid lg:grid-cols-[1.3fr_1fr] lg:gap-8 lg:items-center lg:p-0 lg:py-8 lg:border-0">
{/* Cover Image */} {/* Cover Image */}
<div className="relative overflow-hidden rounded-xl w-full mx-auto h-[65vh] bg-gray-100 lg:h-[460px]"> <div className="relative overflow-hidden rounded-xl w-full mx-auto h-[65vh] bg-gray-100 lg:h-auto lg:aspect-[3/4] lg:bg-WHITE2">
{collection.coverImageUrl ? ( {collection.coverImageUrl ? (
<img <img
src={collection.coverImageUrl} src={collection.coverImageUrl}
alt={collection.title || "Collection"} alt={collection.title || "Collection"}
className="w-full h-full object-cover" className="w-full h-full object-cover lg:object-contain"
/> />
) : ( ) : (
<div className="w-full h-full flex items-center justify-center bg-WHITE2"> <div className="w-full h-full flex items-center justify-center bg-WHITE2">

View File

@ -148,7 +148,7 @@ export default function Collections() {
> >
<div className="flex flex-col gap-3 p-4 bg-WHITE2 rounded-2xl border border-inner-border hover:border-gray-300 transition-all duration-300 hover:shadow-lg"> <div className="flex flex-col gap-3 p-4 bg-WHITE2 rounded-2xl border border-inner-border hover:border-gray-300 transition-all duration-300 hover:shadow-lg">
{/* Collection Image - بزرگ */} {/* Collection Image - بزرگ */}
<div className="relative overflow-hidden rounded-xl w-full h-48 bg-gray-100"> <div className="relative overflow-hidden rounded-xl w-full h-48 lg:h-auto lg:aspect-[3/4] bg-gray-100">
{collection.coverImageUrl ? ( {collection.coverImageUrl ? (
<img <img
src={collection.coverImageUrl} src={collection.coverImageUrl}

View File

@ -198,8 +198,8 @@ export default function Seller() {
<div className="relative h-[200px] rounded-[22px] overflow-hidden bg-gradient-to-br from-VITROWN_BLUE to-[#14213d]"> <div className="relative h-[200px] rounded-[22px] overflow-hidden bg-gradient-to-br from-VITROWN_BLUE to-[#14213d]">
<div className="absolute inset-0 bg-gradient-to-t from-black/30 to-transparent" /> <div className="absolute inset-0 bg-gradient-to-t from-black/30 to-transparent" />
</div> </div>
<div className="flex items-end gap-6 px-2 -mt-16 relative z-10"> <div className="flex items-start gap-6 px-2 mt-4 relative z-10">
<div className="w-[130px] h-[130px] rounded-[28px] border-[5px] border-WHITE shadow-md overflow-hidden bg-WHITE3 shrink-0"> <div className="w-[130px] h-[130px] -mt-[86px] rounded-[28px] border-[5px] border-WHITE shadow-md overflow-hidden bg-WHITE3 shrink-0">
{seller?.storeLogo ? ( {seller?.storeLogo ? (
<img <img
src={seller.storeLogo} src={seller.storeLogo}
@ -208,7 +208,7 @@ export default function Seller() {
/> />
) : null} ) : null}
</div> </div>
<div className="flex-1 pb-2"> <div className="flex-1">
<h1 className="flex items-center gap-2 text-[28px] font-extrabold"> <h1 className="flex items-center gap-2 text-[28px] font-extrabold">
{seller?.storeName} {seller?.storeName}
{seller?.isVerified ? ( {seller?.isVerified ? (
@ -246,7 +246,7 @@ export default function Seller() {
</div> </div>
</div> </div>
</div> </div>
<div className="flex gap-2.5 pb-2"> <div className="flex gap-2.5">
<Button <Button
variant="dark" variant="dark"
size="lg" size="lg"