Single responsive tree (Tailwind lg:) — mobile experience unchanged; desktop chrome is gated to the home route for now. - DesktopHeader / DesktopFooter components (visible only at lg+), wired to real routes with live cart badge and working search - MyLayout: render desktop header/footer on home, expand container and hide mobile bottom nav at lg - Home: hide mobile top bar at lg, 1320px centered container, desktop hero grid (carousel + 2 promos), larger section headers - SellerTilesGrid: 2-col mobile -> 5-col desktop with name overlay Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
116 lines
3.3 KiB
TypeScript
116 lines
3.3 KiB
TypeScript
import { Link } from "@remix-run/react";
|
||
import { ChevronLeft, Store } from "lucide-react";
|
||
import { Skeleton } from "./ui/skeleton";
|
||
import { ErrorState } from "./UiProvider";
|
||
|
||
interface SellerTile {
|
||
id?: string;
|
||
sellerId?: string;
|
||
sellerUsername?: string;
|
||
sellerName?: string;
|
||
imageUrl?: string;
|
||
order?: number;
|
||
}
|
||
|
||
interface SellerTilesGridProps {
|
||
sellers: SellerTile[];
|
||
isLoading: boolean;
|
||
isError: boolean;
|
||
refetch: () => void;
|
||
showViewAllButton?: boolean;
|
||
}
|
||
|
||
const SellerTilesGrid = ({
|
||
sellers,
|
||
isLoading,
|
||
isError,
|
||
refetch,
|
||
showViewAllButton = true,
|
||
}: SellerTilesGridProps) => {
|
||
if (isLoading) {
|
||
return <SellerTilesSkeleton />;
|
||
}
|
||
|
||
if (isError) {
|
||
return (
|
||
<div className="px-4">
|
||
<ErrorState onRetry={refetch} />
|
||
</div>
|
||
);
|
||
}
|
||
|
||
if (sellers.length === 0) {
|
||
return null;
|
||
}
|
||
|
||
return (
|
||
<div className="w-full flex flex-col gap-4 px-4 lg:px-0">
|
||
{/* 2x2 on mobile, single row of 5 on desktop */}
|
||
<div className="grid grid-cols-2 lg:grid-cols-5 gap-2 lg:gap-4">
|
||
{sellers.slice(0, 5).map((seller, index) => (
|
||
<Link
|
||
key={seller.id || index}
|
||
to={`/seller/${seller.sellerUsername}`}
|
||
className={`relative aspect-square overflow-hidden rounded-lg group ${
|
||
index === 4 ? "hidden lg:block" : ""
|
||
}`}
|
||
>
|
||
{/* Background Image */}
|
||
{seller.imageUrl ? (
|
||
<img
|
||
src={seller.imageUrl}
|
||
alt={seller.sellerName || "فروشگاه"}
|
||
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"
|
||
/>
|
||
) : (
|
||
<div className="w-full h-full bg-gradient-to-br from-gray-200 to-gray-300 flex items-center justify-center">
|
||
<Store size={48} className="text-gray-400" />
|
||
</div>
|
||
)}
|
||
|
||
{/* Desktop-only name overlay (matches the desktop design) */}
|
||
{seller.sellerName ? (
|
||
<>
|
||
<div className="hidden lg:block absolute inset-0 bg-gradient-to-t from-black/60 to-transparent" />
|
||
<div className="hidden lg:flex absolute inset-x-0 bottom-0 p-4 items-end">
|
||
<span className="text-white font-bold text-[15px] truncate">
|
||
{seller.sellerName}
|
||
</span>
|
||
</div>
|
||
</>
|
||
) : null}
|
||
</Link>
|
||
))}
|
||
</div>
|
||
|
||
{/* View All Button */}
|
||
{showViewAllButton && (
|
||
<Link
|
||
to="/sellers"
|
||
className="flex items-center justify-center gap-2 w-full py-3 border border-black rounded-none hover:bg-black hover:text-white transition-colors duration-300"
|
||
>
|
||
<span className="text-B14 font-bold">مشاهده همه فروشگاهها</span>
|
||
<ChevronLeft size={18} />
|
||
</Link>
|
||
)}
|
||
</div>
|
||
);
|
||
};
|
||
|
||
const SellerTilesSkeleton = () => {
|
||
return (
|
||
<div className="w-full flex flex-col gap-4 px-4">
|
||
<div className="grid grid-cols-2 gap-2">
|
||
{Array(4)
|
||
.fill(1)
|
||
.map((_, index) => (
|
||
<Skeleton key={index} className="aspect-square rounded-lg" />
|
||
))}
|
||
</div>
|
||
<Skeleton className="w-full h-12 rounded-none" />
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default SellerTilesGrid;
|