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>
56 lines
2.0 KiB
TypeScript
56 lines
2.0 KiB
TypeScript
'use client'
|
|
|
|
import { FileText, Hash, Image as ImageIcon, Library } from 'lucide-react'
|
|
import type { AdvisorResponse } from '@/lib/sdk/advisor/contract'
|
|
import { resolveRegulation } from '@/lib/sdk/advisor/regulation-display'
|
|
|
|
function Card({
|
|
icon,
|
|
value,
|
|
label,
|
|
dim,
|
|
}: {
|
|
icon: React.ReactNode
|
|
value: number
|
|
label: string
|
|
dim?: boolean
|
|
}) {
|
|
return (
|
|
<div
|
|
className={`flex items-center gap-2 rounded-lg border px-2.5 py-1.5 ${
|
|
dim ? 'border-gray-100 bg-gray-50' : 'border-gray-200 bg-white'
|
|
}`}
|
|
>
|
|
<span className={dim ? 'text-gray-300' : 'text-indigo-500'}>{icon}</span>
|
|
<span>
|
|
<span className={`text-sm font-bold ${dim ? 'text-gray-400' : 'text-gray-900'}`}>{value}</span>{' '}
|
|
<span className="text-[11px] text-gray-500">{label}</span>
|
|
</span>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
/**
|
|
* "Antwort basiert auf" — objective counts only (no fabricated trust score). Regelwerke = distinct
|
|
* document families. Leitlinien deliberately omitted until bindingness exists in the Legal-KG.
|
|
*/
|
|
export function EvidenceSummary({ response }: { response: AdvisorResponse }) {
|
|
const families = new Set(
|
|
response.evidence.map((e) => resolveRegulation({ code: e.document, short: e.document }).familyKey),
|
|
).size
|
|
const cls = 'h-4 w-4'
|
|
return (
|
|
<div>
|
|
<div className="mb-1.5 text-[10px] font-semibold uppercase tracking-wide text-gray-400">
|
|
Antwort basiert auf
|
|
</div>
|
|
<div className="grid grid-cols-2 gap-1.5">
|
|
<Card icon={<Library className={cls} />} value={families} label="Regelwerke" />
|
|
<Card icon={<FileText className={cls} />} value={response.evidence.length} label="Evidence Units" />
|
|
<Card icon={<ImageIcon className={cls} />} value={response.visual_evidence.length} label="Diagramme" dim={response.visual_evidence.length === 0} />
|
|
<Card icon={<Hash className={cls} />} value={response.footnotes.length} label="Fußnoten" dim={response.footnotes.length === 0} />
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|