feat(admin-v2): Major SDK/Compliance overhaul and new modules
SDK modules added/enhanced: - compliance-hub, compliance-scope, consent-management, notfallplan - audit-report, workflow, source-policy, dsms - advisory-board documentation section - TOM dashboard components, TOM generator SDM mapping - DSFA: mitigation library, risk catalog, threshold analysis, source attribution - VVT: baseline catalog, profiling engine, types - Loeschfristen: baseline catalog, compliance engine, export, profiling, types - Compliance scope: engine, profiling, golden tests, types Existing SDK pages updated: - dsfa/[id], tom, vvt, loeschfristen, advisory-board — expanded functionality - SDKSidebar, StepHeader — new navigation items and layout - SDK layout, context, types — expanded type system Other admin-v2 changes: - AI agents page, RAG pipeline DSFA integration - GridOverlay component updates - Companion feature (development + education) - Compliance advisor SOUL definition Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
209
admin-v2/components/companion/lesson-mode/LessonEndedView.tsx
Normal file
209
admin-v2/components/companion/lesson-mode/LessonEndedView.tsx
Normal file
@@ -0,0 +1,209 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { CheckCircle, Clock, BarChart3, Plus, RefreshCw } from 'lucide-react'
|
||||
import { LessonSession } from '@/lib/companion/types'
|
||||
import { HomeworkSection } from './HomeworkSection'
|
||||
import { ReflectionSection } from './ReflectionSection'
|
||||
import {
|
||||
PHASE_COLORS,
|
||||
PHASE_DISPLAY_NAMES,
|
||||
formatTime,
|
||||
formatMinutes,
|
||||
} from '@/lib/companion/constants'
|
||||
|
||||
interface LessonEndedViewProps {
|
||||
session: LessonSession
|
||||
onSaveReflection: (rating: number, notes: string, nextSteps: string) => void
|
||||
onAddHomework: (title: string, dueDate: string) => void
|
||||
onRemoveHomework: (id: string) => void
|
||||
onStartNew: () => void
|
||||
}
|
||||
|
||||
export function LessonEndedView({
|
||||
session,
|
||||
onSaveReflection,
|
||||
onAddHomework,
|
||||
onRemoveHomework,
|
||||
onStartNew,
|
||||
}: LessonEndedViewProps) {
|
||||
const [activeTab, setActiveTab] = useState<'summary' | 'homework' | 'reflection'>('summary')
|
||||
|
||||
// Calculate analytics
|
||||
const totalPlannedSeconds = session.totalPlannedDuration * 60
|
||||
const totalActualSeconds = session.elapsedTime
|
||||
const timeDiff = totalActualSeconds - totalPlannedSeconds
|
||||
const timeDiffMinutes = Math.round(timeDiff / 60)
|
||||
|
||||
const startTime = new Date(session.startTime)
|
||||
const endTime = session.endTime ? new Date(session.endTime) : new Date()
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Success Header */}
|
||||
<div className="bg-gradient-to-r from-green-500 to-green-600 rounded-xl p-6 text-white">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="p-3 bg-white/20 rounded-full">
|
||||
<CheckCircle className="w-8 h-8" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold">Stunde beendet!</h2>
|
||||
<p className="text-green-100">
|
||||
{session.className} - {session.subject}
|
||||
{session.topic && ` - ${session.topic}`}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tab Navigation */}
|
||||
<div className="bg-white border border-slate-200 rounded-xl p-1 flex">
|
||||
{[
|
||||
{ id: 'summary', label: 'Zusammenfassung', icon: BarChart3 },
|
||||
{ id: 'homework', label: 'Hausaufgaben', icon: Plus },
|
||||
{ id: 'reflection', label: 'Reflexion', icon: RefreshCw },
|
||||
].map((tab) => (
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => setActiveTab(tab.id as typeof activeTab)}
|
||||
className={`
|
||||
flex-1 flex items-center justify-center gap-2 py-3 px-4 rounded-lg
|
||||
font-medium transition-all duration-200
|
||||
${activeTab === tab.id
|
||||
? 'bg-slate-900 text-white'
|
||||
: 'text-slate-600 hover:bg-slate-100'
|
||||
}
|
||||
`}
|
||||
>
|
||||
<tab.icon className="w-4 h-4" />
|
||||
{tab.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Tab Content */}
|
||||
{activeTab === 'summary' && (
|
||||
<div className="space-y-6">
|
||||
{/* Time Overview */}
|
||||
<div className="bg-white border border-slate-200 rounded-xl p-6">
|
||||
<h3 className="font-semibold text-slate-900 mb-4 flex items-center gap-2">
|
||||
<Clock className="w-5 h-5 text-slate-400" />
|
||||
Zeitauswertung
|
||||
</h3>
|
||||
|
||||
<div className="grid grid-cols-3 gap-4 mb-6">
|
||||
<div className="text-center p-4 bg-slate-50 rounded-xl">
|
||||
<div className="text-2xl font-bold text-slate-900">
|
||||
{formatTime(totalActualSeconds)}
|
||||
</div>
|
||||
<div className="text-sm text-slate-500">Tatsaechlich</div>
|
||||
</div>
|
||||
<div className="text-center p-4 bg-slate-50 rounded-xl">
|
||||
<div className="text-2xl font-bold text-slate-900">
|
||||
{formatMinutes(session.totalPlannedDuration)}
|
||||
</div>
|
||||
<div className="text-sm text-slate-500">Geplant</div>
|
||||
</div>
|
||||
<div className={`text-center p-4 rounded-xl ${timeDiff > 0 ? 'bg-amber-50' : 'bg-green-50'}`}>
|
||||
<div className={`text-2xl font-bold ${timeDiff > 0 ? 'text-amber-600' : 'text-green-600'}`}>
|
||||
{timeDiffMinutes > 0 ? '+' : ''}{timeDiffMinutes} Min
|
||||
</div>
|
||||
<div className={`text-sm ${timeDiff > 0 ? 'text-amber-500' : 'text-green-500'}`}>
|
||||
Differenz
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Session Times */}
|
||||
<div className="flex items-center justify-between text-sm text-slate-500 border-t border-slate-100 pt-4">
|
||||
<span>Start: {startTime.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' })}</span>
|
||||
<span>Ende: {endTime.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' })}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Phase Breakdown */}
|
||||
<div className="bg-white border border-slate-200 rounded-xl p-6">
|
||||
<h3 className="font-semibold text-slate-900 mb-4 flex items-center gap-2">
|
||||
<BarChart3 className="w-5 h-5 text-slate-400" />
|
||||
Phasen-Analyse
|
||||
</h3>
|
||||
|
||||
<div className="space-y-4">
|
||||
{session.phases.map((phase) => {
|
||||
const plannedSeconds = phase.duration * 60
|
||||
const actualSeconds = phase.actualTime
|
||||
const diff = actualSeconds - plannedSeconds
|
||||
const diffMinutes = Math.round(diff / 60)
|
||||
const percentage = Math.min((actualSeconds / plannedSeconds) * 100, 150)
|
||||
|
||||
return (
|
||||
<div key={phase.phase} className="space-y-2">
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="w-3 h-3 rounded-full"
|
||||
style={{ backgroundColor: PHASE_COLORS[phase.phase].hex }}
|
||||
/>
|
||||
<span className="font-medium text-slate-700">
|
||||
{PHASE_DISPLAY_NAMES[phase.phase]}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 text-slate-500">
|
||||
<span>{Math.round(actualSeconds / 60)} / {phase.duration} Min</span>
|
||||
<span className={`
|
||||
px-2 py-0.5 rounded text-xs font-medium
|
||||
${diff > 60 ? 'bg-amber-100 text-amber-700' : diff < -60 ? 'bg-blue-100 text-blue-700' : 'bg-green-100 text-green-700'}
|
||||
`}>
|
||||
{diffMinutes > 0 ? '+' : ''}{diffMinutes} Min
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Progress Bar */}
|
||||
<div className="h-3 bg-slate-100 rounded-full overflow-hidden">
|
||||
<div
|
||||
className="h-full rounded-full transition-all duration-500"
|
||||
style={{
|
||||
width: `${Math.min(percentage, 100)}%`,
|
||||
backgroundColor: percentage > 100
|
||||
? '#f59e0b' // amber for overtime
|
||||
: PHASE_COLORS[phase.phase].hex,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === 'homework' && (
|
||||
<HomeworkSection
|
||||
homeworkList={session.homeworkList}
|
||||
onAdd={onAddHomework}
|
||||
onRemove={onRemoveHomework}
|
||||
/>
|
||||
)}
|
||||
|
||||
{activeTab === 'reflection' && (
|
||||
<ReflectionSection
|
||||
reflection={session.reflection}
|
||||
onSave={onSaveReflection}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Start New Lesson Button */}
|
||||
<div className="pt-4">
|
||||
<button
|
||||
onClick={onStartNew}
|
||||
className="w-full py-4 px-6 bg-slate-900 text-white rounded-xl font-semibold hover:bg-slate-800 transition-colors flex items-center justify-center gap-2"
|
||||
>
|
||||
<RefreshCw className="w-5 h-5" />
|
||||
Neue Stunde starten
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user