diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..e69de29 diff --git a/.gitignore b/.gitignore index ed89f8b..10734e9 100644 --- a/.gitignore +++ b/.gitignore @@ -54,6 +54,7 @@ next-env.d.ts !/.vscode !/.gitattributes !/.gitignore +!/.dockerignore !/biome.json *storybook.log diff --git a/docs/plans/2026-05-07-url-driven-section-routing-design.md b/docs/plans/2026-05-07-url-driven-section-routing-design.md deleted file mode 100644 index 56528be..0000000 --- a/docs/plans/2026-05-07-url-driven-section-routing-design.md +++ /dev/null @@ -1,86 +0,0 @@ -# URL-Driven Section Routing — Design - -**Date:** 2026-05-07 -**Status:** Approved - -## Goal - -Replace the single-page client-state accordion with multi-page URL-driven routing. Each portfolio section gets its own static URL. The sections list remains visible at all times; clicking a section heading navigates to its page. - -## Route Structure - -Delete `app/page.tsx`. Create `app/[[...slug]]/page.tsx` (optional catchall). - -| URL | Active section | -|---|---| -| `/` | `sections[0].slug` (first section, URL stays `/`) | -| `/intro` | `intro` | -| `/bio` | `bio` | -| `/skills` | `skills` | -| `/experience` | `experience` | -| `/projects` | `projects` | - -`generateStaticParams` emits one entry per section plus the root: -```ts -[{}, { slug: ['intro'] }, { slug: ['bio'] }, ...] -``` - -## Component Changes - -### `SectionAccordion` (entity) - -- Replace `onClick: () => void` prop with `href: string` -- Inactive state: render `` instead of ` - - {isOpen && ( -
- {items.map((item) => ( - setIsOpen(false)} - className="block w-full text-left brutal-border bg-ochre-clay px-4 py-3" - > -
- {item.number} - - {item.label} - -
- - ))} -
- )} - - ); -} -``` - -**Step 4: Run tests — verify they pass** - -```bash -yarn test src/widgets/Navigation/ui/MobileNav.test.tsx --run -``` - -Expected: all PASS. - -**Step 5: Commit** - -```bash -git add src/widgets/Navigation/ui/MobileNav.tsx src/widgets/Navigation/ui/MobileNav.test.tsx -git commit -m "refactor: MobileNav section items use Link instead of scrollToSection" -``` - ---- - -## Task 5: Create [[...slug]]/page.tsx and delete app/page.tsx - -Wire everything together with `generateStaticParams` for SSG. - -**Files:** -- Create: `app/[[...slug]]/page.tsx` -- Delete: `app/page.tsx` - -**Step 1: Create the route file** - -Create `app/[[...slug]]/page.tsx`: - -```tsx -import { notFound } from 'next/navigation'; -import type { SectionRecord } from '$entities/Section'; -import { getCollection } from '$shared/api'; -import type { NavItem } from '$widgets/Navigation'; -import { MobileNav, SidebarNav } from '$widgets/Navigation'; -import { SectionFactory } from '$widgets/SectionFactory'; -import { SectionsAccordion } from '$widgets/SectionsAccordion'; - -/** - * Generates static params for all section pages plus the root. - */ -export async function generateStaticParams() { - const { items: sections } = await getCollection('sections', { - sort: 'order', - }); - return [ - {}, - ...sections.map((s) => ({ slug: [s.slug] })), - ]; -} - -/** - * Portfolio page — handles all section routes via optional catchall. - * - * `/` → first section shown as active - * `/{slug}` → that section shown as active - */ -export default async function SectionPage({ - params, -}: { - params: Promise<{ slug?: string[] }>; -}) { - const { slug } = await params; - - const { items: sections } = await getCollection('sections', { - sort: 'order', - }); - - if (!sections.length) notFound(); - - const activeSlug = slug?.[0] ?? sections[0].slug; - - if (!sections.find((s) => s.slug === activeSlug)) notFound(); - - const navItems: NavItem[] = sections.map((s) => ({ - id: s.slug, - label: s.title, - number: s.number, - })); - - return ( -
- -
- - - - -
-
- ); -} -``` - -**Step 2: Delete app/page.tsx** - -```bash -rm app/page.tsx -``` - -**Step 3: TypeScript check** - -```bash -yarn tsc --noEmit -``` - -Expected: no errors. - -**Step 4: Commit** - -```bash -git add app/ -git commit -m "feat: URL-driven section routing via optional catchall with generateStaticParams" -``` - ---- - -## Task 6: Final Verification - -**Step 1: Full test suite** - -```bash -yarn test --run -``` - -Expected: all tests PASS. - -**Step 2: TypeScript check** - -```bash -yarn tsc --noEmit -``` - -Expected: no errors. - -**Step 3: Lint check** - -```bash -yarn check -``` - -Expected: no errors. If auto-fixable issues: `yarn check:fix` then re-run. - -**Step 4: Dev server smoke test** - -```bash -yarn dev -``` - -Open `http://localhost:3000` — should show first section (intro) as active, others as links. Clicking a section link should change the URL and show that section's content.