--- phase: 26-discovery-landing-page plan: 03 type: execute wave: 3 depends_on: [26-01, 26-02] files_modified: - src/client/routes/index.tsx - src/client/components/PublicSetupCard.tsx - src/client/routes/users/$userId.tsx autonomous: false requirements: [DISC-01, DISC-02, DISC-03, DISC-04, DISC-05] must_haves: truths: - "Root URL shows a hero section with a catalog search bar" - "Clicking the search bar opens CatalogSearchOverlay" - "Popular setups section shows setup cards with item counts" - "Recently added items section shows GlobalItemCard components" - "Trending categories section shows category names with item counts" - "Authenticated users see Go to Collection link in hero" - "Anonymous users see the page without login redirect" artifacts: - path: "src/client/routes/index.tsx" provides: "Landing page with hero, popular setups, recent items, trending categories" min_lines: 80 - path: "src/client/components/PublicSetupCard.tsx" provides: "Enhanced setup card with optional itemCount and creatorName" contains: "itemCount" key_links: - from: "src/client/routes/index.tsx" to: "src/client/hooks/useDiscovery.ts" via: "imports useDiscoverySetups, useDiscoveryItems, useDiscoveryCategories" pattern: "from.*useDiscovery" - from: "src/client/routes/index.tsx" to: "src/client/stores/uiStore.ts" via: "openCatalogSearch trigger from hero" pattern: "openCatalogSearch" - from: "src/client/routes/index.tsx" to: "src/client/hooks/useAuth.ts" via: "auth state for conditional Go to Collection CTA" pattern: "useAuth" - from: "src/client/routes/index.tsx" to: "src/client/components/GlobalItemCard.tsx" via: "renders catalog items in recent items section" pattern: "GlobalItemCard" - from: "src/client/routes/index.tsx" to: "src/client/components/PublicSetupCard.tsx" via: "renders setup cards in popular setups section" pattern: "PublicSetupCard" --- Rewrite the landing page at `/` from a personal dashboard to a public discovery page. Enhance PublicSetupCard with itemCount and creatorName. Build hero section with search trigger, three content feed sections, and conditional auth CTA. Purpose: This is the user-facing deliverable — the page visitors see first. Composes existing components with new discovery hooks into the layout specified by D-01 through D-11. Output: Complete landing page at `/`, enhanced PublicSetupCard. @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/26-discovery-landing-page/26-CONTEXT.md @.planning/phases/26-discovery-landing-page/26-RESEARCH.md @.planning/phases/26-discovery-landing-page/26-01-SUMMARY.md From src/client/hooks/useDiscovery.ts: ```typescript export interface DiscoverySetup { id: number; name: string; createdAt: string; itemCount: number; creatorName: string | null; } export interface DiscoveryCategory { name: string; itemCount: number; } export function useDiscoverySetups(limit?: number): UseQueryResult export function useDiscoveryItems(limit?: number): UseQueryResult export function useDiscoveryCategories(limit?: number): UseQueryResult ``` From src/client/hooks/useAuth.ts: ```typescript interface AuthState { user: { id: string; email?: string } | null; authenticated: boolean; } export function useAuth(): UseQueryResult ``` From src/client/stores/uiStore.ts: ```typescript openCatalogSearch: (mode: "collection" | "thread") => void; // Access via: const openCatalogSearch = useUIStore((s) => s.openCatalogSearch); ``` From src/client/components/GlobalItemCard.tsx: ```typescript interface GlobalItemCardProps { id: number; brand: string; model: string; category: string | null; weightGrams: number | null; priceCents: number | null; imageUrl: string | null; } export function GlobalItemCard(props: GlobalItemCardProps): JSX.Element ``` From src/client/components/PublicSetupCard.tsx (current — will be enhanced): ```typescript interface PublicSetupCardProps { setup: { id: number; name: string; createdAt: string; }; } export function PublicSetupCard({ setup }: PublicSetupCardProps): JSX.Element ``` Task 1: Enhance PublicSetupCard with itemCount and creatorName src/client/components/PublicSetupCard.tsx, src/client/routes/users/$userId.tsx - src/client/components/PublicSetupCard.tsx (current component — full content) - src/client/routes/users/$userId.tsx (existing usage of PublicSetupCard — check what shape is passed) **Modify `src/client/components/PublicSetupCard.tsx`:** Update the `PublicSetupCardProps` interface to add optional fields (per Pitfall 3 — keep optional to avoid breaking existing usages): ```typescript interface PublicSetupCardProps { setup: { id: number; name: string; createdAt: string; itemCount?: number; // NEW — optional for backward compat creatorName?: string | null; // NEW — optional }; } ``` Update the component JSX to display the new fields when present: After the existing `

` (setup name) and before/replacing the `

` date line, render: - If `setup.creatorName` is truthy: `

by {setup.creatorName}

` - If `setup.itemCount` is defined and > 0: `{setup.itemCount} items` - Keep the existing date display Layout for the bottom area of the card (below the name): ```tsx
{setup.itemCount != null && setup.itemCount > 0 && ( {setup.itemCount} {setup.itemCount === 1 ? "item" : "items"} )}

{formattedDate}

{setup.creatorName && (

by {setup.creatorName}

)} ``` Add `cursor-pointer` to the Link className (folded todo from CONTEXT.md). **Verify `src/client/routes/users/$userId.tsx`:** Read the file to confirm the existing `PublicSetupCard` usage still compiles. Since `itemCount` and `creatorName` are optional, the existing usage passing `{ id, name, createdAt }` will continue to work without changes. No modification needed to this file unless it already passes extra props. cd /home/jean-luc-makiola/Development/projects/GearBox && bun run build 2>&1 | tail -20 - src/client/components/PublicSetupCard.tsx contains `itemCount?: number` - src/client/components/PublicSetupCard.tsx contains `creatorName?: string | null` - src/client/components/PublicSetupCard.tsx contains `setup.itemCount` (conditional rendering) - src/client/components/PublicSetupCard.tsx contains `setup.creatorName` (conditional rendering) - src/client/components/PublicSetupCard.tsx contains `cursor-pointer` - `bun run build` succeeds without TypeScript errors PublicSetupCard renders item count and creator name when provided, existing usages compile without changes, cursor-pointer applied. Task 2: Rewrite index.tsx as discovery landing page src/client/routes/index.tsx - src/client/routes/index.tsx (current dashboard — will be completely rewritten) - src/client/components/GlobalItemCard.tsx (props interface for rendering items) - src/client/components/PublicSetupCard.tsx (enhanced props from Task 1) - src/client/hooks/useDiscovery.ts (hook signatures from plan 02) - src/client/hooks/useAuth.ts (useAuth hook pattern) - src/client/stores/uiStore.ts (openCatalogSearch usage pattern) - src/client/routes/__root.tsx (isDashboard detection — do NOT modify per Pitfall 2) **Completely rewrite `src/client/routes/index.tsx`** (per D-03, retire DashboardPage entirely): Remove ALL existing imports (DashboardCard, useFormatters, useSetups, useThreads, useTotals). New imports: ```typescript import { createFileRoute, Link } from "@tanstack/react-router"; import { Search } from "lucide-react"; import { GlobalItemCard } from "../components/GlobalItemCard"; import { PublicSetupCard } from "../components/PublicSetupCard"; import { useAuth } from "../hooks/useAuth"; import { useDiscoverySetups, useDiscoveryItems, useDiscoveryCategories } from "../hooks/useDiscovery"; import { useUIStore } from "../stores/uiStore"; ``` Note: Use `lucide-react` for the Search icon. The project already uses lucide-react (check existing imports). If the project uses the custom `LucideIcon` component instead, use `` from `../lib/iconData`. **Route export** (same file route, new component): ```typescript export const Route = createFileRoute("/")({ component: LandingPage, }); ``` **LandingPage function** (per D-01, D-02): ```typescript function LandingPage() { const { data: auth } = useAuth(); const isAuthenticated = !!auth?.user; const openCatalogSearch = useUIStore((s) => s.openCatalogSearch); return (
openCatalogSearch("collection")} />
); } ``` **HeroSection** (per D-01, D-04, D-10, D-11): ```typescript function HeroSection({ isAuthenticated, onSearchFocus }: { isAuthenticated: boolean; onSearchFocus: () => void }) { return (

Discover Gear

Browse what other people carry

e.key === "Enter" && onSearchFocus()} role="button" tabIndex={0} className="max-w-xl mx-auto flex items-center gap-3 px-4 py-3 bg-white rounded-xl border border-gray-200 hover:border-gray-300 cursor-pointer shadow-sm transition-all" > Search the catalog...
{isAuthenticated && (
Go to Collection
)}
); } ``` **PopularSetupsSection** (per D-02, D-05): ```typescript function PopularSetupsSection() { const { data, isLoading } = useDiscoverySetups(6); const setups = data?.items ?? []; if (!isLoading && setups.length === 0) return null; return (

Popular Setups

{isLoading ? ( ) : (
{setups.map((setup) => ( ))}
)}
); } ``` **RecentItemsSection** (per D-02, D-06): ```typescript function RecentItemsSection() { const { data, isLoading } = useDiscoveryItems(8); const items = data?.items ?? []; if (!isLoading && items.length === 0) return null; return (

Recently Added

{isLoading ? ( ) : (
{items.map((item) => ( ))}
)}
); } ``` **TrendingCategoriesSection** (per D-02, D-07): ```typescript function TrendingCategoriesSection() { const { data, isLoading } = useDiscoveryCategories(12); const categories = data ?? []; if (!isLoading && categories.length === 0) return null; return (

Trending Categories

{isLoading ? (
{Array.from({ length: 8 }).map((_, i) => (
))}
) : (
{categories.map((cat) => ( {cat.name} {cat.itemCount} ))}
)}
); } ``` **SectionSkeleton** helper (matches CatalogSearchOverlay animate-pulse pattern): ```typescript function SectionSkeleton({ count, aspect }: { count: number; aspect: string }) { return (
{Array.from({ length: count }).map((_, i) => (
{aspect !== "none" &&
}
))}
); } ``` Ensure all clickable elements have `cursor-pointer` (folded todo from CONTEXT.md). cd /home/jean-luc-makiola/Development/projects/GearBox && bun run build 2>&1 | tail -20 - src/client/routes/index.tsx does NOT contain `DashboardPage` or `DashboardCard` or `useTotals` (per D-03) - src/client/routes/index.tsx contains `function LandingPage()` - src/client/routes/index.tsx contains `function HeroSection(` - src/client/routes/index.tsx contains `openCatalogSearch("collection")` (per D-04) - src/client/routes/index.tsx contains `useDiscoverySetups` and `useDiscoveryItems` and `useDiscoveryCategories` - src/client/routes/index.tsx contains `"Go to Collection"` (per D-10) - src/client/routes/index.tsx contains `!!auth?.user` or `auth?.user` for auth check (per anti-pattern: do not use auth?.authenticated) - src/client/routes/index.tsx contains `GlobalItemCard` import - src/client/routes/index.tsx contains `PublicSetupCard` import - src/client/routes/index.tsx contains `cursor-pointer` on the search bar div - src/client/routes/index.tsx contains `animate-pulse` (loading skeletons) - `bun run build` succeeds without errors Landing page renders hero with search trigger, three feed sections with loading skeletons and empty-state handling, authenticated CTA, and all clickable elements have cursor-pointer. Build succeeds. Task 3: Visual verification of discovery landing page src/client/routes/index.tsx No code changes — this is a visual verification checkpoint. The executor should start the dev server with `bun run dev` and present the verification steps to the user. Complete discovery landing page replacing the personal dashboard at /. Features: - Hero section with "Discover Gear" heading and catalog search bar trigger - Popular Setups section with enhanced cards (item count, creator name) - Recently Added Items section with GlobalItemCard components - Trending Categories section with category pills - Conditional "Go to Collection" link for authenticated users - Loading skeletons for all sections - Empty state handling (sections hide when no data) 1. Run `bun run dev` and open http://localhost:5173/ in a browser 2. Verify the hero section shows "Discover Gear" heading with search bar 3. Click the search bar — it should open the full CatalogSearchOverlay 4. Verify sections appear below: Popular Setups, Recently Added, Trending Categories 5. If there is seed data, verify cards show correct information (item counts, creator names, images) 6. If no data exists, verify empty sections are hidden gracefully (no broken/empty grids) 7. Log in and verify "Go to Collection" link appears in hero area 8. Click "Go to Collection" — should navigate to /collection 9. Check responsive behavior: resize browser to mobile width, verify single-column layout 10. Verify all clickable elements show pointer cursor on hover cd /home/jean-luc-makiola/Development/projects/GearBox && bun run build 2>&1 | tail -5 User has visually verified the landing page renders correctly with all sections, search trigger works, auth CTA appears for logged-in users, and responsive layout is correct. Type "approved" or describe issues to fix - `bun run build` — builds without errors - Visual inspection of landing page at localhost:5173 - CatalogSearchOverlay opens from hero search bar - Authenticated user sees "Go to Collection" link - Anonymous user sees content immediately - Root URL shows discovery landing page (not personal dashboard) - Hero search bar triggers CatalogSearchOverlay on click - Three content sections render with real data or hide when empty - PublicSetupCard displays item count and creator name - Authenticated users see "Go to Collection" CTA in hero - All clickable elements have cursor-pointer - Build succeeds After completion, create `.planning/phases/26-discovery-landing-page/26-03-SUMMARY.md`