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)
114 lines
4.5 KiB
TypeScript
114 lines
4.5 KiB
TypeScript
'use client';
|
|
|
|
import { Mic, Send } from 'lucide-react';
|
|
|
|
const TRANSCRIPT = [
|
|
{
|
|
role: 'examiner' as const,
|
|
question:
|
|
"Walk us through your design choices for multi-agent communication in this artifact. Why a blackboard architecture over direct message passing?",
|
|
},
|
|
{
|
|
role: 'learner' as const,
|
|
answer:
|
|
"I chose a shared blackboard because the agents publish partial results that others consume asynchronously — direct messaging would have tightly coupled them and made re-planning harder. The blackboard also gives me a clean audit trail for each step.",
|
|
},
|
|
{
|
|
role: 'examiner' as const,
|
|
question:
|
|
"What failure mode did you observe under load, and how did you mitigate it?",
|
|
},
|
|
{
|
|
role: 'learner' as const,
|
|
answer:
|
|
"At 50 concurrent requests the planner became a bottleneck because every agent waited on a fresh plan. I added a plan cache keyed by intent signature and moved re-planning to a debounce — throughput improved 3x with no measurable quality regression.",
|
|
},
|
|
];
|
|
|
|
export function OralDefenseInterface() {
|
|
return (
|
|
<div className="flex flex-col gap-6">
|
|
{/* Mic + waveform */}
|
|
<div className="flex flex-col items-center gap-4">
|
|
<button
|
|
type="button"
|
|
aria-label="Start oral defense"
|
|
className="group relative flex h-20 w-20 items-center justify-center rounded-full bg-primary-600 text-white transition-transform hover:scale-105 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-2"
|
|
>
|
|
<span className="absolute inset-0 animate-ping rounded-full bg-primary-500/30 group-hover:opacity-100 opacity-0 transition-opacity" />
|
|
<Mic className="h-8 w-8" />
|
|
</button>
|
|
<p className="text-sm font-medium text-slate-700 dark:text-slate-200">
|
|
Start Oral Defense
|
|
</p>
|
|
|
|
{/* Waveform mockup */}
|
|
<div className="flex items-center gap-1" aria-hidden>
|
|
{[12, 24, 16, 32, 20, 40, 28, 18, 36, 22, 14, 30, 20, 12, 26, 34, 18, 10, 28, 16].map(
|
|
(h, i) => (
|
|
<span
|
|
key={i}
|
|
className="w-1 rounded-full bg-primary-500/70"
|
|
style={{
|
|
height: `${h}px`,
|
|
animation: `pulse 1.2s ease-in-out ${i * 0.06}s infinite`,
|
|
}}
|
|
/>
|
|
),
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Transcript */}
|
|
<div className="rounded-lg border border-slate-200 bg-slate-50 p-4 dark:border-slate-800 dark:bg-slate-900/50">
|
|
<h4 className="mb-3 text-sm font-semibold text-slate-700 dark:text-slate-200">
|
|
Defense transcript
|
|
</h4>
|
|
<div className="space-y-4">
|
|
{TRANSCRIPT.map((turn, i) =>
|
|
turn.role === 'examiner' ? (
|
|
<div key={i} className="flex gap-3">
|
|
<span className="flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-primary-100 text-primary-700 text-xs dark:bg-primary-900/40 dark:text-primary-300">
|
|
AI
|
|
</span>
|
|
<div>
|
|
<p className="text-xs font-medium uppercase tracking-wide text-slate-400">
|
|
Examiner
|
|
</p>
|
|
<p className="mt-0.5 text-sm text-slate-700 dark:text-slate-200">
|
|
{turn.question}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<div key={i} className="flex flex-row-reverse gap-3">
|
|
<span className="flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-slate-200 text-slate-700 text-xs dark:bg-slate-700 dark:text-slate-200">
|
|
AR
|
|
</span>
|
|
<div className="max-w-[80%]">
|
|
<p className="text-right text-xs font-medium uppercase tracking-wide text-slate-400">
|
|
Learner
|
|
</p>
|
|
<p className="mt-0.5 text-sm text-slate-700 dark:text-slate-200">
|
|
{turn.answer}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
),
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Submit */}
|
|
<div className="flex justify-end">
|
|
<button
|
|
type="button"
|
|
className="inline-flex h-10 items-center gap-2 rounded-md bg-primary-600 px-4 text-sm font-medium text-white transition-colors hover:bg-primary-700"
|
|
>
|
|
Submit Defense
|
|
<Send className="h-4 w-4" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
} |