'use client' import { Shield, Search, ChevronRight, ChevronLeft, Filter, Lock, BookOpen, Plus, Zap, BarChart3, ListChecks, Trash2, ChevronsLeft, ChevronsRight, ArrowUpDown, Clock, RefreshCw, } from 'lucide-react' import { CanonicalControl, Framework, SeverityBadge, StateBadge, LicenseRuleBadge, VerificationMethodBadge, CategoryBadge, EvidenceTypeBadge, TargetAudienceBadge, GenerationStrategyBadge, ObligationTypeBadge, VERIFICATION_METHODS, CATEGORY_OPTIONS, EVIDENCE_TYPE_OPTIONS, } from './helpers' import { ControlsMeta } from './useControlLibraryState' import { GeneratorModal } from './GeneratorModal' interface ControlListViewProps { frameworks: Framework[] controls: CanonicalControl[] totalCount: number meta: ControlsMeta | null loading: boolean reviewCount: number bulkProcessing: boolean showStats: boolean processedStats: Array> showGenerator: boolean currentPage: number totalPages: number sortBy: 'id' | 'newest' | 'oldest' | 'source' // Filter values searchQuery: string severityFilter: string domainFilter: string stateFilter: string verificationFilter: string categoryFilter: string evidenceTypeFilter: string audienceFilter: string sourceFilter: string typeFilter: string hideDuplicates: boolean // Setters setSearchQuery: (v: string) => void setSeverityFilter: (v: string) => void setDomainFilter: (v: string) => void setStateFilter: (v: string) => void setVerificationFilter: (v: string) => void setCategoryFilter: (v: string) => void setEvidenceTypeFilter: (v: string) => void setAudienceFilter: (v: string) => void setSourceFilter: (v: string) => void setTypeFilter: (v: string) => void setHideDuplicates: (v: boolean) => void setSortBy: (v: 'id' | 'newest' | 'oldest' | 'source') => void setShowStats: (v: boolean) => void setShowGenerator: (v: boolean) => void setCurrentPage: (v: number | ((p: number) => number)) => void // Actions onSelectControl: (c: CanonicalControl) => void onCreateMode: () => void onEnterReview: () => void onBulkReject: (state: string) => void onRefresh: () => void onLoadStats: () => void onFullReload: () => void } export function ControlListView({ frameworks, controls, totalCount, meta, loading, reviewCount, bulkProcessing, showStats, processedStats, showGenerator, currentPage, totalPages, sortBy, searchQuery, severityFilter, domainFilter, stateFilter, verificationFilter, categoryFilter, evidenceTypeFilter, audienceFilter, sourceFilter, typeFilter, hideDuplicates, setSearchQuery, setSeverityFilter, setDomainFilter, setStateFilter, setVerificationFilter, setCategoryFilter, setEvidenceTypeFilter, setAudienceFilter, setSourceFilter, setTypeFilter, setHideDuplicates, setSortBy, setShowStats, setShowGenerator, setCurrentPage, onSelectControl, onCreateMode, onEnterReview, onBulkReject, onRefresh, onLoadStats, onFullReload, }: ControlListViewProps) { const debouncedSearch = searchQuery // used for empty state message return (
{/* Header */}

Canonical Control Library

{meta?.total ?? totalCount} Security Controls

{reviewCount > 0 && ( <> )}
{frameworks.length > 0 && (
{frameworks[0]?.name} v{frameworks[0]?.version} {frameworks[0]?.description}
)} {/* Filters */}
setSearchQuery(e.target.value)} className="w-full pl-9 pr-4 py-2 text-sm border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500" />
|
{showStats && processedStats.length > 0 && (

Verarbeitungsfortschritt

{processedStats.map((s, i) => (
{String(s.collection)}
{String(s.processed_chunks)} verarbeitet {String(s.direct_adopted)} direkt {String(s.llm_reformed)} reformuliert
))}
)}
{showGenerator && setShowGenerator(false)} onComplete={onFullReload} />} {/* Pagination Header */}
{totalCount} Controls gefunden {totalCount !== (meta?.total ?? totalCount) && ` (von ${meta?.total} gesamt)`} {loading && Lade...} {stateFilter && ['needs_review', 'too_close', 'duplicate'].includes(stateFilter) && totalCount > 0 && ( )}
Seite {currentPage} von {totalPages}
{/* Control List */}
{controls.map((ctrl, idx) => { const prevSource = idx > 0 ? (controls[idx - 1].source_citation?.source || 'Ohne Quelle') : null const curSource = ctrl.source_citation?.source || 'Ohne Quelle' const showSourceHeader = sortBy === 'source' && curSource !== prevSource return (
{showSourceHeader && (
{curSource}
)}
) })} {controls.length === 0 && !loading && (
{totalCount === 0 && !debouncedSearch && !severityFilter && !domainFilter ? 'Noch keine Controls vorhanden. Klicke auf "Neues Control" um zu starten.' : 'Keine Controls gefunden.'}
)}
{/* Pagination Controls */} {totalPages > 1 && (
{Array.from({ length: totalPages }, (_, i) => i + 1) .filter(p => p === 1 || p === totalPages || Math.abs(p - currentPage) <= 2) .reduce<(number | 'dots')[]>((acc, p, i, arr) => { if (i > 0 && p - (arr[i - 1] as number) > 1) acc.push('dots') acc.push(p) return acc }, []) .map((p, i) => p === 'dots' ? ( ... ) : ( ) )}
)}
) }