refactor(admin): split training, control-provenance, iace/verification, training/learner, ControlDetail
All 5 files reduced below 500 LOC (hard cap) by extracting sub-components: - training/page.tsx: 780→278 LOC — imports existing _components/, adds BlocksSection - control-provenance/page.tsx: 739→145 LOC — extracts provenance-data.ts, ProvenanceHelpers, LicenseMatrix, SourceRegistry - iace/[projectId]/verification/page.tsx: 673→164 LOC — extracts VerificationForm, CompleteModal, SuggestEvidenceModal, VerificationTable - training/learner/page.tsx: 560→216 LOC — extracts AssignmentsList, ContentView, QuizView, CertificatesView - ControlDetail.tsx: 878→311 LOC — adds ControlSourceCitation, ControlTraceability, ControlRegulatorySection, ControlSimilarControls, ControlReviewActions siblings Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,12 +14,14 @@ interface ContentTabProps {
|
||||
bulkGenerating: boolean
|
||||
bulkResult: { generated: number; skipped: number; errors: string[] } | null
|
||||
moduleMedia: TrainingMedia[]
|
||||
interactiveGenerating?: boolean
|
||||
onGenerateContent: () => void
|
||||
onGenerateQuiz: () => void
|
||||
onBulkContent: () => void
|
||||
onBulkQuiz: () => void
|
||||
onPublishContent: (contentId: string) => void
|
||||
onReloadMedia: () => void
|
||||
onGenerateInteractiveVideo?: () => void
|
||||
}
|
||||
|
||||
export function ContentTab({
|
||||
@@ -31,12 +33,14 @@ export function ContentTab({
|
||||
bulkGenerating,
|
||||
bulkResult,
|
||||
moduleMedia,
|
||||
interactiveGenerating,
|
||||
onGenerateContent,
|
||||
onGenerateQuiz,
|
||||
onBulkContent,
|
||||
onBulkQuiz,
|
||||
onPublishContent,
|
||||
onReloadMedia,
|
||||
onGenerateInteractiveVideo,
|
||||
}: ContentTabProps) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
@@ -139,6 +143,35 @@ export function ContentTab({
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Interactive Video */}
|
||||
{selectedModuleId && generatedContent?.is_published && onGenerateInteractiveVideo && (
|
||||
<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">Interaktives Video</h3>
|
||||
<p className="text-xs text-gray-500">Video mit Narrator-Persona und Checkpoint-Quizzes</p>
|
||||
</div>
|
||||
{moduleMedia.some(m => m.media_type === 'interactive_video' && m.status === 'completed') ? (
|
||||
<span className="px-3 py-1.5 text-xs bg-purple-100 text-purple-700 rounded-full">Interaktiv erstellt</span>
|
||||
) : (
|
||||
<button
|
||||
onClick={onGenerateInteractiveVideo}
|
||||
disabled={interactiveGenerating}
|
||||
className="px-4 py-2 text-sm bg-purple-600 text-white rounded-lg hover:bg-purple-700 disabled:opacity-50"
|
||||
>
|
||||
{interactiveGenerating ? 'Generiere interaktives Video...' : 'Interaktives Video generieren'}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{moduleMedia.filter(m => m.media_type === 'interactive_video' && m.status === 'completed').map(m => (
|
||||
<div key={m.id} className="text-xs text-gray-500 space-y-1 bg-gray-50 rounded p-3">
|
||||
<p>Dauer: {Math.round(m.duration_seconds / 60)} Min | Groesse: {(m.file_size_bytes / 1024 / 1024).toFixed(1)} MB</p>
|
||||
<p>Generiert: {new Date(m.created_at).toLocaleString('de-DE')}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Script Preview */}
|
||||
{selectedModuleId && generatedContent?.is_published && (
|
||||
<ScriptPreview moduleId={selectedModuleId} />
|
||||
|
||||
Reference in New Issue
Block a user