Based on User Stories v1.0 (PRD v1.3) · Last audited: 2026-03-22 (updated after Stage 8)
| Symbol | Meaning |
|---|---|
| ✅ | Fully implemented |
| 🟡 | Partially implemented — core UI exists, gaps noted |
| ❌ | Not implemented |
| 🔌 | Requires real API (cannot be fully mocked) |
| Epic | Stories | Done | Partial | Missing |
|---|---|---|---|---|
| E1 Workspace Shell | US-001–003 | 0 | 3 | 0 |
| E2 Onboarding | US-004–006 | 2 | 0 | 1 |
| E3 Progress Model | US-007–010 | 3 | 1 | 0 |
| E4 Requirements | US-011–016 | 1 | 1 | 4 |
| E5 Candidate Matching | US-017–022 | 5 | 0 | 1 |
| E6 SidePanel | US-023–028 | 5 | 0 | 1 |
| E7 Matcher Integration | US-029–033 | 4 | 0 | 1 |
| E8 Conversation Layer | US-034–038 | 4 | 0 | 1 |
| E9 History & Audit | US-039–041 | 1 | 0 | 2 |
| TOTAL | 41 | 25 | 5 | 11 |
Status: 🟡 Partial
Implemented:
- Two-column layout with left chat panel and right sidebar
- White card on light grey background
- Toptal logo top-left
Gaps:
- Canvas width is 1024px — spec requires 1032px (PageContent) within 1440px canvas
- Gap between panels is not exactly 48px as specced
- No
max-width: 1440pxouter container
Status: 🟡 Partial
Implemented:
- Role title rendered ("Java Script Back-end Developer")
IndicatorContainerrenders below title, reads phase fromPhaseContext
Gaps:
- Header is not CSS
position: sticky— scrolling the thread scrolls the header away - Role title is hardcoded — not driven by application state
Status: 🟡 Partial
Implemented:
ChatInputhas a real<textarea>, Enter-to-submit,onSend/isLoadingprops- Voice mode toggle button present
- Placeholder text "Write anything..."
Gaps:
- Stop button (to cancel in-flight response) not implemented —
isLoadingstate exists but no Stop button renders - Voice/Stop toggle swap not implemented
- Keyboard shortcut hint (⌘⇧/) not shown
Status: ✅ Done
VideoSnippet renders with thumbnail placeholder, play button, "Show transcription" toggle, and all 5 bullet points. AI welcome messages present in the thread.
Status: ✅ Done
Two pinned pills ("Yes, let's use the voice mode" / "Yes, let's chat") render below the thread. Clicking posts a user-text message, dismisses the pills, and advances phase to 2.
Status: ❌ Not implemented
No input handling for JD generation flow. No phase transition triggered by text input.
Status: ✅ Done
6 dots with correct color states (green/blue/grey), phase label text. Reads activePhase from PhaseContext — updates live as phase changes.
Status: ✅ Done
Card renders with all 6 steps, correct icon states (done/current/pending). Reads activePhase from PhaseContext — step states update live.
Status: ✅ Done
PhaseContext provides activePhase / setActivePhase / phaseLabel. IndicatorContainer and AISnippetSteps both read from context and stay in sync.
Status: 🟡 Partial
Phase 1→2 transition triggered by voice/chat pill selection. Phase 2→3 triggered by JD confirmation pill ("Yes, looks good") — appends AI acknowledgment and unlocks candidates. Not all phase transitions have dedicated messages.
Status: 🟡 Partial
AISnippetRequirements renders inline in the thread with scrollable content, "Job Details" header, and Copy icon. Content is hardcoded.
Gap: Content is not driven by state — always shows the same hardcoded JD.
Status: ❌ Not implemented
No question sequencing, no questionIndex state, no dynamic follow-up questions.
Status: ✅ Done
"Does this look right?" message with interaction-options pills ("Yes, looks good" / "I'd like to adjust this") in thread. Confirming advances to phase 3, appends AI message, and marks JD as updated.
Status: ❌ Not implemented
JD is static. No mechanism to update JD state or append new AISnippetRequirements cards.
Status: ❌ Not implemented
No version selector, no jdVersions array in state.
Status: ❌ Not implemented
Status: ✅ Done
3-card stacked effect with correct absolute positioning, depth shadow, and frontIndex state for cycling candidates.
Status: ✅ Done
Left column (photo placeholder, name, role, CTA button) and right column (fit signals, availability, skill pills). Badge renders as teal "Suggested by [Name]" for matcher-sourced candidates or purple "Auto-matched" for system-matched ones.
Status: ✅ Done
Decision flow: "Review Candidate" → opens modal; decisions tracked in PhaseContext (shared); card advances to next; overlay label (green "Interested" / red "Not a fit").
Status: ✅ Done
Full modal with dark navy header, fit signals, skills, availability, scrollable body (About + Experience), sticky action bar with Interested/Not a Fit buttons, navigation arrows, close button, and reason dropdown.
Status: ✅ Done
First "Not a fit" decision triggers onPass callback which appends an AI acknowledgment message: "Thanks for the feedback on [Name]. I'll factor this into future recommendations."
Status: ❌ Not implemented (correctly deferred — v2.0 scope)
Status: ✅ Done
MatcherCard renders with photo placeholder, green status dot, Call/Chat buttons, and description bullets. NavRow items for Job Details and Candidates present.
Status: ✅ Done
3-panel CSS translateX animation. activePanel state drives slide between default, job-details, and candidates views.
Status: ✅ Done
JobDetailsPanel slides in with back arrow, header, and full JD content (About the job, Key Requirements, Nice to Have).
Status: ✅ Done
CandidatesPanel slides in with back arrow, candidate list (photo, name, role), decision status pills per candidate, and "N shortlisted" count in header.
Status: ✅ Done
Job Details nav badge shows "Updated" after JD is confirmed (phase 2→3 advance), clears when panel is opened. Candidates nav badge shows count of shortlisted (interested) candidates. Both read from PhaseContext.
Status: ❌ Not implemented
Status: ✅ Done
MatcherTooltip shows/hides correctly. Hidden on load. Triggered by keyword detection in handleSend (keywords: "requirements", "matcher", "help", "adjust", "refine", "struggling", "not sure", "unsure", "change"). tooltipTriggerCount in PhaseContext propagates trigger to SidePanel.
Gap: Timer-based and inactivity-based triggers not implemented (keyword trigger covers the demo).
Status: ✅ Done
source: "system" | "matcher" and matcherName?: string on Candidate type. Priya Sharma is source: "matcher", matcherName: "Steven". Card badge renders as teal "Suggested by Steven" vs. purple "Auto-matched".
Status: ❌ Not implemented
Status: ✅ Done
Call and Chat buttons render correctly. onClick is a no-op (prototype requirement met).
Status: ✅ Done
Green dot + "Monitoring your activity" text overlay on matcher photo. Hardcoded as active (prototype requirement met).
Status: ✅ Done
Data-driven messages[] array with discriminated union types (ai-heading | ai-text | user-text | snippet-* | interaction-options). renderMessage() renders each type. Auto-scroll on new message (skips initial mount).
Status: ✅ Done
ChatInput has a real <textarea>, Enter-to-submit, onSend callback, isLoading prop that disables input. Messages append to thread with mock AI response.
Status: ✅ Done
interaction-options message type renders pill buttons inline in the thread. On click: posts user-text message, removes the pills, triggers action handler (e.g. JD confirmation). Pinned bottom pills also use same pattern.
Status: ✅ Done
isLoading state in WorkspaceInner. Animated TypingIndicator (3 bouncing dots via @keyframes typing-bounce) renders at bottom of thread while loading. ChatInput disabled during loading.
Gap: Stop button to cancel mid-response not yet wired up.
Status: ❌ Not implemented (correctly deferred — v1.1 scope)
Status: ✅ Done
candidateDecisions[] and statusHistory[][] (per candidate, with timestamps) stored in PhaseContext. CandidatesPanel shows current decision status per candidate and a "N shortlisted" count. Status can be changed in the modal (re-clicking Interested from Not a Fit updates context).
Status: ❌ Not implemented
Status: ❌ Not implemented
| # | Story | What to build |
|---|---|---|
| US-001 | Layout dimensions | Fix canvas to 1440px outer / 1032px content; exact 48px gap |
| US-002 | Sticky header | Add position: sticky; top: 0 to HeadingContainer |
| US-003 | Stop button | Render Stop button when isLoading; wire cancel |
| # | Story | What to build |
|---|---|---|
| US-006 🔌 | JD generation | Text input → mock structured JD → append AISnippetRequirements |
| US-010 | All transition messages | Dedicated messages for each phase change |
| US-012 🔌 | Clarifying questions | questionIndex state; show questions sequentially after JD draft |
| US-014 | JD updates in thread | Append new AISnippetRequirements card when JD is edited |
| US-028 | Scroll preservation | Store scrollTop per panel; restore on open |
| US-031 | Matcher JD attribution | Append JD version with matcherName; show attribution label |
| # | Story | Notes |
|---|---|---|
| US-015 | JD version history dropdown | jdVersions array; version selector in Job Details panel |
| US-016 🔌 | JD completeness signal | Hardcode for prototype |
| US-022 | Side-by-side comparison | v2.0 scope |
| US-038 | Voice mode | v1.1 scope |
| US-040 | JD change log | Display in version dropdown |
| US-041 | Audit log | In-state, no UI needed for prototype |
| Component | File | Status |
|---|---|---|
| MatchingWorkspace | components/MatchingWorkspace.tsx |
✅ Complete — data-driven thread, phase wired |
| IndicatorContainer | components/IndicatorContainer.tsx |
✅ Complete — reads from PhaseContext |
| AISnippetSteps | components/AISnippetSteps.tsx |
✅ Complete — reads from PhaseContext |
| AISnippetRequirements | components/AISnippetRequirements.tsx |
🟡 Partial — hardcoded content |
| AISnippetTalents | components/AISnippetTalents.tsx |
✅ Complete — decisions in context, badges |
| CandidateModal | components/CandidateModal.tsx |
✅ Complete |
| VideoSnippet | components/VideoSnippet.tsx |
✅ Complete |
| SidePanel | components/SidePanel.tsx |
✅ Complete — 3-panel slide, badges |
| JobDetailsPanel | components/JobDetailsPanel.tsx |
✅ Complete |
| CandidatesPanel | components/CandidatesPanel.tsx |
✅ Complete — decisions + shortlist count |
| MatcherCard | components/MatcherCard.tsx |
✅ Complete |
| MatcherTooltip | components/MatcherTooltip.tsx |
✅ Complete — keyword-triggered |
| ChatInput | components/ChatInput.tsx |
🟡 Partial — missing Stop button |
| NavRow | components/NavRow.tsx |
✅ Complete |
| ToptalLogo | components/ToptalLogo.tsx |
✅ Complete |
| PhaseContext | context/PhaseContext.tsx |
✅ Complete |