feat(agent): Audit-Kopf + 4 KPI-Kacheln ueber den Ergebnis-Tabs
ResultSummary: Titel (Firma aus extracted_profile) + check_id + 4 Kacheln (Dokumente, Konform, Offene Pflichtangaben, Zu pruefen), gerechnet aus result.results. Co-Pilot-Ton: gruen/gelb/rot nur bei echten Werten. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
'use client'
|
||||
|
||||
/**
|
||||
* ResultSummary — Audit-Kopf: Titel + check_id + 4 KPI-Kacheln über den
|
||||
* Dokument-Tabs. Co-Pilot-Ton (grün wenn gut, rot nur bei echten offenen
|
||||
* Punkten, gelb für „zu prüfen"). Rechnet aus result.results (Haupt-Engine).
|
||||
*/
|
||||
|
||||
import React from 'react'
|
||||
|
||||
import type { CheckItem, DocResult } from './ChecklistView'
|
||||
|
||||
type Tone = 'gray' | 'green' | 'red' | 'amber'
|
||||
|
||||
const TONE: Record<Tone, string> = {
|
||||
gray: 'text-gray-800',
|
||||
green: 'text-green-700',
|
||||
red: 'text-red-700',
|
||||
amber: 'text-amber-700',
|
||||
}
|
||||
|
||||
function Tile({ label, value, tone }: { label: string; value: React.ReactNode; tone: Tone }) {
|
||||
return (
|
||||
<div className="border border-gray-200 rounded-lg p-3 bg-white">
|
||||
<div className={`text-2xl font-semibold leading-none ${TONE[tone]}`}>{value}</div>
|
||||
<div className="text-xs text-gray-500 mt-1.5">{label}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function isReview(c: CheckItem): boolean {
|
||||
return c.severity === 'INFO' && !c.passed && !c.skipped
|
||||
}
|
||||
|
||||
export function ResultSummary({ results }: { results: any }) {
|
||||
const docs: DocResult[] = results.results || []
|
||||
const company = results.extracted_profile?.company_profile?.companyName as string | undefined
|
||||
|
||||
let offen = 0
|
||||
let zuPruefen = 0
|
||||
let konform = 0
|
||||
let checked = 0
|
||||
for (const d of docs) {
|
||||
if (d.error) continue
|
||||
checked++
|
||||
const l1Score = d.checks.filter(c => (c.level ?? 1) === 1 && c.severity !== 'INFO')
|
||||
const l1Failed = l1Score.filter(c => !c.passed).length
|
||||
const l2Failed = d.checks.filter(
|
||||
c => (c.level ?? 1) === 2 && !c.skipped && !c.passed && c.severity !== 'INFO',
|
||||
).length
|
||||
offen += l1Failed + l2Failed
|
||||
zuPruefen += d.checks.filter(isReview).length
|
||||
if (l1Failed === 0 && (d.completeness_pct ?? 0) === 100) konform++
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-gray-900">
|
||||
Compliance-Check{company ? `: ${company}` : ''}
|
||||
</h2>
|
||||
<p className="text-xs text-gray-500 mt-0.5">
|
||||
{results.check_id && (
|
||||
<>ID <code className="bg-gray-100 px-1 rounded">{results.check_id}</code> · </>
|
||||
)}
|
||||
{docs.length} Dokument{docs.length !== 1 ? 'e' : ''} geprüft
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
|
||||
<Tile label="Dokumente" value={docs.length} tone="gray" />
|
||||
<Tile
|
||||
label="Konform"
|
||||
value={`${konform}/${checked || docs.length}`}
|
||||
tone={checked > 0 && konform === checked ? 'green' : 'gray'}
|
||||
/>
|
||||
<Tile
|
||||
label="Offene Pflichtangaben"
|
||||
value={offen}
|
||||
tone={offen > 0 ? 'red' : 'green'}
|
||||
/>
|
||||
<Tile
|
||||
label="Zu prüfen"
|
||||
value={zuPruefen}
|
||||
tone={zuPruefen > 0 ? 'amber' : 'gray'}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user