- Delete HeroSection function (Discover Gear heading, search bar, Go to Collection link) - Remove unused imports: Link, Search (lucide-react), useAuth, useUIStore - LandingPage now starts directly with PopularSetupsSection - Search now exclusively in TopNav bar
144 lines
3.9 KiB
TypeScript
144 lines
3.9 KiB
TypeScript
import { createFileRoute } from "@tanstack/react-router";
|
|
import { GlobalItemCard } from "../components/GlobalItemCard";
|
|
import { PublicSetupCard } from "../components/PublicSetupCard";
|
|
import {
|
|
useDiscoveryCategories,
|
|
useDiscoveryItems,
|
|
useDiscoverySetups,
|
|
} from "../hooks/useDiscovery";
|
|
|
|
export const Route = createFileRoute("/")({
|
|
component: LandingPage,
|
|
});
|
|
|
|
function LandingPage() {
|
|
return (
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
|
<PopularSetupsSection />
|
|
<RecentItemsSection />
|
|
<TrendingCategoriesSection />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function PopularSetupsSection() {
|
|
const { data, isLoading } = useDiscoverySetups(6);
|
|
const setups = data?.items ?? [];
|
|
|
|
if (!isLoading && setups.length === 0) return null;
|
|
|
|
return (
|
|
<section className="mb-12">
|
|
<div className="flex items-center justify-between mb-4">
|
|
<h2 className="text-lg font-semibold text-gray-900">Popular Setups</h2>
|
|
</div>
|
|
{isLoading ? (
|
|
<SectionSkeleton count={6} aspect="none" />
|
|
) : (
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
{setups.map((setup) => (
|
|
<PublicSetupCard key={setup.id} setup={setup} />
|
|
))}
|
|
</div>
|
|
)}
|
|
</section>
|
|
);
|
|
}
|
|
|
|
function RecentItemsSection() {
|
|
const { data, isLoading } = useDiscoveryItems(8);
|
|
const items = data?.items ?? [];
|
|
|
|
if (!isLoading && items.length === 0) return null;
|
|
|
|
return (
|
|
<section className="mb-12">
|
|
<div className="flex items-center justify-between mb-4">
|
|
<h2 className="text-lg font-semibold text-gray-900">Recently Added</h2>
|
|
</div>
|
|
{isLoading ? (
|
|
<SectionSkeleton count={8} aspect="[4/3]" />
|
|
) : (
|
|
<div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4">
|
|
{items.map((item) => (
|
|
<GlobalItemCard
|
|
key={item.id}
|
|
id={item.id}
|
|
brand={item.brand}
|
|
model={item.model}
|
|
category={item.category}
|
|
weightGrams={item.weightGrams}
|
|
priceCents={item.priceCents}
|
|
imageUrl={item.imageUrl}
|
|
/>
|
|
))}
|
|
</div>
|
|
)}
|
|
</section>
|
|
);
|
|
}
|
|
|
|
function TrendingCategoriesSection() {
|
|
const { data, isLoading } = useDiscoveryCategories(12);
|
|
const categories = data ?? [];
|
|
|
|
if (!isLoading && categories.length === 0) return null;
|
|
|
|
return (
|
|
<section className="mb-12">
|
|
<div className="flex items-center justify-between mb-4">
|
|
<h2 className="text-lg font-semibold text-gray-900">
|
|
Trending Categories
|
|
</h2>
|
|
</div>
|
|
{isLoading ? (
|
|
<div className="flex flex-wrap gap-2">
|
|
{Array.from({ length: 8 }, (_, i) => `cat-skeleton-${i}`).map(
|
|
(key) => (
|
|
<div
|
|
key={key}
|
|
className="h-8 w-24 bg-gray-100 rounded-full animate-pulse"
|
|
/>
|
|
),
|
|
)}
|
|
</div>
|
|
) : (
|
|
<div className="flex flex-wrap gap-2">
|
|
{categories.map((cat) => (
|
|
<span
|
|
key={cat.name}
|
|
className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full text-sm font-medium bg-gray-50 text-gray-700 border border-gray-100 hover:border-gray-200 hover:bg-gray-100 transition-all cursor-pointer"
|
|
>
|
|
{cat.name}
|
|
<span className="text-xs text-gray-400">{cat.itemCount}</span>
|
|
</span>
|
|
))}
|
|
</div>
|
|
)}
|
|
</section>
|
|
);
|
|
}
|
|
|
|
function SectionSkeleton({ count, aspect }: { count: number; aspect: string }) {
|
|
return (
|
|
<div
|
|
className={`grid ${aspect === "none" ? "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3" : "grid-cols-2 sm:grid-cols-3 lg:grid-cols-4"} gap-4`}
|
|
>
|
|
{Array.from({ length: count }, (_, i) => `skeleton-${i}`).map((key) => (
|
|
<div
|
|
key={key}
|
|
className="bg-white rounded-xl border border-gray-100 overflow-hidden animate-pulse"
|
|
>
|
|
{aspect !== "none" && (
|
|
<div className={`aspect-${aspect} bg-gray-100`} />
|
|
)}
|
|
<div className="p-4 space-y-2">
|
|
<div className="h-3 bg-gray-100 rounded w-16" />
|
|
<div className="h-4 bg-gray-100 rounded w-32" />
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|