Files
nextcraft/apps/web/components/learner/worked-example-tabs.tsx
T
CIAgent 810485ecc8 docs(milestone): complete v0.1-nextcraft-ui-prototype
---ci---
phase: 7
milestone: v0.1
status: complete
requirements:
  covered: [REQ-001, REQ-002, REQ-003, REQ-004, REQ-005, REQ-006, REQ-007, REQ-008, REQ-009, REQ-010, REQ-011, REQ-012, REQ-013, REQ-014, REQ-015, REQ-016, REQ-017, REQ-018, REQ-019, REQ-020, REQ-021, REQ-022, REQ-023, REQ-024, REQ-025, REQ-026, REQ-027, REQ-028]
  partial: []
---/ci---

Milestone v0.1 (nextcraft-ui-prototype) complete.

Summary:
- 7 phases (P0 pre-execution + P1-P6 execution + P7 final review)
- 28 requirements covered (all complete)
- 4 surfaces: Learner (7 pages), Marketplace (5), Employer Dashboard (4), Admin (4)
- 19 routes, 104 TypeScript files
- Shared component library (5 primitives + design tokens)
- Mock data: 5 competency stacks (70 competencies), 20 jobs, 15 candidates, 10 employers
- Tech: Next.js 15, Tailwind CSS v4, lucide-react, recharts, @xyflow/react, Inter font
- Storybook with 6 stories
- Dark mode, breadcrumbs, role switcher, responsive design
- Build passes, typecheck passes, Storybook build passes

Phases:
  P0 pre-execution     → v0.0.1
  P1 project-scaffolding → v0.0.2
  P2 learner-surface   → v0.0.3
  P3 marketplace-surface → v0.0.4
  P4 employer-dashboard → v0.0.5
  P5 admin-surface     → v0.0.6
  P6 polish-integration → v0.0.7
  P7 final-review-ship → v0.1.0 (milestone release)
2026-09-10 22:47:56 +00:00

141 lines
5.7 KiB
TypeScript

'use client';
import { useState } from 'react';
import { Code, PencilRuler, Cpu } from 'lucide-react';
type Tab = 'code' | 'design' | 'simulation';
const CODE_SAMPLE = `import { tool } from '@langchain/core/tools';
import { z } from 'zod';
import { ChatOpenAI } from '@langchain/openai';
// Define a function-calling tool with a typed schema
const weatherSchema = z.object({
city: z.string().describe('City to fetch weather for'),
units: z.enum(['celsius', 'fahrenheit']).default('celsius'),
});
const getWeather = tool(
async ({ city, units }) => {
const res = await fetch(\`/api/weather?city=\${city}&units=\${units}\`);
return res.json();
},
{
name: 'get_weather',
description: 'Fetch the current weather for a city',
schema: weatherSchema,
},
);
// Bind tools to a chat model and invoke with a tool call
const model = new ChatOpenAI({ model: 'gpt-4o-mini' });
const modelWithTools = model.bindTools([getWeather]);
const response = await modelWithTools.invoke(
'What is the weather in Tokyo right now?',
);
console.log(response.tool_calls);`;
export function WorkedExampleTabs() {
const [tab, setTab] = useState<Tab>('code');
return (
<div className="flex h-full flex-col">
{/* Tab bar */}
<div className="flex items-center gap-1 border-b border-slate-200 dark:border-slate-800">
{[
{ id: 'code' as Tab, label: 'Code', icon: Code },
{ id: 'design' as Tab, label: 'Design', icon: PencilRuler },
{ id: 'simulation' as Tab, label: 'Simulation', icon: Cpu },
].map((t) => {
const Icon = t.icon;
const active = tab === t.id;
return (
<button
key={t.id}
onClick={() => setTab(t.id)}
className={`flex items-center gap-1.5 border-b-2 px-3 py-2 text-sm font-medium transition-colors ${
active
? 'border-primary-600 text-primary-700 dark:text-primary-300'
: 'border-transparent text-slate-500 hover:text-slate-800 dark:hover:text-slate-200'
}`}
>
<Icon className="h-4 w-4" />
{t.label}
</button>
);
})}
</div>
{/* Tab content */}
<div className="flex-1 overflow-auto">
{tab === 'code' && (
<pre className="m-4 overflow-auto rounded-lg bg-slate-900 p-4 text-xs leading-relaxed text-slate-100">
<code>{CODE_SAMPLE}</code>
</pre>
)}
{tab === 'design' && (
<div className="flex h-full items-center justify-center p-6">
<div className="w-full max-w-md rounded-lg border border-dashed border-slate-300 p-6 text-center dark:border-slate-700">
<PencilRuler className="mx-auto mb-3 h-8 w-8 text-slate-400" />
<p className="text-sm font-medium text-slate-700 dark:text-slate-200">
Design diagram
</p>
<div className="mt-4 flex items-center justify-between gap-2 text-xs text-slate-500 dark:text-slate-400">
<span className="rounded-md border border-slate-200 bg-slate-50 px-2 py-1 dark:border-slate-700 dark:bg-slate-800">
User
</span>
<span className="text-slate-300"></span>
<span className="rounded-md border border-primary-200 bg-primary-50 px-2 py-1 text-primary-700 dark:border-primary-800 dark:bg-primary-900/30 dark:text-primary-300">
Agent
</span>
<span className="text-slate-300"></span>
<span className="rounded-md border border-slate-200 bg-slate-50 px-2 py-1 dark:border-slate-700 dark:bg-slate-800">
Tools
</span>
</div>
<p className="mt-4 text-xs text-slate-400">
Placeholder diagram replace with your visualizer in a later phase.
</p>
</div>
</div>
)}
{tab === 'simulation' && (
<div className="flex h-full flex-col gap-3 p-6">
<div className="rounded-lg border border-slate-200 bg-slate-50 p-4 dark:border-slate-700 dark:bg-slate-800/50">
<div className="flex items-center justify-between">
<span className="text-sm font-medium text-slate-700 dark:text-slate-200">
Live simulation
</span>
<span className="flex h-2 w-2 items-center justify-center">
<span className="h-2 w-2 animate-ping rounded-full bg-emerald-400" />
<span className="h-2 w-2 rounded-full bg-emerald-500" />
</span>
</div>
<div className="mt-3 grid grid-cols-3 gap-3 text-center">
<div className="rounded-md bg-white p-2 dark:bg-slate-900">
<p className="text-xs text-slate-500">Tokens</p>
<p className="text-lg font-semibold text-slate-800 dark:text-slate-100">1,284</p>
</div>
<div className="rounded-md bg-white p-2 dark:bg-slate-900">
<p className="text-xs text-slate-500">Latency</p>
<p className="text-lg font-semibold text-slate-800 dark:text-slate-100">312ms</p>
</div>
<div className="rounded-md bg-white p-2 dark:bg-slate-900">
<p className="text-xs text-slate-500">Cost</p>
<p className="text-lg font-semibold text-slate-800 dark:text-slate-100">$0.04</p>
</div>
</div>
</div>
<p className="text-center text-xs text-slate-400">
Simulation placeholder interactive runs land in a later phase.
</p>
</div>
)}
</div>
</div>
);
}