From c8ccab5cb816ef5f8bcb4bf599c6569fcdc79f2d Mon Sep 17 00:00:00 2001 From: Ajax Davis Date: Sun, 30 Nov 2025 21:03:33 +1000 Subject: [PATCH] refactor: create shared AppHeader component for consistent navigation across all pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Problem:** - Each page had different header implementations with varying navigation links - Inconsistent user experience across homepage, tools, playground, and publish pages - Duplicate header code throughout the application **Solution:** - Created `AppHeader` component (apps/web/src/components/AppHeader.tsx) with consistent navigation: - TPMJS logo linking to homepage - Tools, Playground, and Publish Tool links - GitHub icon link - Sticky header with medium size - Updated all pages to use the shared component: - apps/web/src/app/page.tsx (homepage) - apps/web/src/app/tool/tool-search/page.tsx (tools search) - apps/web/src/app/playground/page.tsx (component playground) - apps/web/src/app/publish/page.tsx (publish guide) - apps/web/src/app/tool/[...slug]/page.tsx (tool detail pages) **Benefits:** - Consistent header across all pages - Single source of truth for navigation - Easier to maintain and update navigation links - Improved user experience with predictable navigation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- apps/web/src/app/page.tsx | 41 +------------ apps/web/src/app/playground/page.tsx | 25 +------- apps/web/src/app/publish/page.tsx | 37 +----------- apps/web/src/app/tool/[...slug]/page.tsx | 69 ++-------------------- apps/web/src/app/tool/tool-search/page.tsx | 34 +---------- apps/web/src/components/AppHeader.tsx | 53 +++++++++++++++++ 6 files changed, 67 insertions(+), 192 deletions(-) create mode 100644 apps/web/src/components/AppHeader.tsx diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx index 1952622..a8b66c4 100644 --- a/apps/web/src/app/page.tsx +++ b/apps/web/src/app/page.tsx @@ -2,9 +2,8 @@ import { prisma } from '@tpmjs/db'; import { Badge } from '@tpmjs/ui/Badge/Badge'; import { Button } from '@tpmjs/ui/Button/Button'; import { Container } from '@tpmjs/ui/Container/Container'; -import { Header } from '@tpmjs/ui/Header/Header'; import Link from 'next/link'; -import { ThemeToggle } from '../components/ThemeToggle'; +import { AppHeader } from '../components/AppHeader'; import { HeroSection } from '../components/home/HeroSection'; async function getHomePageData() { @@ -93,43 +92,7 @@ export default async function HomePage(): Promise { const data = await getHomePageData(); return (
- {/* Header */} -
- TPMJS - - } - size="md" - sticky={true} - actions={ -
- - - - - - - - - - - - -
- } - /> +
{/* Hero Section - Dithered Design */} diff --git a/apps/web/src/app/playground/page.tsx b/apps/web/src/app/playground/page.tsx index 2d8860a..16054cc 100644 --- a/apps/web/src/app/playground/page.tsx +++ b/apps/web/src/app/playground/page.tsx @@ -14,7 +14,6 @@ import { Checkbox } from '@tpmjs/ui/Checkbox/Checkbox'; import { CodeBlock } from '@tpmjs/ui/CodeBlock/CodeBlock'; import { Container } from '@tpmjs/ui/Container/Container'; import { FormField } from '@tpmjs/ui/FormField/FormField'; -import { Header } from '@tpmjs/ui/Header/Header'; import { Icon } from '@tpmjs/ui/Icon/Icon'; import { Input } from '@tpmjs/ui/Input/Input'; import { Label } from '@tpmjs/ui/Label/Label'; @@ -26,9 +25,8 @@ import { Slider } from '@tpmjs/ui/Slider/Slider'; import { Switch } from '@tpmjs/ui/Switch/Switch'; import { Tabs } from '@tpmjs/ui/Tabs/Tabs'; import { Textarea } from '@tpmjs/ui/Textarea/Textarea'; -import Link from 'next/link'; import { useState } from 'react'; -import { ThemeToggle } from '../../components/ThemeToggle'; +import { AppHeader } from '~/components/AppHeader'; // Disable static generation for this page due to context provider requirements export const dynamic = 'force-dynamic'; @@ -50,26 +48,7 @@ export default function PlaygroundPage() { return (
- {/* Header */} -
- TPMJS Playground - - } - size="md" - sticky={true} - actions={ -
- - - - -
- } - /> +
diff --git a/apps/web/src/app/publish/page.tsx b/apps/web/src/app/publish/page.tsx index a2c9ca7..088fe08 100644 --- a/apps/web/src/app/publish/page.tsx +++ b/apps/web/src/app/publish/page.tsx @@ -1,9 +1,8 @@ import { Button } from '@tpmjs/ui/Button/Button'; import { CodeBlock } from '@tpmjs/ui/CodeBlock/CodeBlock'; import { Container } from '@tpmjs/ui/Container/Container'; -import { Header } from '@tpmjs/ui/Header/Header'; import Link from 'next/link'; -import { ThemeToggle } from '../../components/ThemeToggle'; +import { AppHeader } from '~/components/AppHeader'; export const metadata = { title: 'Publish a Tool | TPMJS', @@ -13,39 +12,7 @@ export const metadata = { export default function PublishPage(): React.ReactElement { return (
- {/* Header */} -
- TPMJS - - } - size="md" - sticky={true} - actions={ -
- - - - - - - - - - -
- } - /> +
diff --git a/apps/web/src/app/tool/[...slug]/page.tsx b/apps/web/src/app/tool/[...slug]/page.tsx index 37f9d5c..3a8dd7a 100644 --- a/apps/web/src/app/tool/[...slug]/page.tsx +++ b/apps/web/src/app/tool/[...slug]/page.tsx @@ -5,14 +5,13 @@ import { Button } from '@tpmjs/ui/Button/Button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@tpmjs/ui/Card/Card'; import { CodeBlock } from '@tpmjs/ui/CodeBlock/CodeBlock'; import { Container } from '@tpmjs/ui/Container/Container'; -import { Header } from '@tpmjs/ui/Header/Header'; import { Icon } from '@tpmjs/ui/Icon/Icon'; import { ProgressBar } from '@tpmjs/ui/ProgressBar/ProgressBar'; import Link from 'next/link'; import { useEffect, useState } from 'react'; -import { Markdown } from '../../../components/Markdown'; -import { ThemeToggle } from '../../../components/ThemeToggle'; -import { ToolPlayground } from '../../../components/ToolPlayground'; +import { AppHeader } from '~/components/AppHeader'; +import { Markdown } from '~/components/Markdown'; +import { ToolPlayground } from '~/components/ToolPlayground'; interface Tool { id: string; @@ -112,19 +111,7 @@ export default function ToolDetailPage({ if (loading) { return (
-
- TPMJS - - } - actions={} - sticky={true} - size="md" - /> +
Loading tool...
@@ -135,19 +122,7 @@ export default function ToolDetailPage({ if (error || !tool) { return (
-
- TPMJS - - } - actions={} - sticky={true} - size="md" - /> +

{error || 'Tool not found'}

@@ -164,39 +139,7 @@ export default function ToolDetailPage({ return (
- {/* Header */} -
- TPMJS - - } - actions={ -
- - - - {tool.npmRepository && ( - - - - )} - -
- } - sticky={true} - size="md" - /> + {/* Main content */} diff --git a/apps/web/src/app/tool/tool-search/page.tsx b/apps/web/src/app/tool/tool-search/page.tsx index f54c41b..affd1de 100644 --- a/apps/web/src/app/tool/tool-search/page.tsx +++ b/apps/web/src/app/tool/tool-search/page.tsx @@ -12,7 +12,6 @@ import { } from '@tpmjs/ui/Card/Card'; import { CodeBlock } from '@tpmjs/ui/CodeBlock/CodeBlock'; import { Container } from '@tpmjs/ui/Container/Container'; -import { Header } from '@tpmjs/ui/Header/Header'; import { Icon } from '@tpmjs/ui/Icon/Icon'; import { Input } from '@tpmjs/ui/Input/Input'; import { ProgressBar } from '@tpmjs/ui/ProgressBar/ProgressBar'; @@ -20,6 +19,7 @@ import { Select } from '@tpmjs/ui/Select/Select'; import { Tabs } from '@tpmjs/ui/Tabs/Tabs'; import Link from 'next/link'; import { useEffect, useState } from 'react'; +import { AppHeader } from '~/components/AppHeader'; interface Tool { id: string; @@ -113,37 +113,7 @@ export default function ToolSearchPage(): React.ReactElement { return (
- {/* Header */} -
- TPMJS - - Beta - -
- } - actions={ -
- - - - - -
- } - sticky={true} - size="md" - /> + {/* Main content */} diff --git a/apps/web/src/components/AppHeader.tsx b/apps/web/src/components/AppHeader.tsx new file mode 100644 index 0000000..735eb5a --- /dev/null +++ b/apps/web/src/components/AppHeader.tsx @@ -0,0 +1,53 @@ +'use client'; + +import { Button } from '@tpmjs/ui/Button/Button'; +import { Header } from '@tpmjs/ui/Header/Header'; +import { Icon } from '@tpmjs/ui/Icon/Icon'; +import Link from 'next/link'; + +/** + * Shared application header used across all pages + */ +export function AppHeader(): React.ReactElement { + return ( +
+ TPMJS + + } + size="md" + sticky={true} + actions={ +
+ + + + + + + + + + + + +
+ } + /> + ); +}