f9b7ba2424
Builds the FE against the SDK<->FE Clarity-Gate contract (board 2026-07-01 /
advisor-clarity-gate-contract). The advisor is now a CASE, not a chat:
- Request {question, context?}; response {mode: clarify|answer, clarity, general_answer,
answer, evidence, citations, visual_evidence, footnotes}.
- clarify mode: short L1 general answer (marked "allgemeine Definition, ohne Rechtsquelle")
+ domain context chips; picking a chip re-runs the case scoped (-> answer).
- answer mode: markdown answer with clickable [n] citation markers coupled to evidence
cards (highlight + scroll), evidence grouped by document family, visual_evidence
(visual_type), footnotes, honest summary counts (no trust score).
- FE never parses the answer for structure — only the deliberate [n] markers, mapped via
citations[]. New: contract.ts, useAdvisorCase, useCitationHighlight, ClarifyView,
EvidenceUnitCard, VisualEvidencePane, CaseView. Removed the v2 stream/chat components.
NOT deployed: FE shape-switch (JSON modes) must deploy TOGETHER with the SDK endpoint
delivering the contract (board deploy-coupling). Proxy/route.ts unchanged (SDK-owned).
tsc clean, 16 vitest (incl. clarify+answer fixtures), check-loc 0.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
69 lines
2.5 KiB
TypeScript
69 lines
2.5 KiB
TypeScript
import { describe, it, expect, vi } from 'vitest'
|
|
import { render, fireEvent } from '@testing-library/react'
|
|
import { CaseView } from './CaseView'
|
|
import type { AdvisorCase } from './useAdvisorCase'
|
|
import type { AdvisorResponse } from '@/lib/sdk/advisor/contract'
|
|
|
|
const clarify: AdvisorResponse = {
|
|
mode: 'clarify',
|
|
question: 'Was ist PDCA?',
|
|
clarity: {
|
|
is_underspecified: true,
|
|
concentration: 0.38,
|
|
suggested_contexts: [
|
|
{ id: 'datenschutz', label: 'Datenschutz' },
|
|
{ id: 'qm', label: 'Qualitätsmanagement' },
|
|
],
|
|
},
|
|
general_answer: 'PDCA steht für **Plan-Do-Check-Act**.',
|
|
answer: null,
|
|
evidence: [],
|
|
citations: [],
|
|
visual_evidence: [],
|
|
footnotes: [],
|
|
}
|
|
|
|
const answer: AdvisorResponse = {
|
|
mode: 'answer',
|
|
question: 'PDCA im Datenschutz?',
|
|
clarity: { is_underspecified: false, dominant_context: 'datenschutz', concentration: 0.88 },
|
|
answer: 'Der DSM-Zyklus [1] beschreibt den Ablauf.',
|
|
evidence: [
|
|
{ evidence_id: 'e1', document: 'DSK Sdm B41', section: 'Art. 5', paragraph: 'Abs. 2', snippet: 'x' },
|
|
],
|
|
citations: [
|
|
{ citation_id: 'c1', evidence_id: 'e1', document: 'DSK Sdm B41', section: 'Art. 5', paragraph: 'Abs. 2' },
|
|
],
|
|
visual_evidence: [
|
|
{ visual_id: 'v1', visual_type: 'flowchart', caption: 'PDCA-Zyklus', document: 'DSK SDM', vision_summary: 's' },
|
|
],
|
|
footnotes: [],
|
|
}
|
|
|
|
function mk(response: AdvisorResponse): AdvisorCase {
|
|
return { id: 'case1', question: response.question, response, selectedContext: null, status: 'done' }
|
|
}
|
|
|
|
describe('CaseView — clarify mode', () => {
|
|
it('renders the L1 general answer + context chips and fires onSelectContext', () => {
|
|
const onSel = vi.fn()
|
|
const { container, getByText } = render(
|
|
<CaseView c={mk(clarify)} busy={false} onSelectContext={onSel} />,
|
|
)
|
|
expect(container.textContent).toContain('Plan-Do-Check-Act')
|
|
expect(container.textContent).toContain('Allgemeine Definition')
|
|
fireEvent.click(getByText('Datenschutz'))
|
|
expect(onSel).toHaveBeenCalledWith('datenschutz')
|
|
})
|
|
})
|
|
|
|
describe('CaseView — answer mode', () => {
|
|
it('renders answer with a clickable [n] citation, grouped evidence (friendly name), and visual', () => {
|
|
const { container } = render(<CaseView c={mk(answer)} busy={false} onSelectContext={() => {}} />)
|
|
expect(container.textContent).toContain('DSM-Zyklus')
|
|
expect(container.querySelector('button[title="Beleg 1 anzeigen"]')).not.toBeNull()
|
|
expect(container.textContent).toContain('DSK Standard-Datenschutzmodell')
|
|
expect(container.textContent).toContain('PDCA-Zyklus')
|
|
})
|
|
})
|