'use client' /** * EHSuggestionPanel * * Panel for displaying Erwartungshorizont-based suggestions. * Uses RAG to find relevant passages from the linked EH. */ import { useState, useCallback } from 'react' import type { AnnotationType } from '../types' import { ANNOTATION_COLORS } from '../types' interface EHSuggestion { id: string eh_id: string eh_title: string text: string score: number criterion: string source_chunk_index: number decrypted: boolean } interface EHSuggestionPanelProps { studentId: string klausurId: string hasEH: boolean apiBase: string onInsertSuggestion?: (text: string, criterion: string) => void } const CRITERIA = [ { id: 'allgemein', label: 'Alle Kriterien' }, { id: 'inhalt', label: 'Inhalt', color: '#16a34a' }, { id: 'struktur', label: 'Struktur', color: '#9333ea' }, { id: 'stil', label: 'Stil', color: '#ea580c' }, ] export default function EHSuggestionPanel({ studentId, klausurId, hasEH, apiBase, onInsertSuggestion, }: EHSuggestionPanelProps) { const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const [suggestions, setSuggestions] = useState([]) const [selectedCriterion, setSelectedCriterion] = useState('allgemein') const [passphrase, setPassphrase] = useState('') const [needsPassphrase, setNeedsPassphrase] = useState(false) const [queryPreview, setQueryPreview] = useState(null) const fetchSuggestions = useCallback(async () => { try { setLoading(true) setError(null) const res = await fetch(`${apiBase}/api/v1/students/${studentId}/eh-suggestions`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ criterion: selectedCriterion === 'allgemein' ? null : selectedCriterion, passphrase: passphrase || null, limit: 5, }), }) if (!res.ok) { const data = await res.json() throw new Error(data.detail || 'Fehler beim Laden der Vorschlaege') } const data = await res.json() if (data.needs_passphrase) { setNeedsPassphrase(true) setSuggestions([]) setError(data.message) } else { setNeedsPassphrase(false) setSuggestions(data.suggestions || []) setQueryPreview(data.query_preview || null) if (data.suggestions?.length === 0) { setError(data.message || 'Keine passenden Vorschlaege gefunden') } } } catch (err) { console.error('Failed to fetch EH suggestions:', err) setError(err instanceof Error ? err.message : 'Unbekannter Fehler') } finally { setLoading(false) } }, [apiBase, studentId, selectedCriterion, passphrase]) const handleInsert = (suggestion: EHSuggestion) => { if (onInsertSuggestion) { onInsertSuggestion(suggestion.text, suggestion.criterion) } } if (!hasEH) { return (

Kein Erwartungshorizont verknuepft

Laden Sie einen EH in der RAG-Verwaltung hoch

Zur RAG-Verwaltung
) } return (
{/* Criterion selector */}
{CRITERIA.map((c) => ( ))}
{/* Passphrase input (if needed) */} {needsPassphrase && (
setPassphrase(e.target.value)} placeholder="Passphrase eingeben..." className="flex-1 px-2 py-1 text-sm border border-yellow-300 rounded focus:ring-2 focus:ring-yellow-500" />
)} {/* Fetch button */}
{/* Query preview */} {queryPreview && (
Basierend auf:
"{queryPreview}"
)} {/* Error message */} {error && !needsPassphrase && (

{error}

)} {/* Suggestions list */}
{suggestions.length === 0 && !loading && !error && (
Klicken Sie auf "EH-Vorschlaege laden" um passende Stellen aus dem Erwartungshorizont zu finden.
)} {suggestions.map((suggestion, idx) => (
{/* Header */}
#{idx + 1} {suggestion.criterion} Relevanz: {Math.round(suggestion.score * 100)}%
{!suggestion.decrypted && ( Verschluesselt )}
{/* Content */}

{suggestion.text}

{/* Source */}
Quelle: {suggestion.eh_title} {onInsertSuggestion && suggestion.decrypted && ( )}
))}
) }