A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.
This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).
Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
115 lines
3.3 KiB
TypeScript
115 lines
3.3 KiB
TypeScript
'use client'
|
|
|
|
import { Users, GraduationCap, BookOpen, FileCheck } from 'lucide-react'
|
|
import { CompanionStats } from '@/lib/companion/types'
|
|
|
|
interface StatsGridProps {
|
|
stats: CompanionStats
|
|
loading?: boolean
|
|
}
|
|
|
|
interface StatCardProps {
|
|
label: string
|
|
value: number
|
|
icon: React.ReactNode
|
|
color: string
|
|
loading?: boolean
|
|
}
|
|
|
|
function StatCard({ label, value, icon, color, loading }: StatCardProps) {
|
|
return (
|
|
<div className="bg-white border border-slate-200 rounded-xl p-4 hover:shadow-md transition-shadow">
|
|
<div className="flex items-start justify-between">
|
|
<div>
|
|
<p className="text-sm text-slate-500 mb-1">{label}</p>
|
|
{loading ? (
|
|
<div className="h-8 w-16 bg-slate-200 rounded animate-pulse" />
|
|
) : (
|
|
<p className="text-2xl font-bold text-slate-900">{value}</p>
|
|
)}
|
|
</div>
|
|
<div className={`p-2 rounded-lg ${color}`}>
|
|
{icon}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export function StatsGrid({ stats, loading }: StatsGridProps) {
|
|
const statCards = [
|
|
{
|
|
label: 'Klassen',
|
|
value: stats.classesCount,
|
|
icon: <Users className="w-5 h-5 text-blue-600" />,
|
|
color: 'bg-blue-100',
|
|
},
|
|
{
|
|
label: 'Schueler',
|
|
value: stats.studentsCount,
|
|
icon: <GraduationCap className="w-5 h-5 text-green-600" />,
|
|
color: 'bg-green-100',
|
|
},
|
|
{
|
|
label: 'Lerneinheiten',
|
|
value: stats.learningUnitsCreated,
|
|
icon: <BookOpen className="w-5 h-5 text-purple-600" />,
|
|
color: 'bg-purple-100',
|
|
},
|
|
{
|
|
label: 'Noten',
|
|
value: stats.gradesEntered,
|
|
icon: <FileCheck className="w-5 h-5 text-amber-600" />,
|
|
color: 'bg-amber-100',
|
|
},
|
|
]
|
|
|
|
return (
|
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
|
{statCards.map((card) => (
|
|
<StatCard
|
|
key={card.label}
|
|
label={card.label}
|
|
value={card.value}
|
|
icon={card.icon}
|
|
color={card.color}
|
|
loading={loading}
|
|
/>
|
|
))}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Compact version of StatsGrid for sidebar or smaller spaces
|
|
*/
|
|
export function StatsGridCompact({ stats, loading }: StatsGridProps) {
|
|
const items = [
|
|
{ label: 'Klassen', value: stats.classesCount, icon: <Users className="w-4 h-4" /> },
|
|
{ label: 'Schueler', value: stats.studentsCount, icon: <GraduationCap className="w-4 h-4" /> },
|
|
{ label: 'Einheiten', value: stats.learningUnitsCreated, icon: <BookOpen className="w-4 h-4" /> },
|
|
{ label: 'Noten', value: stats.gradesEntered, icon: <FileCheck className="w-4 h-4" /> },
|
|
]
|
|
|
|
return (
|
|
<div className="bg-white border border-slate-200 rounded-xl p-4">
|
|
<h3 className="text-sm font-medium text-slate-500 mb-3">Statistiken</h3>
|
|
<div className="space-y-3">
|
|
{items.map((item) => (
|
|
<div key={item.label} className="flex items-center justify-between">
|
|
<div className="flex items-center gap-2 text-slate-600">
|
|
{item.icon}
|
|
<span className="text-sm">{item.label}</span>
|
|
</div>
|
|
{loading ? (
|
|
<div className="h-5 w-8 bg-slate-200 rounded animate-pulse" />
|
|
) : (
|
|
<span className="font-semibold text-slate-900">{item.value}</span>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|