Each page.tsx exceeded the 500-LOC hard cap. Extracted components and hooks into colocated _components/ and _hooks/ directories; page.tsx is now a thin orchestrator. - controls/page.tsx: 944 → 180 LOC; extracted ControlCard, AddControlForm, LoadingSkeleton, TransitionErrorBanner, StatsCards, FilterBar, RAGPanel into _components/ and useControlsData, useRAGSuggestions into _hooks/; types into _types.ts - training/page.tsx: 780 → 288 LOC; extracted ContentTab (inline content generator tab) into _components/ContentTab.tsx - control-provenance/page.tsx: 739 → 122 LOC; extracted MarkdownRenderer, UsageBadge, PermBadge, LicenseMatrix, SourceRegistry into _components/; PROVENANCE_SECTIONS static data into _data/provenance-sections.ts - iace/[projectId]/verification/page.tsx: 673 → 196 LOC; extracted StatusBadge, VerificationForm, CompleteModal, SuggestEvidenceModal, VerificationTable into _components/ Zero behavior changes; logic relocated verbatim. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
58 lines
2.1 KiB
TypeScript
58 lines
2.1 KiB
TypeScript
import { ExternalLink } from 'lucide-react'
|
|
import { PermBadge } from './UsageBadge'
|
|
|
|
interface SourceInfo {
|
|
source_id: string
|
|
title: string
|
|
publisher: string
|
|
url: string | null
|
|
version_label: string | null
|
|
language: string
|
|
license_id: string
|
|
license_name: string
|
|
commercial_use: string
|
|
allowed_analysis: boolean
|
|
allowed_store_excerpt: boolean
|
|
allowed_ship_embeddings: boolean
|
|
allowed_ship_in_product: boolean
|
|
vault_retention_days: number
|
|
vault_access_tier: string
|
|
}
|
|
|
|
export function SourceRegistry({ sources, loading }: { sources: SourceInfo[]; loading: boolean }) {
|
|
return (
|
|
<div>
|
|
<h2 className="text-xl font-bold text-gray-900 mb-4">Quellenregister</h2>
|
|
<p className="text-sm text-gray-600 mb-4">Alle registrierten Quellen mit ihren Berechtigungen.</p>
|
|
{loading ? (
|
|
<div className="animate-pulse h-32 bg-gray-100 rounded" />
|
|
) : (
|
|
<div className="space-y-3">
|
|
{sources.map(src => (
|
|
<div key={src.source_id} className="bg-white border border-gray-200 rounded-lg p-4">
|
|
<div className="flex items-start justify-between mb-2">
|
|
<div>
|
|
<h3 className="text-sm font-medium text-gray-900">{src.title}</h3>
|
|
<p className="text-xs text-gray-500">{src.publisher} — {src.license_name}</p>
|
|
</div>
|
|
{src.url && (
|
|
<a href={src.url} target="_blank" rel="noopener noreferrer" className="flex items-center gap-1 text-xs text-blue-600 hover:text-blue-800">
|
|
<ExternalLink className="w-3 h-3" />
|
|
Quelle
|
|
</a>
|
|
)}
|
|
</div>
|
|
<div className="flex items-center gap-3 mt-2">
|
|
<PermBadge label="Analyse" allowed={src.allowed_analysis} />
|
|
<PermBadge label="Excerpt" allowed={src.allowed_store_excerpt} />
|
|
<PermBadge label="Embeddings" allowed={src.allowed_ship_embeddings} />
|
|
<PermBadge label="Produkt" allowed={src.allowed_ship_in_product} />
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|