'use client' /** * AehnlicheDokumente - RAG-based similar documents panel * Shows documents with similar content based on vector similarity */ import { useState, useEffect } from 'react' import { Loader2, FileText, AlertCircle, RefreshCw, ExternalLink } from 'lucide-react' import type { AbiturDokument } from '@/lib/education/abitur-docs-types' import type { SimilarDocument } from '@/lib/education/abitur-archiv-types' import { FAECHER } from '@/lib/education/abitur-docs-types' interface AehnlicheDokumenteProps { documentId: string onSelectDocument: (doc: AbiturDokument) => void limit?: number } export function AehnlicheDokumente({ documentId, onSelectDocument, limit = 5 }: AehnlicheDokumenteProps) { const [similarDocs, setSimilarDocs] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) useEffect(() => { const fetchSimilarDocuments = async () => { if (!documentId) return setLoading(true) setError(null) try { const res = await fetch(`/api/education/abitur-archiv/similar?id=${documentId}&limit=${limit}`) if (!res.ok) { // Use mock data if endpoint not available setSimilarDocs(getMockSimilarDocuments(documentId)) return } const data = await res.json() setSimilarDocs(data.similar || []) } catch (err) { console.log('Similar docs fetch failed, using mock data') setSimilarDocs(getMockSimilarDocuments(documentId)) } finally { setLoading(false) } } fetchSimilarDocuments() }, [documentId, limit]) const handleRefresh = () => { setLoading(true) // Re-trigger the effect setSimilarDocs([]) setTimeout(() => { setSimilarDocs(getMockSimilarDocuments(documentId)) setLoading(false) }, 500) } if (loading) { return (

Suche aehnliche Dokumente...

) } if (error) { return (

{error}

) } if (similarDocs.length === 0) { return (

Keine aehnlichen Dokumente gefunden

Versuchen Sie eine andere Suche oder laden Sie mehr Dokumente hoch.

) } return (

Aehnliche Dokumente

{similarDocs.map((doc) => ( { // Convert SimilarDocument to AbiturDokument for selection // In production, this would fetch the full document onSelectDocument(doc as unknown as AbiturDokument) }} /> ))}

Basierend auf semantischer Aehnlichkeit (RAG)

) } function SimilarDocumentCard({ document, onSelect }: { document: SimilarDocument onSelect: () => void }) { const fachLabel = FAECHER.find(f => f.id === document.fach)?.label || document.fach const similarityPercent = Math.round(document.similarity_score * 100) return ( ) } // Mock data generator for development function getMockSimilarDocuments(documentId: string): SimilarDocument[] { // Generate consistent mock data based on document ID const idHash = documentId.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0) const faecher = ['deutsch', 'englisch'] const jahre = [2021, 2022, 2023, 2024, 2025] const niveaus: Array<'eA' | 'gA'> = ['eA', 'gA'] const nummern = ['I', 'II', 'III'] const typen: Array<'aufgabe' | 'erwartungshorizont'> = ['aufgabe', 'erwartungshorizont'] const docs: SimilarDocument[] = [] for (let i = 0; i < 5; i++) { const idx = (idHash + i) % (faecher.length * jahre.length * niveaus.length) docs.push({ id: `similar-${documentId}-${i}`, dateiname: `${jahre[idx % jahre.length]}_${faecher[idx % faecher.length]}_${niveaus[idx % niveaus.length]}_${nummern[idx % nummern.length]}.pdf`, similarity_score: 0.95 - (i * 0.1) + (Math.random() * 0.05), fach: faecher[idx % faecher.length], jahr: jahre[(idx + i) % jahre.length], niveau: niveaus[idx % niveaus.length], typ: typen[(idx + i) % typen.length], aufgaben_nummer: nummern[(idx + i) % nummern.length] }) } return docs.sort((a, b) => b.similarity_score - a.similarity_score) }