Dashboard Organization
Dashboard Organization
The dashboard/ directory contains a single Next.js 16 application that hosts multiple skill dashboards. Each skill’s UI is isolated using route groups and component subfolders.
Directory Structure
dashboard/src/
├── app/
│ ├── layout.tsx ← minimal root (fonts, dark mode)
│ ├── globals.css
│ │
│ ├── (jobhunt)/ ← route group (not in URL)
│ │ ├── layout.tsx ← jobhunt metadata
│ │ ├── page.tsx ← URL: /
│ │ ├── position/[id]/page.tsx ← URL: /position/:id
│ │ └── collection/[id]/page.tsx ← URL: /collection/:id
│ │
│ └── api/
│ ├── jobhunt/ ← jobhunt API routes
│ ├── pipeline/route.ts
│ ├── position/[id]/route.ts
│ └── ...
│
├── components/
│ ├── ui/ ← shared shadcn/ui primitives
│ ├── jobhunt/ ← jobhunt-specific components
│ ├── pipeline-board.tsx
│ ├── skills-matrix.tsx
│ ├── stats-overview.tsx
│ ├── learning-plan.tsx
│ └── candidates-table.tsx
│
└── lib/
├── jobhunt.ts ← jobhunt CLI wrapper
└── utils.ts ← shared utilities
Key Concepts
Route Groups
Folders wrapped in parentheses like (jobhunt) are Next.js route groups. They organize files without affecting the URL. This lets each skill dashboard have its own layout.tsx with separate metadata (browser tab title, description) while sharing the same root layout for fonts and global styles.
| Route Group | Layout Metadata | URL Prefix |
|---|---|---|
(jobhunt) |
“Job Hunt Dashboard | Skillful-Alhazen” | / (root) |
Three-Layer Pattern
Each dashboard follows the same three-layer pattern:
Browser (React pages)
→ API routes (thin try/catch wrappers)
→ lib/<skill>.ts (calls Python CLI via uv run)
→ TypeDB
lib/<skill>.ts— CLI wrapper that callsuv run python .claude/skills/<skill>/<skill>.pyviaexecFile, parses JSON outputapi/<skill>/— One route per CLI command, thin try/catch returningNextResponse.json()- Pages — React client components using
useState/useEffectto fetch from API routes
Component Organization
Components are namespaced by skill under components/:
components/ui/— Shared shadcn/ui primitives (Button, Card, Badge, Table, etc.)components/jobhunt/— Jobhunt-specific components
If a component is reusable across skills, it belongs in a shared location. Currently all domain components are skill-specific.
Adding a Dashboard for a New Skill
- Create the CLI wrapper —
src/lib/<skill>.tsimport { execFile } from 'child_process'; import { promisify } from 'util'; import path from 'path'; const execFileAsync = promisify(execFile); const PROJECT_ROOT = process.env.PROJECT_ROOT || path.resolve(__dirname, '../../..'); const SCRIPT = path.join(PROJECT_ROOT, '.claude/skills/<skill>/<skill>.py'); async function runSkill(args: string[]): Promise<unknown> { const { stdout } = await execFileAsync( 'uv', ['run', 'python', SCRIPT, ...args], { cwd: PROJECT_ROOT, maxBuffer: 10 * 1024 * 1024, env: { ...process.env, TYPEDB_DATABASE: 'alhazen_notebook' }, } ); return JSON.parse(stdout); } export async function listItems() { return runSkill(['list-items']); } - Create API routes —
src/app/api/<skill>/import { NextResponse } from 'next/server'; import { listItems } from '@/lib/<skill>'; export async function GET() { try { const data = await listItems(); return NextResponse.json(data); } catch (error) { console.error('Error:', error); return NextResponse.json({ error: 'Failed' }, { status: 500 }); } } - Create route group and pages —
src/app/(<skill>)/layout.tsx— Metadata for the skill dashboard<skill>/page.tsx— Home page at URL/<skill><skill>/<entity>/[id]/page.tsx— Detail pages
-
Create components —
src/components/<skill>/ - Verify —
./node_modules/.bin/next build
Environment Notes
- Always pass
TYPEDB_DATABASE: 'alhazen_notebook'inexecFileenv (the default in some scripts differs) - Build with
./node_modules/.bin/next build(notnpm run buildwhich may miss PATH) - Tech stack: Next.js 16, shadcn/ui, Tailwind CSS 4, React 19
- All pages use
'use client'directive (client-side rendering withuseState/useEffect)
Related
- Skill Architecture — The three-component pattern (Schema + Skill + Dashboard)
- Skills: Jobhunt — Jobhunt skill documentation