feat(advisor): Case Workspace v2 — Evidence grouping, human names, 3-column, summary
Reworks the advisor toward a Compliance Case Workspace (review feedback): - Rename user-facing "Quellen" -> "Evidence". - Evidence grouped by document/regulation family (count + expandable) — no more unsorted DSK/DSK/DPF/... jumble. - Human-readable regulation names via a display registry (DSK Sdm B51 -> "DSK Standard-Datenschutzmodell (SDM)" / Kapitel B51); generic, bridges G2. - Evidence summary "Antwort basiert auf" with meaningful counts; Regelwerke = distinct FAMILIES (fixes the inflated count). NO fabricated trust score (needs a defined basis). - Expanded mode = 3-column workspace (question+summary | answer | evidence, independent scroll) + history switcher; narrow mode stays stacked. - Prompt: push aggressive markdown structure (## per aspect, numbered phases). Deferred/coordinated on board: C8 diagrams (RAG contract), answer<->evidence coupling [1] (needs LLM citation anchors — phase 2), G1 retrieval relevance + G2 metadata (RAG). tsc clean, 17 vitest, check-loc 0. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { ChevronDown, ChevronRight, Library } from 'lucide-react'
|
||||
import type { KnowledgeUnit } from '@/lib/sdk/advisor/evidence'
|
||||
import { resolveRegulation } from '@/lib/sdk/advisor/regulation-display'
|
||||
import { KnowledgeUnitCard } from './KnowledgeUnitCard'
|
||||
import { PaneHeader } from './PaneHeader'
|
||||
|
||||
interface EvidenceGroupData {
|
||||
key: string
|
||||
label: string
|
||||
units: KnowledgeUnit[]
|
||||
}
|
||||
|
||||
function groupByFamily(sources: KnowledgeUnit[]): EvidenceGroupData[] {
|
||||
const map = new Map<string, EvidenceGroupData>()
|
||||
for (const u of sources) {
|
||||
const d = resolveRegulation(u.regulation)
|
||||
const g = map.get(d.familyKey) ?? { key: d.familyKey, label: d.familyLabel, units: [] }
|
||||
g.units.push(u)
|
||||
map.set(d.familyKey, g)
|
||||
}
|
||||
return [...map.values()].sort((a, b) => b.units.length - a.units.length)
|
||||
}
|
||||
|
||||
function EvidenceGroup({ group }: { group: EvidenceGroupData }) {
|
||||
const [open, setOpen] = useState(group.units.length <= 3)
|
||||
return (
|
||||
<div className="rounded-lg border border-gray-200 bg-white">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen((v) => !v)}
|
||||
className="flex w-full items-center justify-between gap-2 px-2.5 py-2 text-left"
|
||||
>
|
||||
<span className="min-w-0 truncate text-xs font-semibold text-gray-900">{group.label}</span>
|
||||
<span className="flex flex-shrink-0 items-center gap-1 text-[11px] text-gray-500">
|
||||
{group.units.length} Treffer
|
||||
{open ? <ChevronDown className="h-3.5 w-3.5" /> : <ChevronRight className="h-3.5 w-3.5" />}
|
||||
</span>
|
||||
</button>
|
||||
{open && (
|
||||
<div className="space-y-1 border-t border-gray-100 px-2 py-2">
|
||||
{group.units.map((u) => (
|
||||
<KnowledgeUnitCard key={u.id} unit={u} compact />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/** Evidence pane — retrieved units grouped by document/regulation family, count + expandable. */
|
||||
export function EvidencePane({ sources }: { sources: KnowledgeUnit[] }) {
|
||||
const groups = groupByFamily(sources)
|
||||
return (
|
||||
<section>
|
||||
<PaneHeader
|
||||
icon={<Library className="h-3.5 w-3.5 text-gray-500" />}
|
||||
title="Evidence"
|
||||
count={sources.length}
|
||||
/>
|
||||
{groups.length === 0 ? (
|
||||
<p className="px-1 text-[11px] text-gray-400">Keine strukturierte Evidence zu dieser Antwort.</p>
|
||||
) : (
|
||||
<div className="space-y-1.5">
|
||||
{groups.map((g) => (
|
||||
<EvidenceGroup key={g.key} group={g} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user