'use client' import { useState, useEffect, useCallback } from 'react' // Types interface BundeslandStats { bundesland: string name: string training_allowed: boolean document_count: number indexed_count: number last_crawled: string | null } interface CrawlerStatus { is_running: boolean current_source: string | null current_bundesland: string | null queue_length: number documents_crawled_today: number documents_indexed_today: number errors_today: number last_activity: string | null } interface ZeugnisStats { total_sources: number total_documents: number indexed_documents: number training_allowed_documents: number active_crawls: number per_bundesland: BundeslandStats[] } interface Document { id: string title: string url: string bundesland: string source_name: string training_allowed: boolean indexed_in_qdrant: boolean created_at: string } // Status badge component function StatusBadge({ allowed }: { allowed: boolean }) { return ( {allowed ? 'Training erlaubt' : 'Kein Training'} ) } // Stats card component function StatsCard({ title, value, subtitle, icon }: { title: string value: number | string subtitle?: string icon: string }) { return (
{icon}

{title}

{value}

{subtitle && (

{subtitle}

)}
) } export default function ZeugnisseCrawlerPage() { const [stats, setStats] = useState(null) const [bundeslandStats, setBundeslandStats] = useState([]) const [crawlerStatus, setCrawlerStatus] = useState(null) const [documents, setDocuments] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [selectedBundesland, setSelectedBundesland] = useState(null) const [isStarting, setIsStarting] = useState(false) const [isStopping, setIsStopping] = useState(false) // Fetch all data const fetchData = useCallback(async () => { try { const [statsRes, bundeslandRes, statusRes, docsRes] = await Promise.all([ fetch('/api/admin/zeugnisse-crawler?action=stats'), fetch('/api/admin/zeugnisse-crawler?action=bundesland-stats'), fetch('/api/admin/zeugnisse-crawler?action=status'), fetch(`/api/admin/zeugnisse-crawler?action=documents${selectedBundesland ? `&bundesland=${selectedBundesland}` : ''}`), ]) if (statsRes.ok) { const data = await statsRes.json() setStats(data) } if (bundeslandRes.ok) { const data = await bundeslandRes.json() setBundeslandStats(data) } if (statusRes.ok) { const data = await statusRes.json() setCrawlerStatus(data) } if (docsRes.ok) { const data = await docsRes.json() setDocuments(data) } setError(null) } catch (err) { setError('Fehler beim Laden der Daten') console.error(err) } finally { setLoading(false) } }, [selectedBundesland]) // Initial load and polling useEffect(() => { fetchData() const interval = setInterval(fetchData, 5000) return () => clearInterval(interval) }, [fetchData]) // Start crawler const startCrawler = async (bundesland?: string) => { setIsStarting(true) try { const res = await fetch('/api/admin/zeugnisse-crawler?action=start', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ bundesland }), }) if (!res.ok) { const data = await res.json() throw new Error(data.detail || 'Start fehlgeschlagen') } await fetchData() } catch (err: any) { setError(err.message) } finally { setIsStarting(false) } } // Stop crawler const stopCrawler = async () => { setIsStopping(true) try { const res = await fetch('/api/admin/zeugnisse-crawler?action=stop', { method: 'POST', }) if (!res.ok) { const data = await res.json() throw new Error(data.detail || 'Stop fehlgeschlagen') } await fetchData() } catch (err: any) { setError(err.message) } finally { setIsStopping(false) } } // Initialize sources const initializeSources = async () => { try { const res = await fetch('/api/admin/zeugnisse-crawler?action=init', { method: 'POST', }) if (!res.ok) { const data = await res.json() throw new Error(data.detail || 'Initialisierung fehlgeschlagen') } await fetchData() } catch (err: any) { setError(err.message) } } if (loading) { return (
{[1, 2, 3, 4].map(i => (
))}
) } return (
{/* Header */}

Zeugnisse Rights-Aware Crawler

Crawlt und indexiert Zeugnisverordnungen aller 16 Bundesländer

{crawlerStatus?.is_running ? ( ) : ( )}
{/* Error message */} {error && (
{error}
)} {/* Stats Cards */}
{/* Crawler Status */} {crawlerStatus?.is_running && (

Crawler läuft

{crawlerStatus.current_bundesland && (

Aktuell: {crawlerStatus.current_bundesland.toUpperCase()}

)}
)} {/* Bundesland Overview */}

Bundesländer-Übersicht

{bundeslandStats.map((bl) => ( ))}
Bundesland Training-Status Dokumente Indexiert Letzter Crawl Aktion
{bl.name} ({bl.bundesland.toUpperCase()})
{bl.document_count} {bl.indexed_count} {bl.last_crawled ? new Date(bl.last_crawled).toLocaleDateString('de-DE') : '-' }
{/* Documents List */}

Dokumente

{documents.length === 0 ? ( ) : ( documents.map((doc) => ( )) )}
Titel Bundesland Status Erstellt
Keine Dokumente vorhanden. Starten Sie den Crawler, um Dokumente zu sammeln.
{doc.bundesland?.toUpperCase()}
{doc.indexed_in_qdrant && ( Indexiert )}
{new Date(doc.created_at).toLocaleDateString('de-DE')}
{/* Rights Legend */}

Hinweis zu Training-Berechtigungen

Dokumente mit sind als amtliche Werke nach §5 UrhG gemeinfrei und können für das KI-Training verwendet werden. Dokumente ohne explizite Lizenzierung werden nicht für das Training verwendet.

) }