--- phase: 04-marketplace-portal-navigation plan: 03 type: execute wave: 2 depends_on: ["04-01"] files_modified: - apps/web/src/components/layout/sidebar.tsx - apps/web/src/components/layout/sidebar-search.tsx - apps/web/src/components/layout/sidebar.test.tsx - apps/web/src/components/layout/sidebar-search.test.tsx autonomous: true requirements: [MRKT-03, PRTAL-02, PRTAL-03, PRTAL-05] must_haves: truths: - "Sidebar shows only the tenant's activated modules, grouped by category, with an individual clickable link per module (PRTAL-02, MRKT-03)" - "Clicking a module link in the sidebar navigates client-side to /modules/{category}/{slug} and the module opens in the main content area (PRTAL-03)" - "The currently open page/module is highlighted active in the sidebar via usePathname (PRTAL-03)" - "A sidebar search field filters visible modules and categories by name in real time; non-matching categories hide (PRTAL-05)" - "After a module is activated/deactivated in the marketplace, the sidebar refreshes its active-module list without a full page reload" artifacts: - path: "apps/web/src/components/layout/sidebar-search.tsx" provides: "Sidebar search input filtering modules + categories" min_lines: 20 - path: "apps/web/src/components/layout/sidebar.tsx" provides: "Sidebar using Next.js Link + usePathname active state + per-module links + search + marketplace-store refresh subscription" contains: "usePathname" key_links: - from: "apps/web/src/components/layout/sidebar.tsx" to: "/modules/active" via: "fetch on mount and on marketplace-store sidebarRefreshKey change" pattern: "modules/active" - from: "apps/web/src/components/layout/sidebar.tsx" to: "marketplace-store sidebarRefreshKey" via: "useEffect dependency triggers re-fetch" pattern: "sidebarRefreshKey" - from: "apps/web/src/components/layout/sidebar.tsx" to: "next/navigation usePathname" via: "active-state comparison against link href" pattern: "usePathname" --- Enhance the existing sidebar so it becomes the live navigation surface for activated modules: migrate all links from raw `` to Next.js ``, add `usePathname`-based active highlighting, render an individual link per activated module under each category, add a sidebar search field, and subscribe to the marketplace-store refresh signal so newly activated/deactivated modules appear without a page reload. Purpose: Delivers PRTAL-02 (sidebar shows categories + activated modules), PRTAL-03 (selected module opens in main area + active highlight), PRTAL-05 (sidebar search/filter), and MRKT-03 (only activated modules appear). Output: Modified sidebar.tsx, new sidebar-search.tsx, and unit tests. ## Phase Goal **As a** Tessera user, **I want to** see my tenant's activated modules in the sidebar, search them, and click one to open it, **so that** I can navigate directly to the tools I use without leaving the portal. @$HOME/.claude/gsd-core/workflows/execute-plan.md @$HOME/.claude/gsd-core/templates/summary.md @.planning/PROJECT.md @.planning/phases/04-marketplace-portal-navigation/04-CONTEXT.md @.planning/phases/04-marketplace-portal-navigation/04-RESEARCH.md @.planning/phases/04-marketplace-portal-navigation/04-PATTERNS.md @.planning/phases/04-marketplace-portal-navigation/04-UI-SPEC.md @.planning/phases/04-marketplace-portal-navigation/04-01-SUMMARY.md Task 1: Build SidebarSearch and migrate sidebar to Next.js Link + usePathname active state - apps/web/src/components/layout/sidebar.tsx (the file being modified — current raw `` links, hardcoded Dashboard active state, categories accordion with per-module `` links at lines 144-169, admin section) - apps/web/src/app/(portal)/admin/tenants/page.tsx (input styling for the search field) - .planning/phases/04-marketplace-portal-navigation/04-PATTERNS.md (sidebar.tsx MODIFY section: Link/usePathname insertion points, active CSS classes; sidebar-search no-analog guidance) - .planning/phases/04-marketplace-portal-navigation/04-UI-SPEC.md (Sidebar Enhancement: SidebarSearch 36px height, placement between main nav and categories, collapsed-mode rule, active classes) - .planning/phases/04-marketplace-portal-navigation/04-RESEARCH.md (Pitfall 1 raw `` reloads, Pitfall 3 active state, Pitfall 5 accordion state reset) - apps/web/src/messages/de.json (sidebar.search + sidebar.noResults added in Plan 01) - Test 1: Sidebar renders the Dashboard and Marketplace links as Next.js Link elements (anchor with client-side href, no full reload semantics) — assert hrefs "/" and "/marketplace" - Test 2: Given pathname "/marketplace", the Marketplace link has the active class (`bg-sidebar-accent`) and Dashboard does not - Test 3: SidebarSearch renders an input with the `sidebar.search` placeholder and calls onChange with typed value - Test 4: With a search term that matches no module, the sidebar renders the `sidebar.noResults` text Create `apps/web/src/components/layout/sidebar-search.tsx`: `'use client'` controlled ``, props `{ value: string; onChange: (v: string) => void }`, placeholder from `useTranslations('sidebar')('search')`, height 36px, `border border-border rounded-md`, focus `focus:ring-2 focus:ring-ring focus:border-transparent`, `px-3` within the sidebar nav padding. Include an accessible label (`aria-label` = placeholder). No debounce needed (client-side, tiny dataset) — filter immediately. Modify `apps/web/src/components/layout/sidebar.tsx`: 1. Add imports `import Link from 'next/link'` and `import { usePathname } from 'next/navigation'`. Add `const pathname = usePathname()` and an `isActive(href)` helper: for "/" return `pathname === '/'`, otherwise `pathname.startsWith(href)`. 2. Replace EVERY `` (Dashboard, Marketplace, category links, per-module links, all admin links) with ``. Remove the hardcoded Dashboard active class; instead apply active classes conditionally via `isActive(href)`: active = `bg-sidebar-accent text-sidebar-accent-foreground font-medium`, inactive = `text-sidebar-foreground hover:bg-muted` (for top-level) / muted variants for nested module links. Per-module links use `/modules/{category}/{slug}` and get active highlight when `isActive` matches that path. 3. Insert `` between the main-nav `