refactor: create shared AppHeader component for consistent navigation across all pages
**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 <noreply@anthropic.com>
This commit is contained in:
parent
0b4a314bd1
commit
c8ccab5cb8
6 changed files with 67 additions and 192 deletions
|
|
@ -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<React.ReactElement> {
|
|||
const data = await getHomePageData();
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col">
|
||||
{/* Header */}
|
||||
<Header
|
||||
title={
|
||||
<Link
|
||||
href="/"
|
||||
className="text-foreground hover:text-foreground text-xl md:text-2xl font-bold uppercase tracking-tight"
|
||||
>
|
||||
TPMJS
|
||||
</Link>
|
||||
}
|
||||
size="md"
|
||||
sticky={true}
|
||||
actions={
|
||||
<div className="flex items-center gap-4">
|
||||
<Link href="/tool/tool-search">
|
||||
<Button variant="ghost" size="sm" className="text-foreground hover:text-foreground">
|
||||
Tools
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href="/playground">
|
||||
<Button variant="ghost" size="sm" className="text-foreground hover:text-foreground">
|
||||
Playground
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href="/publish">
|
||||
<Button variant="ghost" size="sm" className="text-foreground hover:text-foreground">
|
||||
Publish
|
||||
</Button>
|
||||
</Link>
|
||||
<Button variant="secondary" size="sm">
|
||||
Sign In
|
||||
</Button>
|
||||
<Button size="sm">Sign Up</Button>
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
<AppHeader />
|
||||
|
||||
<main className="flex-1">
|
||||
{/* Hero Section - Dithered Design */}
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<div className="min-h-screen flex flex-col dotted-grid-background">
|
||||
{/* Header */}
|
||||
<Header
|
||||
title={
|
||||
<Link href="/" className="text-foreground hover:text-foreground">
|
||||
TPMJS Playground
|
||||
</Link>
|
||||
}
|
||||
size="md"
|
||||
sticky={true}
|
||||
actions={
|
||||
<div className="flex items-center gap-4">
|
||||
<Link href="/">
|
||||
<Button variant="ghost" size="sm" className="text-foreground hover:text-foreground">
|
||||
Home
|
||||
</Button>
|
||||
</Link>
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
<AppHeader />
|
||||
|
||||
<main className="flex-1 py-12 relative">
|
||||
<div className="absolute inset-0 bg-background/95 -z-10" />
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<div className="min-h-screen flex flex-col bg-background">
|
||||
{/* Header */}
|
||||
<Header
|
||||
title={
|
||||
<Link
|
||||
href="/"
|
||||
className="text-foreground hover:text-foreground text-xl md:text-2xl font-bold uppercase tracking-tight"
|
||||
>
|
||||
TPMJS
|
||||
</Link>
|
||||
}
|
||||
size="md"
|
||||
sticky={true}
|
||||
actions={
|
||||
<div className="flex items-center gap-4">
|
||||
<Link href="/tool/tool-search">
|
||||
<Button variant="ghost" size="sm" className="text-foreground hover:text-foreground">
|
||||
Tools
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href="/playground">
|
||||
<Button variant="ghost" size="sm" className="text-foreground hover:text-foreground">
|
||||
Playground
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href="/publish">
|
||||
<Button variant="ghost" size="sm" className="text-foreground hover:text-foreground">
|
||||
Publish
|
||||
</Button>
|
||||
</Link>
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
<AppHeader />
|
||||
|
||||
<main className="flex-1 py-16">
|
||||
<Container size="lg" padding="lg">
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<div className="min-h-screen bg-background">
|
||||
<Header
|
||||
title={
|
||||
<Link
|
||||
href="/"
|
||||
className="text-foreground hover:text-foreground text-xl md:text-2xl font-bold uppercase tracking-tight"
|
||||
>
|
||||
TPMJS
|
||||
</Link>
|
||||
}
|
||||
actions={<ThemeToggle />}
|
||||
sticky={true}
|
||||
size="md"
|
||||
/>
|
||||
<AppHeader />
|
||||
<Container size="xl" padding="md" className="py-12">
|
||||
<div className="text-center text-foreground-secondary">Loading tool...</div>
|
||||
</Container>
|
||||
|
|
@ -135,19 +122,7 @@ export default function ToolDetailPage({
|
|||
if (error || !tool) {
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<Header
|
||||
title={
|
||||
<Link
|
||||
href="/"
|
||||
className="text-foreground hover:text-foreground text-xl md:text-2xl font-bold uppercase tracking-tight"
|
||||
>
|
||||
TPMJS
|
||||
</Link>
|
||||
}
|
||||
actions={<ThemeToggle />}
|
||||
sticky={true}
|
||||
size="md"
|
||||
/>
|
||||
<AppHeader />
|
||||
<Container size="xl" padding="md" className="py-12">
|
||||
<div className="text-center">
|
||||
<p className="text-red-500 text-lg mb-4">{error || 'Tool not found'}</p>
|
||||
|
|
@ -164,39 +139,7 @@ export default function ToolDetailPage({
|
|||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
{/* Header */}
|
||||
<Header
|
||||
title={
|
||||
<Link
|
||||
href="/"
|
||||
className="text-foreground hover:text-foreground text-xl md:text-2xl font-bold uppercase tracking-tight"
|
||||
>
|
||||
TPMJS
|
||||
</Link>
|
||||
}
|
||||
actions={
|
||||
<div className="flex items-center gap-3">
|
||||
<Link href="/tool/tool-search">
|
||||
<Button variant="ghost" size="sm" className="text-foreground hover:text-foreground">
|
||||
Browse Tools
|
||||
</Button>
|
||||
</Link>
|
||||
{tool.npmRepository && (
|
||||
<a
|
||||
href={tool.npmRepository.url.replace('git+', '').replace('.git', '')}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-foreground-secondary hover:text-foreground transition-colors"
|
||||
>
|
||||
<Icon icon="github" size="md" />
|
||||
</a>
|
||||
)}
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
}
|
||||
sticky={true}
|
||||
size="md"
|
||||
/>
|
||||
<AppHeader />
|
||||
|
||||
{/* Main content */}
|
||||
<Container size="xl" padding="md" className="py-8">
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<div className="min-h-screen bg-background">
|
||||
{/* Header */}
|
||||
<Header
|
||||
title={
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-2xl font-bold">TPMJS</span>
|
||||
<Badge variant="outline" size="sm">
|
||||
Beta
|
||||
</Badge>
|
||||
</div>
|
||||
}
|
||||
actions={
|
||||
<div className="flex items-center gap-3">
|
||||
<Button variant="ghost" size="sm">
|
||||
Docs
|
||||
</Button>
|
||||
<a
|
||||
href="https://github.com/tpmjs/tpmjs"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-foreground-secondary hover:text-foreground transition-colors"
|
||||
>
|
||||
<Icon icon="github" size="md" />
|
||||
</a>
|
||||
<Button variant="default" size="sm">
|
||||
Publish Tool
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
sticky={true}
|
||||
size="md"
|
||||
/>
|
||||
<AppHeader />
|
||||
|
||||
{/* Main content */}
|
||||
<Container size="xl" padding="md" className="py-8">
|
||||
|
|
|
|||
53
apps/web/src/components/AppHeader.tsx
Normal file
53
apps/web/src/components/AppHeader.tsx
Normal file
|
|
@ -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 (
|
||||
<Header
|
||||
title={
|
||||
<Link
|
||||
href="/"
|
||||
className="text-foreground hover:text-foreground text-xl md:text-2xl font-bold uppercase tracking-tight"
|
||||
>
|
||||
TPMJS
|
||||
</Link>
|
||||
}
|
||||
size="md"
|
||||
sticky={true}
|
||||
actions={
|
||||
<div className="flex items-center gap-4">
|
||||
<Link href="/tool/tool-search">
|
||||
<Button variant="ghost" size="sm" className="text-foreground hover:text-foreground">
|
||||
Tools
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href="/playground">
|
||||
<Button variant="ghost" size="sm" className="text-foreground hover:text-foreground">
|
||||
Playground
|
||||
</Button>
|
||||
</Link>
|
||||
<a
|
||||
href="https://github.com/tpmjs/tpmjs"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
<Icon icon="github" size="md" />
|
||||
</a>
|
||||
<Link href="/publish">
|
||||
<Button variant="default" size="sm">
|
||||
Publish Tool
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue