3f23a64d5f
Ergebnis-Tab rendert jetzt result.results (Haupt-Doc-Check) statt des abweichenden v3-Agenten — BMW korrekt statt False Positives: - DocResultView: ein Dokument als Pflichtangaben-Tabelle (Label + gefundener Text + 3-Tier-Status), KEINE MC-IDs. ComplianceResultTabs speist Tabs aus result.results; ChecklistView-Bausteine exportiert + wiederverwendet. - profile_extractor: Firmenname/Rechtsform = fruehester Treffer + ausge- schriebene Formen (Aktiengesellschaft) -> BMW AG statt "juris GmbH". - 36 VSBG (MC-010): reines b2c -> POSSIBLY_APPLICABLE (Pruef-Hinweis) statt MEDIUM-FAIL; hart nur bei ecommerce. possibly_hint pro MC. - McCoverage traegt label + found (Snippet); mc_possibly-Aggregat. - AgentFindingCard/Methodik: interne check_id/mc_id nicht mehr angezeigt. Tests: test_four_status (16) + Frontend-Vitest gruen; CI-Suite 206, v3/GT unveraendert. Nur eigene Dateien (geteilter Tree). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
110 lines
3.6 KiB
TypeScript
110 lines
3.6 KiB
TypeScript
'use client'
|
|
|
|
/**
|
|
* AgentResultView — der geteilte Render-Body eines AgentOutput:
|
|
* MC-Coverage + Speedometer + Eskalationslog + Findings (HIGH→LOW) +
|
|
* konsolidierte Maßnahmen. KEIN Header — den setzt der Consumer
|
|
* (AgentSlotCard = Agent-Test-Slot, AgentResultTab = Themen-Tab).
|
|
*
|
|
* Dieser View ist die "Karten"-Darstellung für Themen mit wenigen
|
|
* Findings (z.B. Impressum). Dichte Themen (Cookie, bis ~1000 Zeilen)
|
|
* bekommen später einen eigenen Tabellen-View im gleichen Tab-Rahmen.
|
|
*/
|
|
|
|
import React, { useState } from 'react'
|
|
|
|
import type { Severity, SlotOutput } from './_agentTypes'
|
|
import { AgentFindingCard } from './AgentFindingCard'
|
|
import { AgentPflichtTable } from './AgentPflichtTable'
|
|
import { AgentRecommendationCard } from './AgentRecommendationCard'
|
|
import { AgentSpeedometer } from './AgentSpeedometer'
|
|
|
|
const SEV_ORDER: Record<Severity, number> = {
|
|
HIGH: 0, MEDIUM: 1, LOW: 2, INFO: 3,
|
|
}
|
|
|
|
const INITIAL_VISIBLE = 12
|
|
|
|
export function AgentResultView({ output }: { output: SlotOutput }) {
|
|
const [showAll, setShowAll] = useState(false)
|
|
const sortedFindings = [...output.findings].sort(
|
|
(a, b) => SEV_ORDER[a.severity] - SEV_ORDER[b.severity],
|
|
)
|
|
const visible = showAll
|
|
? sortedFindings
|
|
: sortedFindings.slice(0, INITIAL_VISIBLE)
|
|
|
|
return (
|
|
<div className="space-y-3">
|
|
{output.notes && (
|
|
<div className="text-xs text-amber-700 bg-amber-50 px-2 py-1 rounded">
|
|
Hinweis: {output.notes}
|
|
</div>
|
|
)}
|
|
|
|
<AgentPflichtTable coverage={output.mc_coverage} />
|
|
|
|
<AgentSpeedometer
|
|
total={output.mc_total}
|
|
ok={output.mc_ok}
|
|
na={output.mc_na}
|
|
high={output.mc_high}
|
|
medium={output.mc_medium}
|
|
low={output.mc_low}
|
|
/>
|
|
|
|
{output.escalation_log.length > 0 && (
|
|
<div className="text-xs text-gray-600 border-l-2 border-violet-400 pl-2 space-y-0.5">
|
|
<div className="font-semibold text-violet-700">
|
|
LLM-Eskalation eingesetzt:
|
|
</div>
|
|
{output.escalation_log.map((e, i) => (
|
|
<div key={i}>
|
|
{e.stage} <code className="text-violet-700">{e.model}</code>{' '}
|
|
· {e.duration_ms} ms{' '}
|
|
{e.tokens_in ? `· ${e.tokens_in}→${e.tokens_out} tok` : ''}{' '}
|
|
{e.success ? '✓' : `✗ ${e.error || ''}`}
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
{sortedFindings.length > 0 && (
|
|
<div className="space-y-2">
|
|
<div className="text-xs font-semibold uppercase text-gray-700">
|
|
Findings ({sortedFindings.length}) — nach Schwere sortiert
|
|
</div>
|
|
<div className="space-y-2">
|
|
{visible.map(f => (
|
|
<AgentFindingCard key={f.check_id} f={f} />
|
|
))}
|
|
</div>
|
|
{sortedFindings.length > INITIAL_VISIBLE && (
|
|
<button
|
|
onClick={() => setShowAll(x => !x)}
|
|
className="text-xs text-blue-600 hover:underline"
|
|
>
|
|
{showAll
|
|
? 'Weniger anzeigen'
|
|
: `Alle ${sortedFindings.length} anzeigen`}
|
|
</button>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
{output.recommendations.length > 0 && (
|
|
<div className="space-y-2">
|
|
<div className="text-xs font-semibold uppercase text-gray-700">
|
|
Maßnahmen-Plan ({output.recommendations.length} konsolidiert)
|
|
</div>
|
|
<div className="space-y-2">
|
|
{output.recommendations.map(r => (
|
|
<AgentRecommendationCard key={r.recommendation_id} r={r} />
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|