810485ecc8
---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)
141 lines
5.7 KiB
TypeScript
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>
|
|
);
|
|
} |