Files
breakpilot-lehrer/website/app/admin/rag/page.tsx
Benjamin Admin 0b37c5e692 [split-required] Split website + studio-v2 monoliths (Phase 3 continued)
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>
2026-04-24 17:52:36 +02:00

124 lines
4.9 KiB
TypeScript

'use client'
import { useState, useEffect, useCallback } from 'react'
import AdminLayout from '@/components/admin/AdminLayout'
import type { TabId, Collection, IngestionStatus } from './_components/types'
import { API_BASE } from './_components/types'
import { tabs } from './_components/tabs'
import { CollectionsTab } from './_components/CollectionsTab'
import { UploadTab } from './_components/UploadTab'
import { IngestionTab } from './_components/IngestionTab'
import { SearchTab } from './_components/SearchTab'
import { MetricsTab } from './_components/MetricsTab'
export default function RAGAdminPage() {
const [activeTab, setActiveTab] = useState<TabId>('collections')
const [collections, setCollections] = useState<Collection[]>([])
const [ingestionStatus, setIngestionStatus] = useState<IngestionStatus | null>(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
const fetchData = useCallback(async () => {
try {
setLoading(true)
const collectionsRes = await fetch(`${API_BASE}/api/v1/admin/nibis/collections`)
const statsRes = await fetch(`${API_BASE}/api/v1/admin/nibis/stats`)
const statusRes = await fetch(`${API_BASE}/api/v1/admin/nibis/status`)
if (collectionsRes.ok && statsRes.ok && statusRes.ok) {
const collectionsData = await collectionsRes.json()
const statsData = await statsRes.json()
const statusData = await statusRes.json()
const cols: Collection[] = []
const nibisCollection = collectionsData.collections?.find(
(c: { name: string }) => c.name === 'bp_nibis_eh'
)
if (nibisCollection || statsData.indexed) {
cols.push({
name: 'bp_nibis_eh',
displayName: 'Niedersachsen - Klausurkorrektur',
bundesland: 'NI',
useCase: 'klausur',
documentCount: statsData.total_chunks || nibisCollection?.points_count || 0,
chunkCount: statsData.total_chunks || nibisCollection?.points_count || 0,
years: statsData.years || [],
subjects: statsData.subjects || [],
status: statusData.running ? 'indexing' : (statsData.indexed ? 'ready' : 'empty'),
})
}
setCollections(cols)
setIngestionStatus({
running: statusData.running,
lastRun: statusData.last_run,
documentsIndexed: statusData.documents_indexed,
chunksCreated: statusData.chunks_created,
errors: statusData.errors || [],
})
}
setError(null)
} catch (err) {
console.error('Failed to fetch data:', err)
setError('Verbindung zum Klausur-Service fehlgeschlagen')
} finally {
setLoading(false)
}
}, [])
useEffect(() => {
fetchData()
const interval = setInterval(() => {
if (ingestionStatus?.running) {
fetchData()
}
}, 5000)
return () => clearInterval(interval)
}, [fetchData, ingestionStatus?.running])
return (
<AdminLayout title="Daten & RAG" description="Training Data Management und RAG-Sammlungen">
{/* Error Banner */}
{error && (
<div className="mb-6 bg-red-50 border border-red-200 rounded-lg p-4 flex items-center gap-3">
<svg className="w-5 h-5 text-red-500 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span className="text-red-700">{error}</span>
<button onClick={fetchData} className="ml-auto text-red-600 hover:text-red-800 text-sm font-medium">Erneut versuchen</button>
</div>
)}
{/* Tab Navigation */}
<div className="border-b border-slate-200 mb-6">
<nav className="-mb-px flex space-x-8">
{tabs.map((tab) => (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id)}
className={`flex items-center gap-2 py-4 px-1 border-b-2 font-medium text-sm transition-colors ${
activeTab === tab.id
? 'border-primary-500 text-primary-600'
: 'border-transparent text-slate-500 hover:text-slate-700 hover:border-slate-300'
}`}
>
{tab.icon}
{tab.name}
</button>
))}
</nav>
</div>
{/* Tab Content */}
{activeTab === 'collections' && <CollectionsTab collections={collections} loading={loading} onRefresh={fetchData} />}
{activeTab === 'upload' && <UploadTab onUploadComplete={fetchData} />}
{activeTab === 'ingestion' && <IngestionTab status={ingestionStatus} onRefresh={fetchData} />}
{activeTab === 'search' && <SearchTab collections={collections} />}
{activeTab === 'metrics' && <MetricsTab collections={collections} />}
</AdminLayout>
)
}