Agent-completed splits committed after agents hit rate limits before committing their work. All 4 pages now under 500 LOC: - consent-management: 1303 -> 193 LOC (+ 7 _components, _hooks, _data, _types) - control-library: 1210 -> 298 LOC (+ _components, _types) - incidents: 1150 -> 373 LOC (+ _components) - training: 1127 -> 366 LOC (+ _components) Verification: next build clean (142 pages generated). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
149 lines
6.0 KiB
TypeScript
149 lines
6.0 KiB
TypeScript
'use client'
|
|
|
|
import type { TrainingModule, ModuleContent, TrainingMedia } from '@/lib/sdk/training/types'
|
|
import AudioPlayer from '@/components/training/AudioPlayer'
|
|
import VideoPlayer from '@/components/training/VideoPlayer'
|
|
import ScriptPreview from '@/components/training/ScriptPreview'
|
|
|
|
interface ContentTabProps {
|
|
modules: TrainingModule[]
|
|
selectedModuleId: string
|
|
onSelectModule: (id: string) => void
|
|
generatedContent: ModuleContent | null
|
|
generating: boolean
|
|
bulkGenerating: boolean
|
|
bulkResult: { generated: number; skipped: number; errors: string[] } | null
|
|
moduleMedia: TrainingMedia[]
|
|
onGenerateContent: () => void
|
|
onGenerateQuiz: () => void
|
|
onBulkContent: () => void
|
|
onBulkQuiz: () => void
|
|
onPublishContent: (contentId: string) => void
|
|
onReloadMedia: () => void
|
|
}
|
|
|
|
export function ContentTab({
|
|
modules,
|
|
selectedModuleId,
|
|
onSelectModule,
|
|
generatedContent,
|
|
generating,
|
|
bulkGenerating,
|
|
bulkResult,
|
|
moduleMedia,
|
|
onGenerateContent,
|
|
onGenerateQuiz,
|
|
onBulkContent,
|
|
onBulkQuiz,
|
|
onPublishContent,
|
|
onReloadMedia,
|
|
}: ContentTabProps) {
|
|
return (
|
|
<div className="space-y-6">
|
|
{/* Bulk Generation */}
|
|
<div className="bg-white border rounded-lg p-4">
|
|
<h3 className="text-sm font-medium text-gray-700 mb-3">Bulk-Generierung</h3>
|
|
<p className="text-xs text-gray-500 mb-4">Generiere Inhalte und Quiz-Fragen fuer alle Module auf einmal</p>
|
|
<div className="flex gap-3">
|
|
<button
|
|
onClick={onBulkContent}
|
|
disabled={bulkGenerating}
|
|
className="px-4 py-2 text-sm bg-green-600 text-white rounded-lg hover:bg-green-700 disabled:opacity-50"
|
|
>
|
|
{bulkGenerating ? 'Generiere...' : 'Alle Inhalte generieren'}
|
|
</button>
|
|
<button
|
|
onClick={onBulkQuiz}
|
|
disabled={bulkGenerating}
|
|
className="px-4 py-2 text-sm bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 disabled:opacity-50"
|
|
>
|
|
{bulkGenerating ? 'Generiere...' : 'Alle Quizfragen generieren'}
|
|
</button>
|
|
</div>
|
|
{bulkResult && (
|
|
<div className="mt-4 p-3 bg-gray-50 rounded-lg text-sm">
|
|
<div className="flex gap-6">
|
|
<span className="text-green-700">Generiert: {bulkResult.generated}</span>
|
|
<span className="text-gray-500">Uebersprungen: {bulkResult.skipped}</span>
|
|
{bulkResult.errors?.length > 0 && (
|
|
<span className="text-red-600">Fehler: {bulkResult.errors.length}</span>
|
|
)}
|
|
</div>
|
|
{bulkResult.errors?.length > 0 && (
|
|
<div className="mt-2 text-xs text-red-600">
|
|
{bulkResult.errors.map((err, i) => <div key={i}>{err}</div>)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<div className="bg-white border rounded-lg p-4">
|
|
<h3 className="text-sm font-medium text-gray-700 mb-3">LLM-Content-Generator</h3>
|
|
<p className="text-xs text-gray-500 mb-4">Generiere Schulungsinhalte und Quiz-Fragen automatisch via KI</p>
|
|
<div className="flex gap-3 items-end">
|
|
<div className="flex-1">
|
|
<label className="text-xs text-gray-600 block mb-1">Modul auswaehlen</label>
|
|
<select
|
|
value={selectedModuleId}
|
|
onChange={e => onSelectModule(e.target.value)}
|
|
className="w-full px-3 py-2 text-sm border rounded-lg bg-white"
|
|
>
|
|
<option value="">Modul waehlen...</option>
|
|
{modules.map(m => <option key={m.id} value={m.id}>{m.module_code} - {m.title}</option>)}
|
|
</select>
|
|
</div>
|
|
<button onClick={onGenerateContent} disabled={!selectedModuleId || generating} className="px-4 py-2 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50">
|
|
{generating ? 'Generiere...' : 'Inhalt generieren'}
|
|
</button>
|
|
<button onClick={onGenerateQuiz} disabled={!selectedModuleId || generating} className="px-4 py-2 text-sm bg-purple-600 text-white rounded-lg hover:bg-purple-700 disabled:opacity-50">
|
|
{generating ? 'Generiere...' : 'Quiz generieren'}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{generatedContent && (
|
|
<div className="bg-white border rounded-lg p-4">
|
|
<div className="flex items-center justify-between mb-3">
|
|
<div>
|
|
<h3 className="text-sm font-medium text-gray-700">Generierter Inhalt (v{generatedContent.version})</h3>
|
|
<p className="text-xs text-gray-500">Generiert von: {generatedContent.generated_by} ({generatedContent.llm_model})</p>
|
|
</div>
|
|
{!generatedContent.is_published ? (
|
|
<button onClick={() => onPublishContent(generatedContent.id)} className="px-3 py-1.5 text-xs bg-green-600 text-white rounded hover:bg-green-700">Veroeffentlichen</button>
|
|
) : (
|
|
<span className="px-3 py-1.5 text-xs bg-green-100 text-green-700 rounded">Veroeffentlicht</span>
|
|
)}
|
|
</div>
|
|
<div className="prose prose-sm max-w-none border rounded p-4 bg-gray-50 max-h-96 overflow-y-auto">
|
|
<pre className="whitespace-pre-wrap text-sm text-gray-800">{generatedContent.content_body}</pre>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Audio Player */}
|
|
{selectedModuleId && generatedContent?.is_published && (
|
|
<AudioPlayer
|
|
moduleId={selectedModuleId}
|
|
audio={moduleMedia.find(m => m.media_type === 'audio') || null}
|
|
onMediaUpdate={onReloadMedia}
|
|
/>
|
|
)}
|
|
|
|
{/* Video Player */}
|
|
{selectedModuleId && generatedContent?.is_published && (
|
|
<VideoPlayer
|
|
moduleId={selectedModuleId}
|
|
video={moduleMedia.find(m => m.media_type === 'video') || null}
|
|
onMediaUpdate={onReloadMedia}
|
|
/>
|
|
)}
|
|
|
|
{/* Script Preview */}
|
|
{selectedModuleId && generatedContent?.is_published && (
|
|
<ScriptPreview moduleId={selectedModuleId} />
|
|
)}
|
|
</div>
|
|
)
|
|
}
|