[split-required] Split remaining 500-680 LOC files (final batch)
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>
This commit is contained in:
210
website/app/tools/communication/_components/InputForm.tsx
Normal file
210
website/app/tools/communication/_components/InputForm.tsx
Normal file
@@ -0,0 +1,210 @@
|
||||
import { useState } from 'react'
|
||||
import type { Option, GFKPrinciple } from './types'
|
||||
|
||||
interface InputFormProps {
|
||||
communicationType: string
|
||||
setCommunicationType: (v: string) => void
|
||||
tone: string
|
||||
setTone: (v: string) => void
|
||||
state: string
|
||||
setState: (v: string) => void
|
||||
studentName: string
|
||||
setStudentName: (v: string) => void
|
||||
parentName: string
|
||||
setParentName: (v: string) => void
|
||||
situation: string
|
||||
setSituation: (v: string) => void
|
||||
additionalInfo: string
|
||||
setAdditionalInfo: (v: string) => void
|
||||
types: Option[]
|
||||
tones: Option[]
|
||||
states: Option[]
|
||||
gfkPrinciples: GFKPrinciple[]
|
||||
error: string | null
|
||||
loading: boolean
|
||||
onGenerate: () => void
|
||||
}
|
||||
|
||||
export function InputForm({
|
||||
communicationType, setCommunicationType,
|
||||
tone, setTone,
|
||||
state, setState,
|
||||
studentName, setStudentName,
|
||||
parentName, setParentName,
|
||||
situation, setSituation,
|
||||
additionalInfo, setAdditionalInfo,
|
||||
types, tones, states,
|
||||
gfkPrinciples,
|
||||
error, loading,
|
||||
onGenerate,
|
||||
}: InputFormProps) {
|
||||
return (
|
||||
<div className="lg:col-span-1 space-y-6">
|
||||
<div className="bg-white rounded-lg shadow p-6">
|
||||
<h2 className="text-lg font-semibold mb-4">Nachricht erstellen</h2>
|
||||
|
||||
{/* Communication Type */}
|
||||
<div className="mb-4">
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Art der Kommunikation *
|
||||
</label>
|
||||
<select
|
||||
value={communicationType}
|
||||
onChange={(e) => setCommunicationType(e.target.value)}
|
||||
className="w-full border rounded-md p-2"
|
||||
>
|
||||
{types.map((t) => (
|
||||
<option key={t.value} value={t.value}>
|
||||
{t.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Tone */}
|
||||
<div className="mb-4">
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Tonalitaet
|
||||
</label>
|
||||
<select
|
||||
value={tone}
|
||||
onChange={(e) => setTone(e.target.value)}
|
||||
className="w-full border rounded-md p-2"
|
||||
>
|
||||
{tones.map((t) => (
|
||||
<option key={t.value} value={t.value}>
|
||||
{t.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* State */}
|
||||
<div className="mb-4">
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Bundesland (fuer rechtliche Referenzen)
|
||||
</label>
|
||||
<select
|
||||
value={state}
|
||||
onChange={(e) => setState(e.target.value)}
|
||||
className="w-full border rounded-md p-2"
|
||||
>
|
||||
{states.map((s) => (
|
||||
<option key={s.value} value={s.value}>
|
||||
{s.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Student Name */}
|
||||
<div className="mb-4">
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Name des Schuelers/der Schuelerin *
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={studentName}
|
||||
onChange={(e) => setStudentName(e.target.value)}
|
||||
placeholder="z.B. Max"
|
||||
className="w-full border rounded-md p-2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Parent Name */}
|
||||
<div className="mb-4">
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Anrede der Eltern *
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={parentName}
|
||||
onChange={(e) => setParentName(e.target.value)}
|
||||
placeholder="z.B. Frau Mueller"
|
||||
className="w-full border rounded-md p-2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Situation */}
|
||||
<div className="mb-4">
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Beschreibung der Situation *
|
||||
</label>
|
||||
<textarea
|
||||
value={situation}
|
||||
onChange={(e) => setSituation(e.target.value)}
|
||||
placeholder="Beschreiben Sie die Situation sachlich und konkret..."
|
||||
rows={4}
|
||||
className="w-full border rounded-md p-2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Additional Info */}
|
||||
<div className="mb-4">
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Zusaetzliche Informationen (optional)
|
||||
</label>
|
||||
<textarea
|
||||
value={additionalInfo}
|
||||
onChange={(e) => setAdditionalInfo(e.target.value)}
|
||||
placeholder="Besondere Umstaende, gewuenschte Termine, etc."
|
||||
rows={2}
|
||||
className="w-full border rounded-md p-2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Error */}
|
||||
{error && (
|
||||
<div className="mb-4 p-3 bg-red-50 text-red-700 rounded-md text-sm">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Generate Button */}
|
||||
<button
|
||||
onClick={onGenerate}
|
||||
disabled={loading}
|
||||
className="w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 disabled:opacity-50"
|
||||
>
|
||||
{loading ? 'Wird generiert...' : 'Nachricht generieren'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* GFK Info Toggle */}
|
||||
<GFKInfoPanel gfkPrinciples={gfkPrinciples} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function GFKInfoPanel({ gfkPrinciples }: { gfkPrinciples: GFKPrinciple[] }) {
|
||||
const [showGFKInfo, setShowGFKInfo] = useState(false)
|
||||
|
||||
return (
|
||||
<div className="bg-white rounded-lg shadow p-6">
|
||||
<button
|
||||
onClick={() => setShowGFKInfo(!showGFKInfo)}
|
||||
className="flex items-center justify-between w-full text-left"
|
||||
>
|
||||
<span className="font-semibold">Was ist GFK?</span>
|
||||
<span>{showGFKInfo ? '-' : '+'}</span>
|
||||
</button>
|
||||
{showGFKInfo && (
|
||||
<div className="mt-4 space-y-4">
|
||||
<p className="text-sm text-gray-600">
|
||||
Die Gewaltfreie Kommunikation (GFK) nach Marshall Rosenberg
|
||||
ist ein Kommunikationsmodell, das auf vier Schritten basiert:
|
||||
</p>
|
||||
{gfkPrinciples.map((p, i) => (
|
||||
<div key={i} className="border-l-4 border-blue-500 pl-3">
|
||||
<h4 className="font-medium">{i + 1}. {p.principle}</h4>
|
||||
<p className="text-sm text-gray-600">{p.description}</p>
|
||||
<p className="text-xs text-gray-500 italic mt-1">
|
||||
Beispiel: {p.example}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user