Files
nextcraft/apps/web/components/learner/oral-defense-interface.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

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>
);
}