feat(compliance-check): scenario badges + extracted profile display
Build + Deploy / build-dsms-node (push) Successful in 13s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / loc-budget (push) Failing after 15s
CI / secret-scan (push) Has been skipped
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
Build + Deploy / build-admin-compliance (push) Successful in 1m58s
Build + Deploy / build-backend-compliance (push) Successful in 13s
Build + Deploy / build-ai-sdk (push) Successful in 49s
Build + Deploy / build-developer-portal (push) Successful in 14s
Build + Deploy / build-tts (push) Successful in 15s
Build + Deploy / build-document-crawler (push) Successful in 12s
Build + Deploy / build-dsms-gateway (push) Successful in 11s
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Successful in 2m40s
CI / test-python-dsms-gateway (push) Successful in 24s
CI / validate-canonical-controls (push) Successful in 14s
CI / test-go (push) Successful in 43s
CI / test-python-backend (push) Successful in 39s
CI / test-python-document-crawler (push) Successful in 27s
Build + Deploy / trigger-orca (push) Successful in 2m34s
Build + Deploy / build-dsms-node (push) Successful in 13s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / loc-budget (push) Failing after 15s
CI / secret-scan (push) Has been skipped
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
Build + Deploy / build-admin-compliance (push) Successful in 1m58s
Build + Deploy / build-backend-compliance (push) Successful in 13s
Build + Deploy / build-ai-sdk (push) Successful in 49s
Build + Deploy / build-developer-portal (push) Successful in 14s
Build + Deploy / build-tts (push) Successful in 15s
Build + Deploy / build-document-crawler (push) Successful in 12s
Build + Deploy / build-dsms-gateway (push) Successful in 11s
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Successful in 2m40s
CI / test-python-dsms-gateway (push) Successful in 24s
CI / validate-canonical-controls (push) Successful in 14s
CI / test-go (push) Successful in 43s
CI / test-python-backend (push) Successful in 39s
CI / test-python-document-crawler (push) Successful in 27s
Build + Deploy / trigger-orca (push) Successful in 2m34s
- Show extracted profile fields (company name, legal form, address, DPO, USt-IdNr) with "In Company Profile uebernehmen" button - Show Compliance Scope hints extracted from documents - Scenario badges per document: Neugenerierung (red), Korrekturen (amber), Konform (green) - Summary line shows scenario counts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,13 @@ interface DocResult {
|
|||||||
checks: CheckItem[]
|
checks: CheckItem[]
|
||||||
findings_count: number
|
findings_count: number
|
||||||
error: string
|
error: string
|
||||||
|
scenario?: string // regenerate | fix | import | skip
|
||||||
|
}
|
||||||
|
|
||||||
|
const SCENARIO_LABELS: Record<string, { label: string; color: string; bg: string }> = {
|
||||||
|
regenerate: { label: 'Neugenerierung', color: 'text-red-700', bg: 'bg-red-100' },
|
||||||
|
fix: { label: 'Korrekturen', color: 'text-amber-700', bg: 'bg-amber-100' },
|
||||||
|
import: { label: 'Konform', color: 'text-green-700', bg: 'bg-green-100' },
|
||||||
}
|
}
|
||||||
|
|
||||||
const DOC_TYPE_LABELS: Record<string, string> = {
|
const DOC_TYPE_LABELS: Record<string, string> = {
|
||||||
@@ -91,14 +98,23 @@ export function ChecklistView({ results }: { results: DocResult[] }) {
|
|||||||
|
|
||||||
if (!results || results.length === 0) return null
|
if (!results || results.length === 0) return null
|
||||||
|
|
||||||
const totalOk = results.filter(r => r.completeness_pct === 100).length
|
const scenarioCounts = {
|
||||||
|
regenerate: results.filter(r => r.scenario === 'regenerate').length,
|
||||||
|
fix: results.filter(r => r.scenario === 'fix').length,
|
||||||
|
import: results.filter(r => r.scenario === 'import').length,
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between flex-wrap gap-2">
|
||||||
<h3 className="text-sm font-semibold text-gray-800">
|
<h3 className="text-sm font-semibold text-gray-800">
|
||||||
Dokumenten-Pruefung ({results.length} Dokumente, {totalOk} vollstaendig)
|
Dokumenten-Pruefung ({results.length} Dokumente)
|
||||||
</h3>
|
</h3>
|
||||||
|
<div className="flex gap-2 text-[10px]">
|
||||||
|
{scenarioCounts.import > 0 && <span className="bg-green-100 text-green-700 px-2 py-0.5 rounded-full">{scenarioCounts.import} konform</span>}
|
||||||
|
{scenarioCounts.fix > 0 && <span className="bg-amber-100 text-amber-700 px-2 py-0.5 rounded-full">{scenarioCounts.fix} Korrekturen</span>}
|
||||||
|
{scenarioCounts.regenerate > 0 && <span className="bg-red-100 text-red-700 px-2 py-0.5 rounded-full">{scenarioCounts.regenerate} Neugenerierung</span>}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
@@ -131,7 +147,14 @@ export function ChecklistView({ results }: { results: DocResult[] }) {
|
|||||||
{typeLabel}
|
{typeLabel}
|
||||||
</span>
|
</span>
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
<div className="text-sm font-medium text-gray-900 truncate">{r.label}</div>
|
<div className="text-sm font-medium text-gray-900 truncate flex items-center gap-2">
|
||||||
|
{r.label}
|
||||||
|
{r.scenario && SCENARIO_LABELS[r.scenario] && (
|
||||||
|
<span className={`text-[10px] px-1.5 py-0.5 rounded-full font-medium ${SCENARIO_LABELS[r.scenario].bg} ${SCENARIO_LABELS[r.scenario].color}`}>
|
||||||
|
{SCENARIO_LABELS[r.scenario].label}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<div className="text-xs text-gray-500 truncate">
|
<div className="text-xs text-gray-500 truncate">
|
||||||
{l1Checks.length > 0
|
{l1Checks.length > 0
|
||||||
? `${l1Passed}/${l1Scoreable.length} Pflichtangaben`
|
? `${l1Passed}/${l1Scoreable.length} Pflichtangaben`
|
||||||
|
|||||||
@@ -364,6 +364,46 @@ export function ComplianceCheckTab() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Extracted Profile — pre-fill suggestion */}
|
||||||
|
{results.extracted_profile?.company_profile && Object.keys(results.extracted_profile.company_profile).length > 0 && (
|
||||||
|
<div className="mb-4 p-3 bg-emerald-50 border border-emerald-200 rounded-lg text-xs">
|
||||||
|
<div className="flex items-center justify-between mb-1">
|
||||||
|
<span className="font-semibold text-emerald-900">Aus Dokumenten extrahiert</span>
|
||||||
|
<button className="text-emerald-700 hover:text-emerald-900 text-xs font-medium underline"
|
||||||
|
onClick={() => { /* TODO: navigate to company profile with pre-fill */ }}>
|
||||||
|
In Company Profile uebernehmen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-wrap gap-x-4 gap-y-1 text-emerald-700">
|
||||||
|
{results.extracted_profile.company_profile.companyName && (
|
||||||
|
<span>Firma: <strong>{results.extracted_profile.company_profile.companyName}</strong></span>
|
||||||
|
)}
|
||||||
|
{results.extracted_profile.company_profile.legalForm && (
|
||||||
|
<span>Rechtsform: {results.extracted_profile.company_profile.legalForm.toUpperCase()}</span>
|
||||||
|
)}
|
||||||
|
{results.extracted_profile.company_profile.headquartersCity && (
|
||||||
|
<span>Sitz: {results.extracted_profile.company_profile.headquartersZip} {results.extracted_profile.company_profile.headquartersCity}</span>
|
||||||
|
)}
|
||||||
|
{results.extracted_profile.company_profile.dpoEmail && (
|
||||||
|
<span>DSB: {results.extracted_profile.company_profile.dpoEmail}</span>
|
||||||
|
)}
|
||||||
|
{results.extracted_profile.company_profile.ustIdNr && (
|
||||||
|
<span>USt-IdNr: {results.extracted_profile.company_profile.ustIdNr}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{results.extracted_profile.compliance_scope_hints?.length > 0 && (
|
||||||
|
<div className="mt-2 pt-2 border-t border-emerald-200 text-emerald-600">
|
||||||
|
<span className="font-medium">Scope-Hinweise: </span>
|
||||||
|
{results.extracted_profile.compliance_scope_hints.map((h: any, i: number) => (
|
||||||
|
<span key={i} className="inline-block bg-emerald-100 rounded px-1.5 py-0.5 mr-1 mb-1">
|
||||||
|
{h.source}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Banner Check Result */}
|
{/* Banner Check Result */}
|
||||||
{results.banner_result && (
|
{results.banner_result && (
|
||||||
<div className={`mb-4 p-3 rounded-lg border text-xs ${
|
<div className={`mb-4 p-3 rounded-lg border text-xs ${
|
||||||
@@ -387,8 +427,8 @@ export function ComplianceCheckTab() {
|
|||||||
<>
|
<>
|
||||||
Banner erkannt{results.banner_result.provider ? ` (${results.banner_result.provider})` : ''}.
|
Banner erkannt{results.banner_result.provider ? ` (${results.banner_result.provider})` : ''}.
|
||||||
{results.banner_result.violations > 0
|
{results.banner_result.violations > 0
|
||||||
? ` ${results.banner_result.violations} Auffaelligkeit${results.banner_result.violations !== 1 ? 'en' : ''} gefunden — Cross-Check-Ergebnisse sind in der Cookie-Richtlinie-Checkliste enthalten.`
|
? ` ${results.banner_result.violations} Auffaelligkeit${results.banner_result.violations !== 1 ? 'en' : ''} gefunden.`
|
||||||
: ' Keine Auffaelligkeiten beim Banner-Cookie-Abgleich.'}
|
: ' Keine Auffaelligkeiten.'}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
'Kein Cookie-Banner erkannt oder Banner-Check nicht moeglich.'
|
'Kein Cookie-Banner erkannt oder Banner-Check nicht moeglich.'
|
||||||
|
|||||||
Reference in New Issue
Block a user