refactor(admin): split control-library, iace/mitigations, iace/components pages
Extract hooks, sub-components, and constants into colocated files to bring all three page.tsx files under the 500-LOC hard cap (225, 134, 111 LOC). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
'use client'
|
||||
|
||||
import { ChevronRight, BookOpen, Clock } from 'lucide-react'
|
||||
import { CanonicalControl, SeverityBadge, StateBadge, LicenseRuleBadge, VerificationMethodBadge, CategoryBadge, EvidenceTypeBadge, TargetAudienceBadge, GenerationStrategyBadge, ObligationTypeBadge } from './helpers'
|
||||
|
||||
interface ControlListItemProps {
|
||||
ctrl: CanonicalControl
|
||||
sortBy: string
|
||||
prevSource: string | null
|
||||
onClick: () => void
|
||||
}
|
||||
|
||||
export function ControlListItem({ ctrl, sortBy, prevSource, onClick }: ControlListItemProps) {
|
||||
const curSource = ctrl.source_citation?.source || 'Ohne Quelle'
|
||||
const showSourceHeader = sortBy === 'source' && curSource !== prevSource
|
||||
|
||||
return (
|
||||
<div key={ctrl.control_id}>
|
||||
{showSourceHeader && (
|
||||
<div className="flex items-center gap-2 pt-3 pb-1">
|
||||
<div className="h-px flex-1 bg-blue-200" />
|
||||
<span className="text-xs font-semibold text-blue-700 bg-blue-50 px-2 py-0.5 rounded whitespace-nowrap">{curSource}</span>
|
||||
<div className="h-px flex-1 bg-blue-200" />
|
||||
</div>
|
||||
)}
|
||||
<button
|
||||
onClick={onClick}
|
||||
className="w-full text-left bg-white border border-gray-200 rounded-lg p-4 hover:border-purple-300 hover:shadow-sm transition-all group"
|
||||
>
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2 mb-1 flex-wrap">
|
||||
<span className="text-xs font-mono text-purple-600 bg-purple-50 px-1.5 py-0.5 rounded">{ctrl.control_id}</span>
|
||||
<SeverityBadge severity={ctrl.severity} />
|
||||
<StateBadge state={ctrl.release_state} />
|
||||
<LicenseRuleBadge rule={ctrl.license_rule} />
|
||||
<VerificationMethodBadge method={ctrl.verification_method} />
|
||||
<CategoryBadge category={ctrl.category} />
|
||||
<EvidenceTypeBadge type={ctrl.evidence_type} />
|
||||
<TargetAudienceBadge audience={ctrl.target_audience} />
|
||||
<GenerationStrategyBadge strategy={ctrl.generation_strategy} pipelineInfo={ctrl} />
|
||||
<ObligationTypeBadge type={ctrl.generation_metadata?.obligation_type as string} />
|
||||
{ctrl.risk_score !== null && (
|
||||
<span className="text-xs text-gray-400">Score: {ctrl.risk_score}</span>
|
||||
)}
|
||||
</div>
|
||||
<h3 className="text-sm font-medium text-gray-900 group-hover:text-purple-700">{ctrl.title}</h3>
|
||||
<p className="text-xs text-gray-500 mt-1 line-clamp-2">{ctrl.objective}</p>
|
||||
<div className="flex items-center gap-2 mt-2">
|
||||
<BookOpen className="w-3 h-3 text-green-600" />
|
||||
<span className="text-xs text-green-700">{ctrl.open_anchors.length} Referenzen</span>
|
||||
{ctrl.source_citation?.source && (
|
||||
<>
|
||||
<span className="text-gray-300">|</span>
|
||||
<span className="text-xs text-blue-600">
|
||||
{ctrl.source_citation.source}
|
||||
{ctrl.source_citation.article && ` ${ctrl.source_citation.article}`}
|
||||
{ctrl.source_citation.paragraph && ` ${ctrl.source_citation.paragraph}`}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
<span className="text-gray-300">|</span>
|
||||
<Clock className="w-3 h-3 text-gray-400" />
|
||||
<span className="text-xs text-gray-400" title={ctrl.created_at}>
|
||||
{ctrl.created_at ? new Date(ctrl.created_at).toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: '2-digit', hour: '2-digit', minute: '2-digit' }) : '–'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<ChevronRight className="w-4 h-4 text-gray-300 group-hover:text-purple-500 flex-shrink-0 mt-1 ml-4" />
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user