← back to ClawCoder
src/app/page.tsx
213 lines
'use client'
import { useEffect, useState } from 'react'
import Link from 'next/link'
import { useRouter } from 'next/navigation'
interface ProfileSummary {
slug: string
ageGroup: string
skillLevel: string
displayName: string
persona: string
ui: { fontSize: number }
skillCount: number
mcpCount: number
subagentCount: number
}
const ageGroupLabels: Record<string, string> = {
teen: 'Teen (13-17)',
young_adult: 'Young Adult (18-30)',
professional: 'Professional (31-50)',
pre_senior: 'Pre-Senior (51-65)',
senior: 'Senior (65+)',
}
const ageGroupOrder = ['teen', 'young_adult', 'professional', 'pre_senior', 'senior']
const skillLevelOrder = ['beginner', 'intermediate', 'expert']
const skillLevelLabels: Record<string, string> = {
beginner: 'Beginner',
intermediate: 'Intermediate',
expert: 'Expert',
}
export default function HomePage() {
const [profiles, setProfiles] = useState<ProfileSummary[]>([])
const [loading, setLoading] = useState(true)
const router = useRouter()
useEffect(() => {
fetch('/api/profiles')
.then((r) => r.json())
.then((data) => {
setProfiles(data)
setLoading(false)
})
.catch(() => setLoading(false))
}, [])
function getProfile(age: string, skill: string) {
return profiles.find(
(p) => p.ageGroup === age && p.skillLevel === skill
)
}
return (
<div className="flex flex-col">
{/* Hero Section */}
<section className="flex flex-col items-center justify-center px-4 py-20 text-center sm:py-28">
<h1 className="max-w-2xl text-4xl font-bold tracking-tight text-slate-900 sm:text-5xl">
Build your perfect CLAUDE.md
</h1>
<p className="mt-6 max-w-xl text-lg leading-8 text-slate-600">
15 profiles tailored to your age and experience level. Or upload
your existing config for instant analysis.
</p>
<div className="mt-10 flex flex-col gap-4 sm:flex-row">
<Link
href="/onboarding"
className="inline-flex min-h-[48px] items-center justify-center rounded-xl bg-blue-600 px-8 py-3 text-base font-medium text-white transition-colors hover:bg-blue-700 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500"
>
Start Guided Setup
</Link>
<a
href="#profiles"
className="inline-flex min-h-[48px] items-center justify-center rounded-xl border border-slate-300 px-8 py-3 text-base font-medium text-slate-700 transition-colors hover:bg-slate-50 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500"
>
Choose a Profile
</a>
<Link
href="/analyzer"
className="inline-flex min-h-[48px] items-center justify-center rounded-xl border border-slate-300 px-8 py-3 text-base font-medium text-slate-700 transition-colors hover:bg-slate-50 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500"
>
Analyze My CLAUDE.md
</Link>
</div>
</section>
{/* Profile Grid */}
<section id="profiles" className="mx-auto w-full max-w-7xl px-4 pb-20 sm:px-6">
<h2 className="mb-8 text-center text-2xl font-bold text-slate-900">
Choose Your Profile
</h2>
{loading ? (
<div className="flex items-center justify-center py-20">
<div className="h-8 w-8 animate-spin rounded-full border-2 border-slate-300 border-t-blue-600" />
</div>
) : (
<>
{/* Desktop Grid */}
<div className="hidden lg:block">
{/* Skill level headers */}
<div className="mb-4 grid grid-cols-[180px_1fr_1fr_1fr] gap-4">
<div />
{skillLevelOrder.map((skill) => (
<div
key={skill}
className="text-center text-sm font-semibold uppercase tracking-wider text-slate-500"
>
{skillLevelLabels[skill]}
</div>
))}
</div>
{/* Rows by age group */}
{ageGroupOrder.map((age) => (
<div
key={age}
className="mb-4 grid grid-cols-[180px_1fr_1fr_1fr] gap-4"
>
<div className="flex items-center text-sm font-semibold text-slate-700">
{ageGroupLabels[age]}
</div>
{skillLevelOrder.map((skill) => {
const profile = getProfile(age, skill)
if (!profile) {
return (
<div
key={skill}
className="rounded-xl border border-dashed border-slate-200 p-4 text-center text-sm text-slate-400"
>
Coming soon
</div>
)
}
return (
<button
key={skill}
type="button"
onClick={() =>
router.push(`/builder?profile=${profile.slug}`)
}
className="group cursor-pointer rounded-xl border border-slate-200 bg-white p-5 text-left shadow-sm transition-all hover:-translate-y-0.5 hover:border-blue-300 hover:shadow-md focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500 motion-reduce:transform-none"
>
<h3 className="text-sm font-semibold text-slate-900 group-hover:text-blue-600">
{profile.displayName}
</h3>
<p className="mt-1 line-clamp-2 text-xs text-slate-500">
{profile.persona}
</p>
<div className="mt-3 flex gap-3 text-xs text-slate-400">
<span>Font {profile.ui.fontSize}px</span>
<span>{profile.skillCount} skills</span>
<span>{profile.mcpCount} MCP</span>
</div>
</button>
)
})}
</div>
))}
</div>
{/* Mobile List */}
<div className="space-y-6 lg:hidden">
{ageGroupOrder.map((age) => (
<div key={age}>
<h3 className="mb-3 text-sm font-semibold uppercase tracking-wider text-slate-500">
{ageGroupLabels[age]}
</h3>
<div className="space-y-3">
{skillLevelOrder.map((skill) => {
const profile = getProfile(age, skill)
if (!profile) return null
return (
<button
key={skill}
type="button"
onClick={() =>
router.push(`/builder?profile=${profile.slug}`)
}
className="w-full cursor-pointer rounded-xl border border-slate-200 bg-white p-4 text-left shadow-sm transition-all hover:border-blue-300 hover:shadow-md focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500"
>
<div className="flex items-center justify-between">
<h4 className="text-sm font-semibold text-slate-900">
{profile.displayName}
</h4>
<span className="rounded-full bg-slate-100 px-2 py-0.5 text-xs text-slate-500">
{skillLevelLabels[skill]}
</span>
</div>
<p className="mt-1 line-clamp-2 text-xs text-slate-500">
{profile.persona}
</p>
<div className="mt-2 flex gap-3 text-xs text-slate-400">
<span>Font {profile.ui.fontSize}px</span>
<span>{profile.skillCount} skills</span>
<span>{profile.mcpCount} MCP</span>
</div>
</button>
)
})}
</div>
</div>
))}
</div>
</>
)}
</section>
</div>
)
}