diff --git a/admin-compliance/app/sdk/agent/_components/ComplianceResultTabs.tsx b/admin-compliance/app/sdk/agent/_components/ComplianceResultTabs.tsx index 84f1a465..2ed00cd8 100644 --- a/admin-compliance/app/sdk/agent/_components/ComplianceResultTabs.tsx +++ b/admin-compliance/app/sdk/agent/_components/ComplianceResultTabs.tsx @@ -13,6 +13,7 @@ import React, { useState } from 'react' import { ChecklistView, DOC_TYPE_LABELS, type DocResult } from './ChecklistView' import { DocResultView } from './DocResultView' import { MigrationPanel } from './MigrationPanel' +import { ResultSummary } from './ResultSummary' export function ComplianceResultTabs({ results }: { results: any }) { // Themen-Tabs aus der HAUPT-Engine (result.results) — nicht aus dem @@ -23,6 +24,9 @@ export function ComplianceResultTabs({ results }: { results: any }) { return (
+ {/* Audit-Kopf: Titel + check_id + 4 KPI-Kacheln */} + + {/* Kopf-Boxen über den Tabs */} {results.business_profile && (
diff --git a/admin-compliance/app/sdk/agent/_components/ResultSummary.tsx b/admin-compliance/app/sdk/agent/_components/ResultSummary.tsx new file mode 100644 index 00000000..e8171f22 --- /dev/null +++ b/admin-compliance/app/sdk/agent/_components/ResultSummary.tsx @@ -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 = { + 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 ( +
+
{value}
+
{label}
+
+ ) +} + +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 ( +
+
+

+ Compliance-Check{company ? `: ${company}` : ''} +

+

+ {results.check_id && ( + <>ID {results.check_id} · + )} + {docs.length} Dokument{docs.length !== 1 ? 'e' : ''} geprüft +

+
+
+ + 0 && konform === checked ? 'green' : 'gray'} + /> + 0 ? 'red' : 'green'} + /> + 0 ? 'amber' : 'gray'} + /> +
+
+ ) +} diff --git a/admin-compliance/app/sdk/agent/_components/__tests__/ResultSummary.test.tsx b/admin-compliance/app/sdk/agent/_components/__tests__/ResultSummary.test.tsx new file mode 100644 index 00000000..99b8ccb8 --- /dev/null +++ b/admin-compliance/app/sdk/agent/_components/__tests__/ResultSummary.test.tsx @@ -0,0 +1,30 @@ +import { describe, it, expect } from 'vitest' +import { render, screen } from '@testing-library/react' + +import { ResultSummary } from '../ResultSummary' + +describe('ResultSummary', () => { + it('zeigt Firma im Titel + zählt Konform-KPI aus result.results', () => { + const results = { + check_id: 'abc123', + extracted_profile: { company_profile: { companyName: 'Bayerische Motoren Werke Aktiengesellschaft' } }, + results: [ + { doc_type: 'impressum', completeness_pct: 100, correctness_pct: 100, error: '', + checks: [{ id: 'a', label: 'X', passed: true, severity: 'HIGH', matched_text: '', level: 1 }] }, + { doc_type: 'dse', completeness_pct: 50, correctness_pct: 50, error: '', + checks: [ + { id: 'b', label: 'Y', passed: false, severity: 'HIGH', matched_text: '', level: 1 }, + { id: 'c', label: 'Z', passed: false, severity: 'INFO', matched_text: '', level: 1 }, + ] }, + ], + } + render() + expect(screen.getByText(/Bayerische Motoren Werke/)).toBeInTheDocument() + // 4 Kachel-Labels + Konform 1/2 (impressum konform, dse nicht) + expect(screen.getByText('Dokumente')).toBeInTheDocument() + expect(screen.getByText('Konform')).toBeInTheDocument() + expect(screen.getByText('Offene Pflichtangaben')).toBeInTheDocument() + expect(screen.getByText('Zu prüfen')).toBeInTheDocument() + expect(screen.getByText('1/2')).toBeInTheDocument() + }) +})