Files
breakpilot-compliance/admin-compliance/components/sdk/advisor/KnowledgeUnitCard.tsx
T
Benjamin Admin 3884038b06 fix(advisor): generic — drop trailing source list in answer + de-duplicate source card
Two structural fixes (not query-specific):
- Proxy prompt: forbid ANY trailing "Quellen:"/"Quellen im RAG-System" list and make it
  the LAST instruction so it overrides the soul file's answer-structure + example that
  teach a closing sources section. Applies to every answer.
- KnowledgeUnitCard: render the label only when it differs from regulation.short, so a
  source whose label == short name no longer prints twice. Applies to every source.

Answer text is still never parsed in the FE (sources live in the pane). + card test.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-01 10:13:58 +02:00

72 lines
2.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
import { useState } from 'react'
import { ChevronDown, ChevronRight, ExternalLink } from 'lucide-react'
import type { KnowledgeUnit } from '@/lib/sdk/advisor/evidence'
/**
* A source rendered as a hierarchical Knowledge Unit (Regelwerk → Section → Paragraph → Footnote),
* not a text-list line. [öffnen] resolves to the original source when available; the optional
* snippet lets the user peek the cited text.
*/
export function KnowledgeUnitCard({ unit }: { unit: KnowledgeUnit }) {
const [open, setOpen] = useState(false)
const crumbs = [unit.section, unit.subsection, unit.paragraph, unit.footnoteRef].filter(Boolean)
const href = unit.open?.originalUrl
const canOpen = href && /^https?:\/\//i.test(href)
return (
<div className="rounded-lg border border-gray-200 bg-white p-2.5">
<div className="flex items-start justify-between gap-2">
<div className="min-w-0">
<div className="truncate text-xs font-semibold text-gray-900">{unit.regulation.short}</div>
{crumbs.length > 0 ? (
<div className="mt-0.5 flex flex-wrap items-center gap-x-1 text-[11px] text-gray-500">
{crumbs.map((c, i) => (
<span key={i} className="flex items-center gap-1">
{i > 0 && <span className="text-gray-300"></span>}
{c}
</span>
))}
</div>
) : (
unit.label &&
unit.label !== unit.regulation.short && (
<div className="mt-0.5 text-[11px] text-gray-500">{unit.label}</div>
)
)}
</div>
{canOpen && (
<a
href={href}
target="_blank"
rel="noopener noreferrer"
className="flex flex-shrink-0 items-center gap-0.5 rounded px-1.5 py-0.5 text-[11px] font-medium text-indigo-600 hover:bg-indigo-50"
>
<ExternalLink className="h-3 w-3" />
öffnen
</a>
)}
</div>
{unit.snippet && (
<div className="mt-1.5">
<button
type="button"
onClick={() => setOpen((v) => !v)}
className="flex items-center gap-0.5 text-[11px] text-gray-400 hover:text-gray-600"
>
{open ? <ChevronDown className="h-3 w-3" /> : <ChevronRight className="h-3 w-3" />}
Textauszug
</button>
{open && (
<p className="mt-1 border-l-2 border-gray-200 pl-2 text-[11px] italic text-gray-500">
{unit.snippet}
</p>
)}
</div>
)}
</div>
)
}