website (17 pages + 3 components): - multiplayer/wizard, middleware/wizard+test-wizard, communication - builds/wizard, staff-search, voice, sbom/wizard - foerderantrag, mail/tasks, tools/communication, sbom - compliance/evidence, uni-crawler, brandbook (already done) - CollectionsTab, IngestionTab, RiskHeatmap backend-lehrer (5 files): - letters_api (641 → 2), certificates_api (636 → 2) - alerts_agent/db/models (636 → 3) - llm_gateway/communication_service (614 → 2) - game/database already done in prior batch klausur-service (2 files): - hybrid_vocab_extractor (664 → 2) - klausur-service/frontend: api.ts (620 → 3), EHUploadWizard (591 → 2) voice-service (3 files): - bqas/rag_judge (618 → 3), runner (529 → 2) - enhanced_task_orchestrator (519 → 2) studio-v2 (6 files): - korrektur/[klausurId] (578 → 4), fairness (569 → 2) - AlertsWizard (552 → 2), OnboardingWizard (513 → 2) - korrektur/api.ts (506 → 3), geo-lernwelt (501 → 2) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
242 lines
7.6 KiB
TypeScript
242 lines
7.6 KiB
TypeScript
'use client'
|
|
|
|
/**
|
|
* Communication Tool - Lehrer-Eltern-Kommunikation
|
|
*
|
|
* KI-gestuetzte Unterstuetzung fuer professionelle, rechtlich fundierte
|
|
* und empathische Elternkommunikation basierend auf den Prinzipien
|
|
* der gewaltfreien Kommunikation (GFK).
|
|
*/
|
|
|
|
import { useState, useEffect } from 'react'
|
|
import {
|
|
API_BASE,
|
|
Option,
|
|
LegalReference,
|
|
GFKPrinciple,
|
|
ValidationResult,
|
|
} from './_components/types'
|
|
import { InputForm } from './_components/InputForm'
|
|
import { OutputArea } from './_components/OutputArea'
|
|
|
|
export default function CommunicationToolPage() {
|
|
// Form state
|
|
const [communicationType, setCommunicationType] = useState('behavior')
|
|
const [tone, setTone] = useState('professional')
|
|
const [state, setState] = useState('NRW')
|
|
const [studentName, setStudentName] = useState('')
|
|
const [parentName, setParentName] = useState('')
|
|
const [situation, setSituation] = useState('')
|
|
const [additionalInfo, setAdditionalInfo] = useState('')
|
|
|
|
// Options
|
|
const [types, setTypes] = useState<Option[]>([])
|
|
const [tones, setTones] = useState<Option[]>([])
|
|
const [states, setStates] = useState<Option[]>([])
|
|
|
|
// Result state
|
|
const [generatedMessage, setGeneratedMessage] = useState('')
|
|
const [subject, setSubject] = useState('')
|
|
const [validation, setValidation] = useState<ValidationResult | null>(null)
|
|
const [legalRefs, setLegalRefs] = useState<LegalReference[]>([])
|
|
const [gfkPrinciples, setGfkPrinciples] = useState<GFKPrinciple[]>([])
|
|
|
|
// UI state
|
|
const [loading, setLoading] = useState(false)
|
|
const [error, setError] = useState<string | null>(null)
|
|
|
|
// Fetch options on mount
|
|
useEffect(() => {
|
|
const fetchOptions = async () => {
|
|
try {
|
|
const [typesRes, tonesRes, statesRes, gfkRes] = await Promise.all([
|
|
fetch(`${API_BASE}/v1/communication/types`),
|
|
fetch(`${API_BASE}/v1/communication/tones`),
|
|
fetch(`${API_BASE}/v1/communication/states`),
|
|
fetch(`${API_BASE}/v1/communication/gfk-principles`),
|
|
])
|
|
|
|
if (typesRes.ok) setTypes(await typesRes.json())
|
|
if (tonesRes.ok) setTones(await tonesRes.json())
|
|
if (statesRes.ok) setStates(await statesRes.json())
|
|
if (gfkRes.ok) setGfkPrinciples(await gfkRes.json())
|
|
} catch (e) {
|
|
console.error('Fehler beim Laden der Optionen:', e)
|
|
// Fallback-Werte
|
|
setTypes([
|
|
{ value: 'behavior', label: 'Verhalten/Disziplin' },
|
|
{ value: 'academic', label: 'Schulleistungen' },
|
|
{ value: 'attendance', label: 'Fehlzeiten' },
|
|
{ value: 'meeting_invite', label: 'Einladung zum Gespraech' },
|
|
{ value: 'positive_feedback', label: 'Positives Feedback' },
|
|
{ value: 'concern', label: 'Bedenken aeussern' },
|
|
{ value: 'conflict', label: 'Konfliktloesung' },
|
|
])
|
|
setTones([
|
|
{ value: 'professional', label: 'Professionell-freundlich' },
|
|
{ value: 'formal', label: 'Sehr foermlich' },
|
|
{ value: 'warm', label: 'Warmherzig' },
|
|
{ value: 'concerned', label: 'Besorgt' },
|
|
])
|
|
setStates([
|
|
{ value: 'NRW', label: 'Nordrhein-Westfalen' },
|
|
{ value: 'BY', label: 'Bayern' },
|
|
{ value: 'BW', label: 'Baden-Wuerttemberg' },
|
|
])
|
|
}
|
|
}
|
|
fetchOptions()
|
|
}, [])
|
|
|
|
// Generate message
|
|
const handleGenerate = async () => {
|
|
if (!studentName || !parentName || !situation) {
|
|
setError('Bitte fuellen Sie alle Pflichtfelder aus.')
|
|
return
|
|
}
|
|
|
|
setLoading(true)
|
|
setError(null)
|
|
|
|
try {
|
|
const res = await fetch(`${API_BASE}/v1/communication/generate`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
communication_type: communicationType,
|
|
tone,
|
|
state,
|
|
student_name: studentName,
|
|
parent_name: parentName,
|
|
situation,
|
|
additional_info: additionalInfo || undefined,
|
|
}),
|
|
})
|
|
|
|
if (!res.ok) {
|
|
throw new Error(`Server-Fehler: ${res.status}`)
|
|
}
|
|
|
|
const data = await res.json()
|
|
setGeneratedMessage(data.message)
|
|
setSubject(data.subject)
|
|
setValidation(data.validation)
|
|
setLegalRefs(data.legal_references || [])
|
|
} catch (e) {
|
|
setError(`Fehler bei der Generierung: ${e instanceof Error ? e.message : 'Unbekannter Fehler'}`)
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
// Improve existing text
|
|
const handleImprove = async () => {
|
|
if (!generatedMessage) return
|
|
|
|
setLoading(true)
|
|
setError(null)
|
|
|
|
try {
|
|
const res = await fetch(`${API_BASE}/v1/communication/improve`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ text: generatedMessage }),
|
|
})
|
|
|
|
if (!res.ok) {
|
|
throw new Error(`Server-Fehler: ${res.status}`)
|
|
}
|
|
|
|
const data = await res.json()
|
|
if (data.was_improved) {
|
|
setGeneratedMessage(data.improved_text)
|
|
// Re-validate
|
|
const valRes = await fetch(`${API_BASE}/v1/communication/validate`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ text: data.improved_text }),
|
|
})
|
|
if (valRes.ok) {
|
|
setValidation(await valRes.json())
|
|
}
|
|
}
|
|
} catch (e) {
|
|
setError(`Fehler bei der Verbesserung: ${e instanceof Error ? e.message : 'Unbekannter Fehler'}`)
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
// Copy to clipboard
|
|
const handleCopy = async () => {
|
|
try {
|
|
await navigator.clipboard.writeText(generatedMessage)
|
|
alert('In die Zwischenablage kopiert!')
|
|
} catch (e) {
|
|
console.error('Kopieren fehlgeschlagen:', e)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="min-h-screen bg-gray-50 py-8">
|
|
<div className="max-w-6xl mx-auto px-4">
|
|
{/* Header */}
|
|
<div className="mb-8">
|
|
<h1 className="text-3xl font-bold text-gray-900 mb-2">
|
|
Kommunikationsassistent
|
|
</h1>
|
|
<p className="text-gray-600">
|
|
KI-gestuetzte Unterstuetzung fuer professionelle Elternkommunikation
|
|
nach den Prinzipien der gewaltfreien Kommunikation (GFK)
|
|
</p>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
<InputForm
|
|
communicationType={communicationType}
|
|
setCommunicationType={setCommunicationType}
|
|
tone={tone}
|
|
setTone={setTone}
|
|
state={state}
|
|
setState={setState}
|
|
studentName={studentName}
|
|
setStudentName={setStudentName}
|
|
parentName={parentName}
|
|
setParentName={setParentName}
|
|
situation={situation}
|
|
setSituation={setSituation}
|
|
additionalInfo={additionalInfo}
|
|
setAdditionalInfo={setAdditionalInfo}
|
|
types={types}
|
|
tones={tones}
|
|
states={states}
|
|
gfkPrinciples={gfkPrinciples}
|
|
error={error}
|
|
loading={loading}
|
|
onGenerate={handleGenerate}
|
|
/>
|
|
|
|
<OutputArea
|
|
generatedMessage={generatedMessage}
|
|
setGeneratedMessage={setGeneratedMessage}
|
|
subject={subject}
|
|
validation={validation}
|
|
legalRefs={legalRefs}
|
|
loading={loading}
|
|
onImprove={handleImprove}
|
|
onCopy={handleCopy}
|
|
/>
|
|
</div>
|
|
|
|
{/* Footer */}
|
|
<div className="mt-8 text-center text-sm text-gray-500">
|
|
<p>
|
|
Hinweis: Die generierten Texte sind Vorschlaege und sollten vor dem
|
|
Versand ueberprueft und ggf. angepasst werden.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|