Website (14 monoliths split): - compliance/page.tsx (1,519 → 9), docs/audit (1,262 → 20) - quality (1,231 → 16), alerts (1,203 → 10), docs (1,202 → 11) - i18n.ts (1,173 → 8 language files) - unity-bridge (1,094 → 12), backlog (1,087 → 6) - training (1,066 → 8), rag (1,063 → 8) - Deleted index_original.ts (4,899 LOC dead backup) Studio-v2 (5 monoliths split): - meet/page.tsx (1,481 → 9), messages (1,166 → 9) - AlertsB2BContext.tsx (1,165 → 5 modules) - alerts-b2b/page.tsx (1,019 → 6), korrektur/archiv (1,001 → 6) All existing imports preserved. Zero new TypeScript errors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
124 lines
5.3 KiB
TypeScript
124 lines
5.3 KiB
TypeScript
'use client'
|
|
|
|
import type { Collection } from './types'
|
|
|
|
function CollectionCard({ collection }: { collection: Collection }) {
|
|
const statusColors = {
|
|
ready: 'bg-green-100 text-green-800',
|
|
indexing: 'bg-yellow-100 text-yellow-800',
|
|
empty: 'bg-slate-100 text-slate-800',
|
|
}
|
|
|
|
const statusLabels = {
|
|
ready: 'Bereit',
|
|
indexing: 'Indexierung...',
|
|
empty: 'Leer',
|
|
}
|
|
|
|
return (
|
|
<div className="bg-white rounded-lg border border-slate-200 p-6 hover:shadow-md transition-shadow">
|
|
<div className="flex items-start justify-between">
|
|
<div className="flex items-center gap-4">
|
|
<div className="w-12 h-12 bg-primary-100 rounded-lg flex items-center justify-center">
|
|
<svg className="w-6 h-6 text-primary-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<h3 className="text-lg font-semibold text-slate-900">{collection.displayName}</h3>
|
|
<p className="text-sm text-slate-500 font-mono">{collection.name}</p>
|
|
</div>
|
|
</div>
|
|
<span className={`px-3 py-1 rounded-full text-xs font-medium ${statusColors[collection.status]}`}>
|
|
{statusLabels[collection.status]}
|
|
</span>
|
|
</div>
|
|
|
|
<div className="mt-4 grid grid-cols-2 md:grid-cols-4 gap-4">
|
|
<div>
|
|
<p className="text-xs text-slate-500 uppercase tracking-wider">Chunks</p>
|
|
<p className="text-lg font-semibold text-slate-900">{collection.chunkCount.toLocaleString()}</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-xs text-slate-500 uppercase tracking-wider">Jahre</p>
|
|
<p className="text-lg font-semibold text-slate-900">
|
|
{collection.years.length > 0 ? `${Math.min(...collection.years)}-${Math.max(...collection.years)}` : '-'}
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-xs text-slate-500 uppercase tracking-wider">Fächer</p>
|
|
<p className="text-lg font-semibold text-slate-900">{collection.subjects.length}</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-xs text-slate-500 uppercase tracking-wider">Bundesland</p>
|
|
<p className="text-lg font-semibold text-slate-900">{collection.bundesland}</p>
|
|
</div>
|
|
</div>
|
|
|
|
{collection.subjects.length > 0 && (
|
|
<div className="mt-4 flex flex-wrap gap-2">
|
|
{collection.subjects.slice(0, 8).map((subject) => (
|
|
<span key={subject} className="px-2 py-1 bg-slate-100 text-slate-700 text-xs rounded-md">{subject}</span>
|
|
))}
|
|
{collection.subjects.length > 8 && (
|
|
<span className="px-2 py-1 bg-slate-100 text-slate-500 text-xs rounded-md">+{collection.subjects.length - 8} weitere</span>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export function CollectionsTab({
|
|
collections,
|
|
loading,
|
|
onRefresh,
|
|
}: {
|
|
collections: Collection[]
|
|
loading: boolean
|
|
onRefresh: () => void
|
|
}) {
|
|
return (
|
|
<div className="space-y-6">
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h2 className="text-lg font-semibold text-slate-900">RAG Sammlungen</h2>
|
|
<p className="text-sm text-slate-500">Verwaltung der indexierten Dokumentensammlungen</p>
|
|
</div>
|
|
<button onClick={onRefresh} className="px-4 py-2 text-sm font-medium text-slate-700 bg-white border border-slate-300 rounded-lg hover:bg-slate-50">
|
|
Aktualisieren
|
|
</button>
|
|
</div>
|
|
|
|
{loading && (
|
|
<div className="flex items-center justify-center py-12">
|
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary-600"></div>
|
|
</div>
|
|
)}
|
|
|
|
{!loading && (
|
|
<div className="grid gap-4">
|
|
{collections.length === 0 ? (
|
|
<div className="bg-slate-50 rounded-lg p-8 text-center">
|
|
<svg className="w-12 h-12 text-slate-400 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4" />
|
|
</svg>
|
|
<h3 className="text-lg font-medium text-slate-900 mb-2">Keine Sammlungen vorhanden</h3>
|
|
<p className="text-slate-500 mb-4">Starten Sie die Ingestion oder laden Sie Dokumente hoch.</p>
|
|
</div>
|
|
) : (
|
|
collections.map((col) => <CollectionCard key={col.name} collection={col} />)
|
|
)}
|
|
|
|
<div className="border-2 border-dashed border-slate-300 rounded-lg p-6 text-center hover:border-slate-400 transition-colors cursor-pointer">
|
|
<svg className="w-8 h-8 text-slate-400 mx-auto mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
|
|
</svg>
|
|
<span className="text-sm font-medium text-slate-600">Neue Sammlung (demnächst)</span>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|