fix(checklist-ui): show INFO-severity checks as gray info icon
Build + Deploy / build-admin-compliance (push) Successful in 2m7s
Build + Deploy / build-backend-compliance (push) Successful in 3m20s
Build + Deploy / build-ai-sdk (push) Successful in 1m2s
Build + Deploy / build-developer-portal (push) Successful in 1m14s
Build + Deploy / build-tts (push) Successful in 1m45s
Build + Deploy / build-document-crawler (push) Successful in 48s
Build + Deploy / build-dsms-gateway (push) Successful in 37s
Build + Deploy / build-dsms-node (push) Successful in 23s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / loc-budget (push) Failing after 17s
CI / secret-scan (push) Has been skipped
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Successful in 2m44s
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / test-go (push) Successful in 49s
CI / test-python-backend (push) Successful in 37s
CI / test-python-document-crawler (push) Successful in 27s
CI / test-python-dsms-gateway (push) Successful in 23s
CI / validate-canonical-controls (push) Successful in 14s
Build + Deploy / trigger-orca (push) Failing after 32s

INFO checks (V.i.S.d.P., Streitbeilegung, Berufsrecht, Stammkapital,
etc.) that fail are now shown with a gray info icon instead of red X,
with gray hint text. They are excluded from the Pflichtangaben count
since they are context-dependent and likely not applicable.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-05-12 12:28:00 +02:00
parent baca0f6b80
commit 128967fa3d
@@ -46,7 +46,7 @@ function groupChecks(checks: CheckItem[]): GroupedCheck[] {
}))
}
function CheckIcon({ passed, skipped }: { passed: boolean; skipped?: boolean }) {
function CheckIcon({ passed, skipped, isInfo }: { passed: boolean; skipped?: boolean; isInfo?: boolean }) {
if (skipped) {
return (
<svg className="w-4 h-4 text-gray-300 mt-0.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -61,6 +61,13 @@ function CheckIcon({ passed, skipped }: { passed: boolean; skipped?: boolean })
</svg>
)
}
if (isInfo) {
return (
<svg className="w-4 h-4 text-gray-400 mt-0.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
)
}
return (
<svg className="w-4 h-4 text-red-500 mt-0.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
@@ -104,8 +111,9 @@ export function ChecklistView({ results }: { results: DocResult[] }) {
const typeLabel = DOC_TYPE_LABELS[r.doc_type] || r.doc_type
const grouped = groupChecks(r.checks)
const l1Checks = r.checks.filter(c => (c.level ?? 1) === 1)
const l1Scoreable = l1Checks.filter(c => c.severity !== 'INFO')
const l2Active = r.checks.filter(c => (c.level ?? 1) === 2 && !c.skipped)
const l1Passed = l1Checks.filter(c => c.passed).length
const l1Passed = l1Scoreable.filter(c => c.passed).length
const l2Passed = l2Active.filter(c => c.passed).length
return (
@@ -126,7 +134,7 @@ export function ChecklistView({ results }: { results: DocResult[] }) {
<div className="text-sm font-medium text-gray-900 truncate">{r.label}</div>
<div className="text-xs text-gray-500 truncate">
{l1Checks.length > 0
? `${l1Passed}/${l1Checks.length} Pflichtangaben`
? `${l1Passed}/${l1Scoreable.length} Pflichtangaben`
+ (l2Active.length > 0 ? `, ${l2Passed}/${l2Active.length} Detailpruefungen` : '')
: r.url}
</div>
@@ -164,13 +172,18 @@ export function ChecklistView({ results }: { results: DocResult[] }) {
<p className="text-sm text-red-600">{r.error}</p>
) : (
<div className="space-y-1">
{grouped.map((g) => (
{grouped.map((g) => {
const l1Info = g.check.severity === 'INFO' && !g.check.passed
return (
<div key={g.check.id}>
{/* L1 check */}
<div className="flex items-start gap-2">
<CheckIcon passed={g.check.passed} />
<CheckIcon passed={g.check.passed} isInfo={l1Info} />
<div className="flex-1">
<div className={`text-sm ${g.check.passed ? 'text-gray-700' : 'text-red-700 font-medium'}`}>
<div className={`text-sm ${
g.check.passed ? 'text-gray-700'
: l1Info ? 'text-gray-500' : 'text-red-700 font-medium'
}`}>
{g.check.label}
{g.children.length > 0 && <L2Summary>{g.children}</L2Summary>}
</div>
@@ -180,7 +193,7 @@ export function ChecklistView({ results }: { results: DocResult[] }) {
</div>
)}
{!g.check.passed && g.check.hint && (
<div className="text-xs text-red-600/80 mt-0.5">
<div className={`text-xs mt-0.5 ${l1Info ? 'text-gray-400' : 'text-red-600/80'}`}>
{g.check.hint}
</div>
)}
@@ -190,13 +203,16 @@ export function ChecklistView({ results }: { results: DocResult[] }) {
{/* L2 children — always visible */}
{g.children.length > 0 && (
<div className="ml-6 mt-0.5 mb-1 space-y-0.5 border-l-2 border-gray-200 pl-3">
{g.children.map((ch) => (
{g.children.map((ch) => {
const chInfo = ch.severity === 'INFO' && !ch.passed && !ch.skipped
return (
<div key={ch.id} className="flex items-start gap-2">
<CheckIcon passed={ch.passed} skipped={ch.skipped} />
<CheckIcon passed={ch.passed} skipped={ch.skipped} isInfo={chInfo} />
<div className="flex-1">
<div className={`text-xs ${
ch.skipped ? 'text-gray-400 italic'
: ch.passed ? 'text-gray-600' : 'text-red-600 font-medium'
: ch.passed ? 'text-gray-600'
: chInfo ? 'text-gray-400' : 'text-red-600 font-medium'
}`}>
{ch.label}
{ch.skipped && ' (uebersprungen)'}
@@ -207,17 +223,19 @@ export function ChecklistView({ results }: { results: DocResult[] }) {
</div>
)}
{!ch.passed && !ch.skipped && ch.hint && (
<div className="text-xs text-red-500/80 mt-0.5">
<div className={`text-xs mt-0.5 ${chInfo ? 'text-gray-400' : 'text-red-500/80'}`}>
{ch.hint}
</div>
)}
</div>
</div>
))}
)
})}
</div>
)}
</div>
))}
)
})}
{r.word_count > 0 && (
<div className="text-xs text-gray-400 mt-2 pt-2 border-t border-gray-200">
{r.word_count} Woerter analysiert